我对一个有大约35万件物品的词典进行了三次测试。我需要看看我可以在30秒的时间内迭代多少项目。
我的初始测试只是在字典中循环,没有检查,也没有UI /控制台更新。它在开始测试后仅仅一秒就完成了。
第二项测试是将计数写入控制台。已达到约23,600件物品。
foreach (KeyValuePair<UInt64, MftFile> entry in mftFiles)
{
fileCount++;
Console.WriteLine(fileCount.ToString());
}
然后我测试了用计数更新表单时运行的速度。它只达到了16000多项。
foreach (KeyValuePair<UInt64, MftFile> entry in mftFiles)
{
fileCount++;
MessageReceived(this, GetMessageReceivedEventArgs("Proactive Checks - RIFT",
string.Format("Analyzing Drive {0} - MFT Record {1} of {2}", drive.Name, fileCount.ToString(), mftFiles.Count.ToString()), string.Empty));
}
在循环中执行任何类型的条件逻辑会使循环减慢很多。我最初在循环外创建了一个秒表,并在循环内部检查它以查看它何时到达某个时间。一分钟过去了,它只迭代了大约5000件物品。
有一个C ++应用程序与我正在做的事情做类似的事情。它需要不到60秒的时间来遍历所有300,000个项目,更新UI,查找所有重复项,并为每个副本创建哈希值。
有没有更好的方法来遍历字典?
答案 0 :(得分:0)
您已经在使用更快的方式来遍历字典。没有其他更快捷的方法来遍历字典。见What is the best way to iterate over a Dictionary in C#?
如果你想在达到一定时间后停止,那么这可能会起作用
var t = new Thread(() => {
foreach (KeyValuePair<UInt64, MftFile> entry in mftFiles)
{
fileCount++;
Console.WriteLine(fileCount.ToString());
}
});
t.Start();
// Sleep the current thread as long as you want to run the task
Thread.Sleep(/* Specify time in milliseconds */);
// After that abort the Thread to exit the job
t.Abort();
进一步使用StringBuilder而不是String.Format,因为它必须解析需要时间的字符串。
修改强> 快速更新UI在其他线程中运行MFT扫描并在第二个线程中监视扫描。而不是更新每个文件上的UI,而是在特定时间后更新。通过使用下面的代码,我在更新UI方面取得了很好的表现。
public MyForm() {
InitializeComponent();
scanProgress = new Action(() => {
MessageReceived(this, GetMessageReceivedEventArgs("Proactive Checks - RIFT", string.Format("Analyzing Drive {0} - MFT Record {1} of {2}", drive.Name, cMft, count, string.Empty));
});
}
Action<> scanProgress;
int cMft = 0;
int count;
void ScanMft() {
count = mftFiles.Count;
foreach (KeyValuePair<UInt64, MftFile> entry in mftFiles) {
cMft++;
/* Scan MFT */
}
}
void WatchScan() {
while (cMft < count) {
Thread.Sleep(200);
this.BeginInvoke(scanProgress);
}
}
void RunScan() {
new Thread(ScanMft).Start();
new Thread(WatchScan).Start();
}
答案 1 :(得分:0)
那里有两个问题,如何更快地完成它(你没有提供足够的代码,你的瓶颈肯定不应该自己迭代一个集合,而你得到的那些数字是非常缓慢的,是你在调试中测试了吗?从来没有在调试中进行过性能测试!)
发布更多代码并在发布时对其进行测试,不要发布示例测试,而是发布您的实际代码,我们可以为您提供帮助。
问题的第二部分是,如何避免在处理时阻止UI,这很简单,不要在主线程上处理,将整个函数移动到另一个线程并定期(每100个ish?insert) )使用Control.BeginInvoke http://msdn.microsoft.com/en-us/library/system.windows.forms.control.begininvoke(v=vs.110).aspx
更新控件这意味着用户界面在处理过程中处于完全反应状态,只有在实际更新时才会对微小时间片进行频繁处理,以便始终保持响应。
如果所有这些都不够,那么另一个优化是,不做你不需要做的工作!坚持我所说的(在另一个线程上处理)但不更新UI除了第一个200或300元素,然后实现一个虚拟化控件,只显示当前应该看到的内容(在WPF中做的很简单,但我是假设您必须在winform中滚动自己或购买组件)。请注意,如果您在项目的早期阶段,我强烈建议您切换到WPF,因为您的应用程序听起来像是可以使用它(不是带有几个按钮/标签的简单商业应用程序)
编辑:为了清楚起见,枚举字典可能不是你的瓶颈,我只是在我的中档机器上进行了测试,并在字典中列举了一百万个键值对。 。 12毫秒!
编辑2:但是Console.WriteLine肯定是你的瓶颈,这就是为什么你不应该在测试时引入不相关的API,迭代到下一个项目=没有工作,构建字符串=一点工作,实际传递它到控制台进行打印=大量工作
答案 2 :(得分:-1)
您已经回答了自己的问题。你已经证明你可以在一秒钟内迭代字典,所以听起来你不需要提高速度 - 慢速与字典无关。这是MessageReceived函数调用。我不知道那个功能是做什么的,所以我无法解决为什么它很慢。
我也会尝试删除它:
mftFiles.Count.ToString()
你可以在循环外执行一次并存储值,这可能会有所帮助。
答案 3 :(得分:-1)
您可以创建一个工作线程并将foreach循环放入其中。通过这样做,UI和foreach循环将在不同的线程上运行,因此您不需要更新UI。
另一种替代方式(我不建议,因为它有其自身的后果)是放
Application.DoEvent();
在foreach循环中。这样做,您的UI将在每次迭代时得到更新。