总结:在代码中如何指定OpenMP仅使用真实核心的线程,即不包括超线程?
详细分析:多年来,我在业余时间编写了一个纯软件、开源的渲染器(光栅化/光线追踪器) 。该GPL代码和Windows二进制文件可以从以下链接获取:https://www.thanassis.space/renderer.html。它可以在Windows、Linux、OS/X和BSD下编译和运行。
最近一个月,我引入了一种光线追踪模式,生成图片的质量突飞猛进。不幸的是,射线跟踪比光栅化慢几个数量级。为了提高速度,就像对光栅化程序所做的那样,我将OpenMP(和TBB)支持添加到射线追踪器中,以便于利用额外的CPU核心。无论光栅化还是光线追踪都很容易实现线程化(三角形工作 - 每个像素的工作)。
在家里,我的Core2Duo,第二个核心帮助所有模式——光栅化和光线追踪模式都得到了1.85倍到1.9倍的加速。
问题:当然,我很想看到最高的CPU性能(我还“玩”GPU,CUDA端口),所以我想要一个可靠的基础进行比较。我把代码交给了一个好朋友,他有一个拥有16个核心、1500美元的英特尔超级处理器的“怪物”机器。
他在“最重”的模式下运行它,即射线追踪模式......
......但他得到的速度只有我的Core2Duo的五分之一(!)
哇——太可怕了。发生了什么?
我们开始尝试不同的修改、补丁,... 最终我们找到了解决方法。
通过使用OMP_NUM_THREADS环境变量,可以控制生成多少个OpenMP线程。
随着线程数量从1增加到8,速度也在增加(接近线性增长)。但是一旦超过了8,速度开始下降,当使用所有16个核心时,速度甚至只有我的Core2Duo的五分之一!为什么是8呢?因为8是实际的核心数,另外8个是超线程核心!这就是理论:我对此感到很意外——我曾经看到超线程在其他算法中有很大帮助(高达25%),所以这个结果出乎意料。显然,尽管每个超线程核心都配备了自己的寄存器(和SSE单元?),光线追踪器无法利用额外的处理能力。这让我想到...可能不是处理能力被限制了,而是内存带宽被限制了。光线追踪器使用边界体分层数据结构来加速射线-三角形相交。如果使用超线程核心,则每个成对的“逻辑核心”都试图从该数据结构的不同位置(即内存中)读取 - 并且CPU缓存(每对本地)完全被压垮了。至少,这是我的理论-欢迎任何建议。那么问题来了:OpenMP检测“核心”的数量并生成相应的线程-也就是说,它将超线程“核心”包含在计算中。在我的情况下,这显然导致了灾难性的结果,速度方面。有人知道如何使用OpenMP API(如果可能的话)仅为实际核心而不是超线程核心生成线程吗?P.S.代码是开放的(GPL),并可在上面的链接处获得,在自己的机器上自由复制——我猜这会在所有超线程CPU上发生。P.P.S.请原谅我发布的长度,我认为这是一次教育经历,并想分享。