如上所述,不要每次进入该函数时都启动一个线程,并且“作业”粒度大于内部函数的一次操作,以便很好地摊销创建作业的开销。将您的原始例程描述为:
void OuterFunction( Thingy inputData[N] )
{
for ( int i = 0 ; i < N ; ++i )
InnerFunction( inputData[i] );
}
我们将通过(假设存在作业队列系统)解决您的问题:
void JobFunc( Thingy inputData[], int start, int stop )
{
for ( int i = start ; i < stop ; ++i )
InnerFunction( inputData[i] );
}
void OuterFunction( Thingy inputData[N], int numCores )
{
int perCore = N / numCores; // assuming N%numCores=0
// (omitting edge case for clarity)
for ( int c = 0 ; c < numCores ; ++c )
QueueJob( JobFunc, inputData, c * perCore, (c + 1) * perCore );
}
只要您的输入数据是完全独立的,正如您在原始问题中所说,您无需锁定它;仅当线程之间存在依赖关系时才需要同步,而这里没有。
此外,在这个性能级别上,微优化开始变得相关:最重要的是缓存局部性。预取可以让你走得更远。
然后考虑 SIMD 的可能性,您可以将其矢量化以同时通过单个寄存器运行四个输入点。使用四个内核和 4 宽 SIMD,理论上您可以获得 16 倍的加速,但这假设 InnerFunction 所做的工作主要是一个固定的数学函数,因为分支往往会抹杀 SSE/VMX 的性能提升。