为了使性能保持“高”,在使用我们的应用程序时,我们将所有列表视图都使用(预加载)图像列表。
biggeset列表视图可以包含大约9000张图像的图像列表。
因此,为了生成图像列表,我们使用了addRange()
,它可以使该过程很快完成。
但是,在启动过程中,将图像从文件系统“加载”到image[]
阵列中仍然需要4-5秒。 (〜80 MB,在SSD上)
我想知道是否有更好的方法从(本地)文件系统填充“大”图像列表?
当前我们正在使用:
System.IO.DirectoryInfo di2 = new System.IO.DirectoryInfo(Config.ProductImageLocalPath);
System.IO.FileInfo[] files2 = di.EnumerateFiles("*.jpg", SearchOption.AllDirectories).ToArray();
int idx = 1; //0 is reservevd for default image, which is already added.
foreach (FileInfo fi in files2)
{
Resources.ImageIndex.Add(long.Parse(Path.GetFileNameWithoutExtension(fi.Name)), idx++);
}
Image[] images = Array.ConvertAll(files2, file => Image.FromFile(file.FullName));
Resources.ProductImageList_256.Images.AddRange(images);
ImageIndex
只是一个dictionary<Long, int>
,它允许将实际的id
(从文件名派生)映射到相应图像映射中的基于0的索引,并且不需要任何时间都可以生成。
很明显,Image[] images = Array.ConvertAll(files2, file => Image.FromFile(file.FullName));
是瓶颈,因为这等于对Image.fromFile
的9000个调用(是否还有一些bulk-Option?)。
这里有“更好”的想法吗?
答案 0 :(得分:0)
好的,所以我编写了一个函数,该函数可以完成分配给多个线程的工作-然后在我的代码中发现一个“错误”。 (但是,使用多线程不仅有很多帮助,所以image.fromFile
显然是CPU的瓶颈,而不是(本地)SSD的瓶颈)。
1。)网络加载
在上述代码中:System.IO.FileInfo[] files2 = di.EnumerateFiles("*.jpg", SearchOption.AllDirectories).ToArray();
-我使用di
进行枚举,而不是di2
(di
代表网络共享)
(因此Array.ConvertAll
正在访问远程文件)
“此”方法的运行时间: 6秒414毫秒
2。)本地加载
修复此问题后,运行时间将为 1 886毫秒
3。)本地线程加载
但是,我的ThreadedLoading
功能已经完成,所以我也尝试了:
0s 543ms -因此,在具有4个物理内核的8内核上,速度仍快将近4倍。
这是代码。可能还不是防弹的,但是会产生可靠和最快的(最佳)结果。
用法:
DateTime t1 = DateTime.Now;
//Image[] images = Array.ConvertAll(files2, file => Image.FromFile(file.FullName));
Image[] images = loadImagesThreaded(files2);
DateTime t2 = DateTime.Now;
TimeSpan ts = t2.Subtract(t1);
MessageBox.Show("Loading took: " + ts.Seconds + "s " + ts.Milliseconds + "ms"); //6s 414ms
Resources.ProductImageList_256.Images.AddRange(images);
功能:
private Image[] loadImagesThreaded(FileInfo[] files)
{
int threadCount = 8;
int chunkSize = files.Length / threadCount; //Round about - doesn't need to be precise.
Thread[] threads = new Thread[threadCount];
Image[][] result = new Image[threadCount][];
for (int i = 0; i < threadCount; i++)
{
FileInfo[] chunk;
int lowerBound = i * chunkSize;
if (i < threadCount - 1)
{
chunk = files.Skip(lowerBound).Take(chunkSize).ToArray();
}
else
{
//take the rest
chunk = files.Skip(lowerBound).ToArray();
}
int j = i;
threads[i] = new Thread(() =>
{
result[j] = Array.ConvertAll(chunk, file => Image.FromFile(file.FullName));
});
threads[i].Start();
}
//wait for threads to finish.
Boolean oneAlive = true;
while (oneAlive)
{
oneAlive = false;
foreach (Thread t in threads)
{
if (t.IsAlive)
{
oneAlive = true;
break;
}
}
}
//all done.
Image[] finalResult = new Image[files.Count()];
for (int i = 0; i < threadCount; i++)
{
int lowerBound = i * chunkSize;
result[i].CopyTo(finalResult, lowerBound);
}
return finalResult;
}
在预加载图像时这不是一个很好的峰值吗? :-)
4。)Parallel.ForEach
按照Felipe Ramos的建议,我尝试了Parallel.ForEach
。它比我的“线程化”解决方案快大约40-50毫秒(通常为480-500毫秒)-但也许更重要的事实:
这很容易,因此更可靠。
Image[] images = new Image[files2.Count()];
Parallel.ForEach(files2, (fileInfo, state, index) =>
{
images[index] = Image.FromFile(fileInfo.FullName);
});
DateTime t2 = DateTime.Now;
TimeSpan ts = t2.Subtract(t1);
MessageBox.Show("Loading took: " + ts.Seconds + "s " + ts.Milliseconds + "ms");