Мне записали этот цикл в C++, это скомпилировало с MSVC2010, занимает много времени для выполнения. (300 мс)
for (int i=0; i<h; i++) {
for (int j=0; j<w; j++) {
if (buf[i*w+j] > 0) {
const int sy = max(0, i - hr);
const int ey = min(h, i + hr + 1);
const int sx = max(0, j - hr);
const int ex = min(w, j + hr + 1);
float val = 0;
for (int k=sy; k < ey; k++) {
for (int m=sx; m < ex; m++) {
val += original[k*w + m] * ds[k - i + hr][m - j + hr];
}
}
heat_map[i*w + j] = val;
}
}
}
Это казалось немного странным для меня, таким образом, я сделал некоторые тесты, затем изменился на несколько битов на встроенный ассемблерный код: (а именно, код, который суммирует "val"),
for (int i=0; i<h; i++) {
for (int j=0; j<w; j++) {
if (buf[i*w+j] > 0) {
const int sy = max(0, i - hr);
const int ey = min(h, i + hr + 1);
const int sx = max(0, j - hr);
const int ex = min(w, j + hr + 1);
__asm {
fldz
}
for (int k=sy; k < ey; k++) {
for (int m=sx; m < ex; m++) {
float val = original[k*w + m] * ds[k - i + hr][m - j + hr];
__asm {
fld val
fadd
}
}
}
float val1;
__asm {
fstp val1
}
heat_map[i*w + j] = val1;
}
}
}
Теперь это работает в половину времени, 150 мс. Это делает точно то же самое, но почему это вдвое более быстро? В обоих случаях это было выполнено в режиме Release с оптимизацией на. Я делаю что-то не так в своем исходном коде C++?
Я предлагаю вам попробовать различные модели вычислений с плавающей запятой, поддерживаемые компилятором - точный
, строгий
или быстрый
(см. / fp
option) - с вашим исходным кодом, прежде чем делать какие-либо выводы.Я подозреваю, что ваш исходный код был скомпилирован с использованием какой-то чрезмерно ограничительной модели с плавающей запятой (за которой не следовала ваша сборка во второй версии кода), поэтому оригинал намного медленнее.
Другими словами, если исходная модель действительно была слишком строгой, то вы просто сравнивали яблоки с апельсинами. Две версии на самом деле не делали одно и то же, хотя на первый взгляд так могло показаться.
Обратите внимание, например, что в первой версии кода промежуточная сумма накапливается в значении с плавающей запятой
. Если бы он был скомпилирован с помощью точной
модели, промежуточные результаты пришлось бы округлить до точности типа float
, даже если переменная val
была оптимизирована и Вместо этого использовался внутренний регистр FPU. В вашем ассемблерном коде вы не пытаетесь округлить накопленный результат, что могло бы способствовать его лучшей производительности.
Я бы посоветовал вам скомпилировать обе версии кода в режиме / fp: fast
и посмотреть, как их производительность сравнится в этом случае.
Несколько вещей, которые нужно проверить:
Вам нужно проверить, что это на самом деле тот же самый код. То есть, точно ли ваши строчные ассемблерные утверждения совпадают с теми, которые генерирует компилятор? Я вижу три потенциальных различия (потенциальных, потому что они могут быть оптимизированы). Первое - это начальная установка val
в ноль, второе - дополнительная переменная val1
(маловероятно, поскольку она, скорее всего, просто изменит константу вычитания указателя стека), третье - ваша версия встроенного ассемблера может не поместить промежуточные результаты обратно в val
.
Вам нужно убедиться, что пространство выборки велико. Вы не упомянули, сделали ли вы только один прогон каждой версии или сотню, но чем больше прогонов, тем лучше, чтобы устранить эффект "шума" в статистике.
Еще лучшим измерением было бы время процессора, а не прошедшее время. Прошедшее время подвержено изменениям окружающей среды (например, ваша программа проверки на вирусы или одна из ваших служб решает что-то сделать в то время, когда вы тестируете). Большое пространство выборки облегчит, но не обязательно решит эту проблему.