Я написал две версии функции сглаживания, которая применяет ядро к выводу 9 координат.
float PerlinHeightMapGenerator::defaultSmooth(int x, int z, const float kernel[9])
{
const float top = defaultNoise(x - 1, z + 1) * kernel[0] + defaultNoise(x, z + 1) * kernel[1] + defaultNoise(x + 1, z + 1) * kernel[2];
const float middle = defaultNoise(x - 1, z) * kernel[3] + defaultNoise(x, z) * kernel[4] + defaultNoise(x + 1, z) * kernel[5];
const float bottom = defaultNoise(x - 1, z - 1) * kernel[6] + defaultNoise(x, z - 1) * kernel[7] + defaultNoise(x + 1, z - 1) * kernel[8];
const float total = (top + middle + bottom);
return total;
}
float PerlinHeightMapGenerator::defaultSmooth(int x, int z, const XMVECTOR kernel[3])
{
const XMVECTOR top = XMVector3Dot(XMVectorSet(defaultNoise(x - 1, z + 1), defaultNoise(x, z + 1), defaultNoise(x + 1, z+ 1), 0), kernel[0]);
const XMVECTOR middle = XMVector3Dot(XMVectorSet(defaultNoise(x - 1, z), defaultNoise(x, z), defaultNoise(x + 1, z), 0), kernel[1]);
const XMVECTOR bottom = XMVector3Dot(XMVectorSet(defaultNoise(x - 1, z - 1), defaultNoise(x, z - 1), defaultNoise(x + 1, z - 1), 0), kernel[2]);
XMFLOAT4 answer;
XMStoreFloat4(&answer, XMVectorAdd(top, XMVectorAdd(middle, bottom)));
return answer.x;
}
Вторая версия использует векторные инструкции, но для ввода, который я использую, значительно медленнее (добавляет почти 200 миллисекунд к общему времени выполнения всей программы). Разве векторизованная версия не должна быть быстрее, поскольку она может вызывать «defaultNoise» по три одновременно, а также может выполнять умножения одновременно?
Если это уместно, «defaultNoise (x, z)» делает это:
float PerlinHeightMapGenerator::defaultNoise(int x, int z)
{
int n = x + z * 57;
n = (n<<13) ^ n;
return static_cast<float>( 1.0f - ( (n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0f);
};
РЕДАКТИРОВАТЬ: Я попробовал еще несколько тестов с результатами, которые я не понимаю.
Сначала я попытался изменить его, чтобы векторизованная функция взяла массив из 9 чисел с плавающей запятой, а затем скопировала их в векторы в функции; ничего не делает.
Затем я попытался изменить код, чтобы увидеть, что произойдет, если ни один из векторов ядра фактически не используется. Сначала заменим все экземпляры ядра на XMVectorSet (1, 2, 3, 4), чтобы код был:
const XMVECTOR top = XMVector3Dot(XMVectorSet(defaultNoise(x - 1, z + 1), defaultNoise(x, z + 1), defaultNoise(x + 1, z+ 1), 0), XMVectorSet(1, 2, 3, 4));
const XMVECTOR middle = XMVector3Dot(XMVectorSet(defaultNoise(x - 1, z), defaultNoise(x, z), defaultNoise(x + 1, z), 0), XMVectorSet(1, 2, 3, 4));
const XMVECTOR bottom = XMVector3Dot(XMVectorSet(defaultNoise(x - 1, z - 1), defaultNoise(x, z - 1), defaultNoise(x + 1, z - 1), 0), XMVectorSet(1, 2, 3, 4));
Это сократило время выполнения программы с 4 секунд до менее 2 секунд (сокращение на 50%).
Затем я попытался заменить первый XMVectorSet (1, 2, 3, 4) обратно ядром [0], и это увеличило время выполнения примерно до 2100 миллисекунд. Замена второго XMVectorSet (1, 2, 3, 4) на ядро [1] увеличила время до 2700 миллисекунд; заменив окончательный, третий вектор вернул время до 4 секунд.
Я попытался также скопировать числа с плавающей точкой в векторы внутри функции вместо того, чтобы вызывать ее, и результат тот же.
РЕДАКТИРОВАТЬ 2: Похоже, XMVectorSet (1, 2, 3, 4) является особым случаем. Может быть, это быстрее, потому что это 4 дюйма; замена его на XMVectorSet (.0357143f, 0,0714286f, 0,0357143f, 0) замедляет его до первоначальной скорости.
Я до сих пор не понимаю, почему векторизованная версия медленнее.
Задача ещё не решена.
Других решений пока нет …