通过for循环更改foreach循环

时间:2017-07-07 07:36:16

标签: c# performance for-loop foreach performance-testing

我正在创建一个指纹验证系统,我必须使用数据库中的记录来匹配指纹。我已经使用了foreach循环来实现这一目标,但只需要40秒即可完成350条记录。我想加快速度。我希望我的foreach循环转换为for循环但我在初始化for循环时遇到一些困难。这是我的代码。

foreach (KeyValuePair<decimal, MemoryStream> tt in profileDic)
{
    this.Invoke(new Function(delegate()
    {
        textBox1.Text += "\n" + tt.Key.ToString();
    }));
    temp = new DPFP.Template(tt.Value);

    Verificator = new DPFP.Verification.Verification();
    featuresForVerification = ExtractFeatures(Sample, DPFP.Processing.DataPurpose.Verification);
    if (featuresForVerification != null)
    {
        DPFP.Verification.Verification.Result result = new DPFP.Verification.Verification.Result();
        Verificator.Verify(featuresForVerification, temp, ref result);

        #region UI Envoke
        this.Invoke(new Function(delegate()
        {
            if (result.Verified)
            {
                MessageBox.Show("FAR " + result.FARAchieved + "\n" + tt.Key.ToString());
            }
            else
            {
                //MessageBox.Show("No Match");
            }
        }));
        #endregion
    }
    this.Invoke(new Function(delegate()
    {
        progressBar1.Value += 1;
    }));
    Thread.CurrentThread.Priority = ThreadPriority.Highest;
}

我对第一行foreach (KeyValuePair<decimal, MemoryStream> tt in profileDic)感到困惑。有人可以告诉我如何使用for循环迭代Dictionary对象profileDic中的每个项目。我不确定在使用KeyValuePair<decimal, MemoryStream> tt in profileDic循环时如何获得for

4 个答案:

答案 0 :(得分:2)

  

我必须匹配[列表中的条目]。我用foreach循环来做   所以但是只有350秒的记录需要大约40秒。我想加快速度   它了。我希望我的foreach循环转换为for循环[...]

在这一点上,最好退一步思考一下我们在这里做的事情。性能优化通常分为两个层次:算法和工作流程。

基本上,这里要解决的问题是在一组可能很大的记录中找到一个条目。可能有两个原因导致这种情况变慢:

  • 列表非常大,迭代需要很长时间
  • 列表可能不是那么大,但是例程经常被调用

列表非常大

