Мой первый пост здесь. Отличный сайт и ресурс.
Я немного поискал и просмотрел вопросы с похожими названиями, но не смог найти что-то конкретно об этом.
Я пытаюсь удалить избыточность и раздутость из библиотеки астрономических расчетов на C, которую использует моя программа на C++. Я запустил простой профайлер (VerySleepy).
Вот код, который профилировщик показал как использующий больше всего времени (не считая функций библиотеки Си sprintf и т.д.):
double swi_echeb(const double x, const double* const coef, const int ncf)
{
int j = ncf - 1;
double x2, br, brp2, brpp;
x2 = x * 2.;
br = 0.;
brp2 = 0.; /* dummy assign to silence gcc warning */
brpp = 0.;
for (; j >= 0; --j) { // <-- 0.39s
brp2 = brpp; // <-- 0.01s
brpp = br; // <-- 0.32s
br = x2 * brpp - brp2 + coef[j]; // <-- 3.49s ***
} // <-- 0.14s
return (br - brp2) * .5; // <-- 0.06s
} // <-- 0.05s
Эта конкретная функция глубоко встроена в другие, и основная "стартовая" функция, которую вызывает моя программа, вызывается тысячи раз.
Вы можете видеть, что выделяющееся утверждение с временем 3,49 с намного больше, чем время всех остальных утверждений. Я знаю, что есть способы ускорить арифметику C, используя умножение вместо деления, когда это возможно. Но я не знаю намного больше, чем это.
Например:
Не лучше ли разбить это утверждение на более мелкие части:
br = x2 * brpp;
br -= brp2;
br += coef[j];
Любые другие идеи или критика. Я не писал этот код, хотя я добавил const к параметрам функции, так как я люблю const-корректность.
Я никогда раньше не пробовал использовать регистры или другие причудливые трюки для ускорения работы. Кто-нибудь думает, что что-то подобное может сработать здесь?
Я знаю, что люди скажут: "Попробуй!". Так что я попробую, и буду обновлять результаты, если это поможет кому-то с похожими арифметическими вопросами.
EDIT: Выкладываю результаты, которые я проверил из предложенных
В порядке от самого быстрого к самому медленному, вот что я нашел на данный момент. Профилировщик - VerySleepy. Компилятор - Visual Studio 2008 Pro Ed. Параметры компиляции для библиотеки и моего приложения:
Debug, формат C7, /O2 /Ob2 /Oi /Ot /Oy /GT /GL /GF /FD /MTd /GS- /Gy /fp:fast /FAs
Ниже приводится предложение Эндрю о выполнении "4 итераций на цикл". Это было самым быстрым на данный момент.
ОБЩЕЕ ВРЕМЯ, проведенное в функции (время других утверждений в функции здесь не показано) = 2,08 секунды
for (; index >= 3; index -= 4) { // 0.02s
brp2 = brpp;
brpp = br; // 0.02s
br = x2 * brpp - brp2 + coef[index]; // 0.25s
brp2 = brpp;
brpp = br; // 0.13s
br = x2 * brpp - brp2 + coef[index - 1]; // 0.33s
brp2 = brpp;
brpp = br; // 0.13s
br = x2 * brpp - brp2 + coef[index - 2]; // 0.34s
brp2 = brpp;
brpp = br; // 0.14s
br = x2 * brpp - brp2 + coef[index - 3]; // 0.42s
}
for (; index >= 0; --index) { // 0.03s
brp2 = brpp; // 0.03s
brpp = br;
br = x2 * brpp - brp2 + coef[index]; // 0.11s
}
Следующим по скорости был оригинальный неизмененный код, с общим временем 2,39 секунды внутри функции, опять же включая утверждения вне цикла. Обратите внимание, что это меньше, чем в моем первоначальном сообщении. Мой первоначальный пост был неоптимизированным кодом, но поскольку все предложили это, все мои тесты были впоследствии оптимизированы настолько, насколько я мог получить в VS08:
for (j = ncf - 1; j >= 0; j--) { // 0.02s
brp2 = brpp; // 0.03s
brpp = br; // 0.07s
br = x2 * brpp - brp2 + coef[j]; // 2.14s
}
После этого первоначального кода, следующим самым быстрым была идея Дрю установить указатель заранее и использовать это. Общее время, проведенное внутри функции, составило 2.49 секунды, включая время от операторов вне цикла:
for (; index >= coef; --index) { // 0.01s
brp2 = brpp;
brpp = br; // 0.06s
br = x2 * brpp - brp2 + *index; // 2.24s
}
Я также попробовал сочетание разворачивания цикла Эндрю и использования указателя Дрю, но это заняло 2.39 секунды, столько же, сколько и неизмененный код.
Судя по результатам, разворачивание цикла является оптимальным для моего использования.