gpt4 book ai didi

multithreading - 为什么多线程处理此代码会导致时序不一致?

转载 作者:行者123 更新时间:2023-12-03 12:52:56 25 4
gpt4 key购买 nike

我有处理大图像的功能。根据规范,该图像最大可以为55mb。该处理需要将图像分为几个不同的波段,然后通过将这些波段重新添加到输出图像中来重构图像。由于图像非常大,因此我无法在32位系统上同时将所有四个图像以及输入和输出图像保留在内存中。结果,我将每个镜像放在磁盘上,然后分批读取。

在多线程之前,伪代码如下所示:

for (y is 0 to ysize)
unsigned short* ptr1 = ReadLineFromDisk(image1, y)
unsigned short* ptr2 = ReadLineFromDisk(image2, y)
unsigned short* ptr3 = ReadLineFromDisk(image3, y)
unsigned short* ptr4 = ReadLineFromDisk(image4, y)
unsigned short* outPtr = &(outImage[y*inXSize])
for (x is 0 to xsize, ++x, ++ptr1, ++ptr2, ++ptr3, ++ptr4, ++outPtr){
outPtr = combination of ptr1, ptr2, ptr3, ptr4;
}
}

此代码在具有高性能计数器的标准500 gb硬盘驱动器的双核计算机上运行3秒钟。

如果将从磁盘读取的行数增加到大约100,然后逐步执行,代码如下所示:
chunksize = 100;
for (y is 0 to ysize by chunksize)
unsigned short* ptr1 = ReadChunkFromDisk(image1, y)
unsigned short* ptr2 = ReadChunkFromDisk(image2, y)
unsigned short* ptr3 = ReadChunkFromDisk(image3, y)
unsigned short* ptr4 = ReadChunkFromDisk(image4, y)
unsigned short* outPtr = &(outImage[y*inXSize])
for (x is 0 to xsize*chunk, ++x, ++ptr1, ++ptr2, ++ptr3, ++ptr4, ++outPtr){
outPtr = combination of ptr1, ptr2, ptr3, ptr4;
}
}

此代码比以前的代码快,最多1.5秒。

问题1:为什么代码速度更快?

我假设它的速度更快,因为以我的经验,对于相同数量的数据,大而连续的读取要快于较小的连续读取。也就是说,如果我一次读取全部100行数据,则至少比常规(非SSD)硬盘驱动器要快100次单独读取。我的假设接近正确吗?

即使这样,这里也不会大量使用处理器。实际上,增加缓存大小是令人望而却步的,因为1.5是我可以获得的最佳值,然后该值似乎下降了一点(不知道为什么会这样,除了可能有一些磁盘缓存在起作用)。那导致我

问题2:为什么块大小会有一个最佳点?

如果我了解这里的内容(但实际上我不认为我知道),那么如果所有内容都可以保存在内存中,那将是非常快的,因为不会有磁盘命中。如果阅读更多也使事情变得更快,难道一次读不到四分之一的图像只会对速度造成轻微的影响吗?

因此,然后我切换到将外部循环放在lambda表达式中,并使用 Intel's TBB来执行代码,例如:
chunksize = 100;
parallel_for (y is 0 to ysize by chunksize in a lambda expression)
unsigned short* ptr1 = ReadChunkFromDisk(image1, y)
unsigned short* ptr2 = ReadChunkFromDisk(image2, y)
unsigned short* ptr3 = ReadChunkFromDisk(image3, y)
unsigned short* ptr4 = ReadChunkFromDisk(image4, y)
unsigned short* outPtr = &(outImage[y*inXSize])
for (x is 0 to xsize*chunk, ++x, ++ptr1, ++ptr2, ++ptr3, ++ptr4, ++outPtr){
outPtr = combination of ptr1, ptr2, ptr3, ptr4;
}
}

该代码的速度范围从0.4秒到1.6秒。

这使我想到:

问题3:速度增加最多不应该是2倍,而不是4倍吗?

这是我运行这些基准测试的双核计算机,因此在理想情况下,一个线程从磁盘读取数据,而另一个线程从磁盘读取数据。即使以4倍的速度运行时,它也仅使用80%的处理器,而不是100%,因此仍然存在磁盘瓶颈。但是增长4倍意味着其他事情也在发生。

我还假设速度差异很大,这是因为线程在读取时无法完美同步,如果这就是速度提高的方式。真正的最后一个问题是:

问题4:如何使速度提高4倍?

最佳答案

答案1:是的,您受磁盘限制,因此不会过多固定CPU,是的,读取较大的块会更有效(只要这些块与磁盘缓存对齐)。

答案2:具有8 MB高速缓存且以10k RPM旋转的磁盘可能会获得60至80 MB/sec的吞吐量,因此“最佳点”将是读取与高速缓存大小对齐的块。您可以增加缓冲区,但要使其与缓存大小保持一致:即8MB,16MB,32MB等。

答案3:理想情况下,您希望将一个线程专用于从磁盘读取,而另一个专用于处理数据(您可能希望使用多个线程进行处理)。读取磁盘的多线程可能会稍微提高性能,但是通常不是这样。我不知道为什么当您增加4倍时,您会认为“其他”正在发生。

答案3更新:
坦白地说,我也不完全知道为什么会发生这种情况,但是我也已经在.NET应用程序中的多线程磁盘I/O上看到了这一点。事实上,我什至have a C# test example which demonstrates the same kind of performance increase注意到了。请注意,在我的测试中,我正在加载HTML页面,这些页面大致与您在“野生”状态下看到的页面相同(每个页面大约80至160 KB),因此我没有将读取的内容与磁盘缓存对齐。实际上,一次读取多个线程可能更有效率,因为尽管您进行了多次读取,但您仍在利用磁盘缓存。当然,这只是一个临时的假设,即我尚无证据可支持,因此请带一点盐!我认为,如果文件足够大,并且磁盘读取线程实际上具有与磁盘缓存对齐的缓冲区,那么添加更多线程根本不会提高速度。 如果您仍然看到速度有所提高,请告诉我们!

答案4:
请尝试以下方法:

  • 将缓冲区与磁盘的缓存大小对齐。
  • 减少同时尝试访问磁盘的应用程序的数量。
  • 尽可能在内存中加载尽可能多的图像,并运行足够的线程以充分利用CPU(您将即兴使用线程数,四处逛逛,看看“甜蜜点”在哪里)。
  • 仅使用一个磁盘读取线程,并确保其不断读取!!!

  • 再说一次,您受磁盘限制,因此您可能永远无法真正获得100%的CPU使用率。

    答案4更新:
    我不认为英特尔的TBB确实是导致您(和我)看到的性能提高的原因……正如我所说,我最好的 猜测是,如果它们提供多个线程,实际上可能会更高效更好地利用磁盘缓存。我什至不确定这是否是正确的假设,所以不要在没有测试的情况下引用我!

    阅读:
    我发现了一篇非常详细的论文,名为 Asynchronous/Multi Threaded I/O on Commodity Systems with Multiple Disks – a performance study,它对多线程I/O优于单线程I/O的情况进行了一些惊人的分析和测试。看看第86页。

    Dr. Dobbs also has an article on the subject,尽管我没有机会阅读全部内容,但我只是略读了一下。

    关于multithreading - 为什么多线程处理此代码会导致时序不一致?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6339216/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com