Я работал с фрагментом кода, который интенсивно связан с памятью. Я пытаюсь оптимизировать его в одном ядре, вручную применяя блокировку кэша, sw preetetching, разворачивание циклов и т. Д. Несмотря на то, что блокирование кеша дает значительное улучшение производительности. Однако, когда я запускаю разворот цикла, я получаю огромную деградацию производительности.Влияние развертки Loop на данные с привязкой к памяти
Я компилирую с Intel icc с флагами компилятора -O2 и -ipo во всех моих тестовых случаях.
Мой код похож на этот (3D 25-балльной трафарета):
void stencil_baseline (double *V, double *U, int dx, int dy, int dz, double c0, double c1, double c2, double c3, double c4)
{
int i, j, k;
for (k = 4; k < dz-4; k++)
{
for (j = 4; j < dy-4; j++)
{
//x-direction
for (i = 4; i < dx-4; i++)
{
U[k*dy*dx+j*dx+i] = (c0 * (V[k*dy*dx+j*dx+i]) //center
+ c1 * (V[k*dy*dx+j*dx+(i-1)] + V[k*dy*dx+j*dx+(i+1)])
+ c2 * (V[k*dy*dx+j*dx+(i-2)] + V[k*dy*dx+j*dx+(i+2)])
+ c3 * (V[k*dy*dx+j*dx+(i-3)] + V[k*dy*dx+j*dx+(i+3)])
+ c4 * (V[k*dy*dx+j*dx+(i-4)] + V[k*dy*dx+j*dx+(i+4)]));
}
//y-direction
for (i = 4; i < dx-4; i++)
{
U[k*dy*dx+j*dx+i] += (c1 * (V[k*dy*dx+(j-1)*dx+i] + V[k*dy*dx+(j+1)*dx+i])
+ c2 * (V[k*dy*dx+(j-2)*dx+i] + V[k*dy*dx+(j+2)*dx+i])
+ c3 * (V[k*dy*dx+(j-3)*dx+i] + V[k*dy*dx+(j+3)*dx+i])
+ c4 * (V[k*dy*dx+(j-4)*dx+i] + V[k*dy*dx+(j+4)*dx+i]));
}
//z-direction
for (i = 4; i < dx-4; i++)
{
U[k*dy*dx+j*dx+i] += (c1 * (V[(k-1)*dy*dx+j*dx+i] + V[(k+1)*dy*dx+j*dx+i])
+ c2 * (V[(k-2)*dy*dx+j*dx+i] + V[(k+2)*dy*dx+j*dx+i])
+ c3 * (V[(k-3)*dy*dx+j*dx+i] + V[(k+3)*dy*dx+j*dx+i])
+ c4 * (V[(k-4)*dy*dx+j*dx+i] + V[(k+4)*dy*dx+j*dx+i]));
}
}
}
}
Когда я петля разворачивая на внутреннем цикле (размер я) и раскатать в направлениях х, у, г отдельно от Раскатайте коэффициент 2,4,8 соответственно, я получаю ухудшение производительности во всех 9 случаях, т. е. разворачивается на 2 по направлению x, разворачивается на 2 по направлению y, разворачивается на 2 в направлении z, разворачивается на 4 в направлении x ... и т.д. Но когда я выполняю цикл разворачивания по самому внешнему циклу (размерность k) в 8 раз (2), я получаю хорошее улучшение производительности, которое даже лучше блокировки кеша.
Я даже пробовал профилировать свой код с помощью Intel Vtune. Это было похоже на узкие места, в основном из-за 1.LLC Miss и 2. Недостатки загрузки LLC, обслуживаемые Remote DRAM.
Я не могу понять, почему разворачивается самый быстрый самый быстрый цикл при снижении производительности, тогда как разворачивание самого дальнего и самого медленного измерения приводит к улучшению производительности. Однако это улучшение в последнем случае - когда я использую -O2 и -ipo при компиляции с icc.
Я не уверен, как интерпретировать эти статистические данные. Может кто-то помочь пролить свет на это.
как в сторону: предположительно компилятор оптимизирует k * dy * dx во временную точку? –
@Mitch Wheat: Я использую общее исключение подвыражения, принимая во внимание k * dx * dy и j * dx в моем исходном коде. Это скорее пример кода – Anusuya
Можете ли вы использовать инструкции SSE для вычисления некоторых из этих данных? С быстрым взглядом это выглядит подходящим ... – sarnold