如果我们记住我们对所谓的Big-O notation的了解并考虑一下,我们可能会很快发现数组搜索需要O(n)而哈希集搜索只需要{{1}在正常情况下,只有在最坏的情况下,我们才会再次降至O(1)。还不错!

通过一些幸运的巧合(以及上面链接的备忘单的帮助),我们发现O(n)或者数据库索引或多或少是我们想要的:字典基本上是“拉皮条”哈希集,数据库索引通常是B-Tree,它与Dictionary<K,V>一起执行。我们应该使用字典或数据库表的最终决定主要取决于我们所讨论的数据量。

作为一个实际的例子,我最近在我的桌子上有一段代码以相同的线性方式迭代列表。代码在另外两个嵌套循环中完成了这个。该算法需要4个小时才能完成。在战略性地点引入两个字典之后,我把它降到了不到一分钟。我把它留给有趣的读者来计算百分比。

因此,要问的真正问题不是“Θ(log(n))for更快吗?” (否)但我们应该问:“如何重新组织我的数据结构并优化所涉及的算法以使其发挥作用?

代码经常被调用

这是另一个但相关的问题。在某些情况下,数据结构无法真正优化,或者会导致代码中的太多更改。但是当我们仔细查看剖析器告诉我们的内容时,我们可能会发现昂贵的rountine在15秒内被称为800.000次,并且这些调用仅占总时间的相当大的数量。

如果我们看得更近,我们可能会发现我们使用非常有限的输入数据集来调用例程,因此通过缓存昂贵操作的结果可以省略大部分调用。上周我刚才有这样一个案例,我可以将数据库调用量减少到原始数量的5%。可以想象这对整体表现有什么影响。

在第二种情况下,我们应该问自己一个稍微不同的问题:“为什么我们这样做?我们如何改变逻辑工作流程以避免大多数这些调用?是否可能采用完全不同的方式实现相同的结果?“。

摘要(TL; DR)

每种性能优化都有两种基本方法:

  • 算法或“低级别”:Quicksort或Bubblesort?树,列表或哈希集?
  • 工作流程和逻辑:为什么我们必须将这个特别昂贵的例行程序称为500万次?

答案 1 :(得分:0)

您可能实际上想要添加类似Exit For或Break;而不是将其从For Loop更改为Foreach循环;在c#中,一旦你找到了结果。

答案 2 :(得分:0)

您使用什么生物识别系统?这种工作应该在生物识别设备中完成。但是如果你真的需要直接在数据库中找到一个人,你不应该使用C#集合,而应该使用数据库本身。

每个指纹都有它的细节。指纹有独特的功能。有一些算法可以将其转换为可存储的数据,例如在数据库中。这可能看起来像md5哈希。

接下来,当您使用minutiaes在数据库中有记录时,您可以向数据库询问此值。

它应该是这样的:你得到minutias(或者可以直接存储在数据库中的完整数据),然后向数据库询问这个值,如:

SELECT *
FROM users
WHERE fingerprint = :your_data

数据库操作比以任何方式迭代任何集合要快得多。

答案 3 :(得分:0)

回答你提出的问题:用for循环替换foreach循环,替换:

foreach (KeyValuePair<decimal, MemoryStream> tt in profileDic) 
{
    //...
}

for (var i=0; i < profileDic.Count, i++) 
{
    KeyValuePair<decimal, MemoryStream> tt = profileDic.ElementAt(i);
    //...
}

要使用此功能,您还需要在代码中包含using System.Linq;语句。 也就是说,这假设字典中元素的顺序不会改变;这不保证(除非您使用SortedDictionaryOrderedDictionary)。 因此,更好的方法是:

[decimal[]]keys = profileDic.Keys;
for (var i=0; i < keys.Count, i++) 
{
    KeyValuePair<decimal, MemoryStream> tt = profileDic[keys[i]];
    //...
}

但这会增加更多开销/可能会将for循环的时间推到foreach循环的时间/是微优化,无法解决您的实际性能问题。

根据评论,循环可能不是你的问题,但是在那个循环中发生了什么(如果在代码的这一部分中)。

我们对您的代码的评论不够充分,因此您最好调查自己;例如使用此处描述的性能分析技术:https://msdn.microsoft.com/en-us/library/ms182372.aspx

我重构了你的代码以使其更具可读性(即通过将UI更新拉入他们自己的方法,因此它们不会使主线程混乱)。

我还移动了那些在循环之外每次迭代都不需要更新的操作......但是在不知道你的代码的情况下这是纯粹的猜测/所以没有保证。

最后,我删除了您的代码,以便在每次迭代结束时更改当前线程的优先级。使用线程优先级不是修复慢速代码的好方法;只有某些情况适合,并且在这里看到它的背景我超过99%确定这不是其中一种情况。

//...
featuresForVerification = ExtractFeatures(Sample, DPFP.Processing.DataPurpose.Verification); //since this appears unaffected by our profileDic values, let's initialise once
if (featuresForVerification != null)
{
    DPFP.Verification.Verification verificator = new DPFP.Verification.Verification();
    foreach (KeyValuePair<decimal, MemoryStream> tt in profileDic)
    {
        string key = tt.Key.ToString(); //we use this a lot, so let's only convert it to string once, then reuse that
        UIReportCurrentKey(key);
        temp = new DPFP.Template(tt.Value); 
        DPFP.Verification.Verification.Result result = new DPFP.Verification.Verification.Result();
        verificator.Verify(featuresForVerification, temp, ref result);
        UIReportMatch(result, key);
        //if a match were found, would we want to keep comparing, or exit on first match?  If the latter, add code to record which record matched, then use break to exit the loop
        UIIncremementProgressBar();
    }
} else {
    throw new NoFeaturesToVerifyException("The verfication tool was not given any features to verify");
    //alternatively set progress bar to complete / whatever other UI actions you have /
    //whatever you want to happen if no features were selected for comparison
}


//...

#region UI Updaters
/*
    I don't know much about winforms updates; have a feeling these can be made more efficient too, 
    but for the moment just shoving the code into its own functions to make the main method less
    cluttered with UI logic.
*/

// Adds the key of the item currently being processed to the UI textbox
private void UIReportCurrentKey(string key) 
{
    this.Invoke(new Function(delegate()
    {
        textBox1.Text += "\n" + key;
    }));
}
private void UIReportMatch(DPFP.Verification.Verification.Result result, string key) 
{
    if (result.Verified) 
    {
        this.Invoke(new Function(delegate()
        {
                MessageBox.Show("FAR " + result.FARAchieved + "\n" + key);
        }));
    } 
    /* 
    else 
    {
        this.Invoke(new Function(delegate()
        {
                MessageBox.Show("No Match");
        }));        
    } 
    */
}
private void UIIncremementProgressBar() 
{
    this.Invoke(new Function(delegate()
    {
        progressBar1.Value++;
    }));
}

#endregion UI Updaters

#region Custom Exceptions
public class NoFeaturesToVerifyException: ApplicationException 
{ //Choose base class appropriately
    public NoFeaturesToVerifyException(): base() {}
    public NoFeaturesToVerifyException(string message): base(message) {}
    //...
}
#endregion Custom Exceptions
相关问题