С++ Оптимизация векторизации вложенных циклов

У меня есть программа, работающая с несколькими вложенными циклами, работающими в 3D-домене:

unsigned int sX(m_sizeZ*m_sizeY);
unsigned int b(sX+m_sizeZ);
for(unsigned int i(1);i
<p>Где мои массивы в два раза больше размера m_sizeX * m_sizeY * m_sizeZ.</p> <p>Я повторяю этот путь, потому что не хочу касаться границ домена.</p> <p>При компиляции с (g++) -msse2 -ftree-vectorizer-verbose = 2, я, конечно, получаю многократные вложенные записи циклов.</p> <p>Можно ли каким-либо образом использовать один цикл без (более или менее) сложных проверочных операций?</p> <p>Спасибо!</p>
4 ответа

Если ваша цель - хорошая векторизация, вероятно, лучше всего применить один и тот же расчет к своим крайним точкам по отношению к внутренним точкам, только к reset им после того, как вы закончите вычисление всех точек. Что-то вроде этого:

unsigned int sX(m_sizeZ*m_sizeY);
unsigned int start = (1*m_sizeY + 1)*m_sizeZ + 1;
unsigned int end = ((m_sizeX - 1)*m_sizeY - 1)*m_sizeZ - 1;
//Do calculation for everything, including the edges.
for(unsigned int i = start; i < end; i++) {
 m_r[i]=m_func[i]-m_cX*(m_data[i-sX]+m_data[i+sX]-2.0*m_data[i])
 -m_cY*(m_data[i-m_sizeZ]+m_data[i+m_sizeZ]-2.0*m_data[i])
 -m_cZ*(m_data[i-1]+m_data[i+1]-2.0*m_data[i]);
}
//Reset the edges.
for(unsigned x = 0; x < m_sizeX; x++) {
 for(unsigned y = 0; y < m_sizeY; y++) {
 m_r[x*sX + y*m_sizeZ] = m_data[x*sX + y*m_sizeZ];
 m_r[x*sX + y*m_sizeZ + m_sizeZ-1] = m_data[x*sX + y*m_sizeZ + m_sizeZ-1];
 }
}
for(unsigned x = 0; x < m_sizeX; x++) {
 for(unsigned z = 0; z < m_sizeZ; z++) {
 m_r[x*sX + z] = m_data[x*sX + z];
 m_r[x*sX + (m_sizeY-1)*m_sizeZ + z] = m_data[x*sX + (m_sizeY-1)*m_sizeZ + z];
 }
}

Это дополнительные расчеты, которые будут выполнены, но он имеет два положительных эффекта:

  • Теперь вашему компилятору очень легко оцифровать первый цикл (который занимает большую часть времени).

  • Этот подход практически исключает краевую проблему, которая возникает из-за фиксированного размера вектора: поскольку ваш векторный блок обрабатывает несколько итераций цикла с замкнутым циклом (!) в одном, каждое ребро в ваших вычислениях приводит к двум специальным итерациям, которые должны быть сделанный. Один в начале прогона, чтобы выравнивать векторный цикл, а один в конце обрабатывать остатки векторного цикла.


Вы можете выполнять итерацию в одном цикле от 1 до m_sizeX*m_sizeY*m_sizeZ (с помощью счетчика C) и вычислять i, j, k как:

i = C / (m_sizeY*m_sizeZ)
j = (C % (m_sizeY*m_sizeZ)) / m_sizeZ
k = (C % (m_sizeY*m_sizeZ)) % m_sizeZ

Этот метод имеет ограничение, что вы должны заботиться о m_sizeX*m_sizeY*m_sizeZ в пределах диапазона C без переполнения.

ИЗМЕНИТЬ

Чтобы управлять своими границами без использования предложений if-else, вы можете создать функцию

size_t nextToCalculate(size_t previous)
{
 return previous+1+!condition;
}

И используйте его в своем цикле:

for(int C = 0; C < m_sizeX*m_sizeY*m_sizeZ; C = nextToCalculate(C))
{
 int z = (C % (m_sizeY*m_sizeZ)) % m_sizeZ;
 int y = (C % (m_sizeY*m_sizeZ)) / m_sizeZ;
 int x = C / (m_sizeY*m_sizeZ);
 ...
 ...
 ...
}

Или даже включить его реализацию в строку:

for(int C = 0; C < m_sizeX*m_sizeY*m_sizeZ; C = C+1+!CONDITION(C+1))
{
 int z = (C % (m_sizeY*m_sizeZ)) % m_sizeZ;
 int y = (C % (m_sizeY*m_sizeZ)) / m_sizeZ;
 int x = C / (m_sizeY*m_sizeZ);
 ...
 ...
 ...
}


Поместите код как целую функцию, которая может быть скомпилирована. Сначала посмотрите:

  • используйте const int m_sizeX_1 = m_sizeX-1 и используйте его в цикле
  • использовать временную переменную для b + k
  • даже использовать временную переменную для m_data + b + k
  • общий совет для векторизации - упростить код с помощью дополнительных переменных, и тогда вам будет проще, что делать дальше


вы можете попробовать это (один цикл, как вы просили):

unsigned int sX(m_sizeZ*m_sizeY);
unsigned int b(sX+m_sizeZ);
unsigned int i, j, k;
for (i = 1, j = 1, k = 1;
 i < m_sizeX-1 && j < m_sizeY - 1 && k < m_sizeZ - 1;
 k++) {
 m_r[b+k]=m_func[b+k]-m_cX*(m_data[b+k-sX]+m_data[b+k+sX]-2.0*m_data[b+k])
 -m_cY*(m_data[b+k-m_sizeZ]+m_data[b+k+m_sizeZ]-2.0*m_data[b+k])
 -m_cZ*(m_data[b+k-1]+m_data[b+k+1]-2.0*m_data[b+k]);
 if (k == (m_sizeZ - 2)) {
 if (j == (m_sizeY - 2)) {
 b+=2*m_sizeZ;
 j = 0;
 i++;
 }
 k = 0;
 b+=m_sizeZ;
 j++;
 }
}

licensed under cc by-sa 3.0 with attribution.