本篇基于 C++性能优化系列——3D高斯核卷积计算(八)3D高斯卷积 中的代码实现,测试超线程对多线程并行性的影响。
代码实现
测试平台支持8核16线,具体信息参考 C++性能优化系列——百倍加速比的矩阵转置性能调优。为了避免重复调用函数带来的多线程测试结果的干扰,这里只对函数调用一次。
并行区开辟8线程
void GaussSmoothCPU3DBase_OneParallelRegion(float* pSrc, int iDim[3], float* pKernel, int kernelSize[3], float* pDst, float* pBuffer)
{
int iSliceSize = iDim[1] * iDim[0];
int nCenter = kernelSize[0] / 2;
#pragma omp parallel num_threads(8)
{
#pragma omp for schedule(dynamic)
for (int z = 0; z < (iDim[2]); z++)
{
{
float* pSrcSlice = pSrc + z * iSliceSize;
float* pBuffSlice = pBuffer + z * iSliceSize;
memset(pBuffSlice, 0, iSliceSize * sizeof(float));
for (int y = 0; y < iDim[1]; y++)
{
float* pSrcLine = pSrcSlice + y * iDim[0];
float* pDstLine = pBuffSlice + y * iDim[0];
Conv1D_Opt_Cmb(pSrcLine, iDim[0], pKernel, kernelSize[0], pDstLine);
}
for (int y = 0; y < (iDim[1] - kernelSize[0] + 1); y++)
{
float* pDstLine = pSrcSlice + (y + nCenter) * iDim[0];
memset(pDstLine, 0, iDim[0] * sizeof(float));
for (int kx = 0; kx < kernelSize[0]; kx++)
{
float* pSrcLine = pBuffSlice + (y + kx) * iDim[0];
#pragma omp simd aligned(pSrcLine, pDstLine)
for (int i = 0; i < iDim[0]; i++)
{
pDstLine[i] += pSrcLine[i] * pKernel[kx];
}
}
}
}
}
#pragma omp for schedule(dynamic)
for (int z = 0; z < (iDim[2] - kernelSize[0] + 1); z++)
{
{
float* pDstSlice = pDst + (z + nCenter) * iSliceSize;
memset(pDstSlice, 0, iSliceSize * sizeof(float));
for (int kx = 0; kx < kernelSize[0]; kx++)
{
float* pSrcSlice = pSrc + (z + kx) * iSliceSize;
#pragma omp simd
for (int i = 0; i < iSliceSize; ++i)
{
pDstSlice[i] += pKernel[kx] * pSrcSlice[i];
}
}
}
}
}
}
执行时间
GaussSmoothCPU3DBase_OneParallelRegion cost Time(ms) 246
并行区开辟16线程
代码实现
void GaussSmoothCPU3DBase_OneParallelRegion(float* pSrc, int iDim[3], float* pKernel, int kernelSize[3], float* pDst, float* pBuffer)
{
int iSliceSize = iDim[1] * iDim[0];
int nCenter = kernelSize[0] / 2;
#pragma omp parallel num_threads(16)
{
#pragma omp for schedule(dynamic)
for (int z = 0; z < (iDim[2]); z++)
{
{
float* pSrcSlice = pSrc + z * iSliceSize;
float* pBuffSlice = pBuffer + z * iSliceSize;
memset(pBuffSlice, 0, iSliceSize * sizeof(float));
for (int y = 0; y < iDim[1]; y++)
{
float* pSrcLine = pSrcSlice + y * iDim[0];
float* pDstLine = pBuffSlice + y * iDim[0];
Conv1D_Opt_Cmb(pSrcLine, iDim[0], pKernel, kernelSize[0], pDstLine);
}
for (int y = 0; y < (iDim[1] - kernelSize[0] + 1); y++)
{
float* pDstLine = pSrcSlice + (y + nCenter) * iDim[0];
memset(pDstLine, 0, iDim[0] * sizeof(float));
for (int kx = 0; kx < kernelSize[0]; kx++)
{
float* pSrcLine = pBuffSlice + (y + kx) * iDim[0];
#pragma omp simd aligned(pSrcLine, pDstLine)
for (int i = 0; i < iDim[0]; i++)
{
pDstLine[i] += pSrcLine[i] * pKernel[kx];
}
}
}
}
}
#pragma omp for schedule(dynamic)
for (int z = 0; z < (iDim[2] - kernelSize[0] + 1); z++)
{
{
float* pDstSlice = pDst + (z + nCenter) * iSliceSize;
memset(pDstSlice, 0, iSliceSize * sizeof(float));
for (int kx = 0; kx < kernelSize[0]; kx++)
{
float* pSrcSlice = pSrc + (z + kx) * iSliceSize;
#pragma omp simd
for (int i = 0; i < iSliceSize; ++i)
{
pDstSlice[i] += pKernel[kx] * pSrcSlice[i];
}
}
}
}
}
}
执行时间
GaussSmoothCPU3DBase_OneParallelRegion cost Time(ms) 384
VTune分析线程性能
8线程
热点语句
16线程
热点语句
从函数执行结果和VTune性能分析可以看到:
1)并行区开辟8个线程总的执行速度快,线程执行的效率更高,线程创建后一直在进行有效的计算
2)并行区开辟16个线程总的执行速度慢,线程执行效率低,线程在有效计算的中间夹杂着大量的等待时间
3)对于两个版本的热点语句,由于开辟16线程的并行区同一时刻总的访问内存数量加倍,因此造成了内存性能瓶颈更为突出。
因为超线程技术本身是复用相同的物理核心,当前场景每个线程执行的功能类似,因此使用超线程时,两个线程使用同一个物理核心上的同一个执行部件,同时内存压力加倍,造成线程等待。
结论
对于类似本篇中的代码实现,线程功能类似,负载较小,同时已经有明确的内存问题时,通过使用超线程技术,并不一定得到更好的加速比,因此在选择超线程时,需要根据具体的代码实现随机应变。