Мой первый пост здесь. Отличный сайт и ресурс.
Я немного искал и смотрел на вопросы с похожими заголовками, но не мог найти что-то конкретное об этом.
Я пытаюсь удалить избыточность и раздувание из библиотеки астрономических вычислений C, которые использует моя программа на С++. Я запустил простой профайлер (VerySleepy).
Вот код, который профайлер показал как использующий наибольшее время (кроме функций библиотеки C 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
Эта конкретная функция глубоко встроена в другие, а основная функция "kick-off", которую вызывает моя программа, называется тысячами раз.
Вы можете видеть выдающийся оператор с 3.49s намного выше, чем все другие времена инструкции. Я знаю, что есть способы ускорить арифметику С с использованием умножения на деление, когда это возможно. Но я не знаю гораздо больше.
Как
-
Было бы лучше разделить это утверждение на более мелкие куски?:
br = x2 * brpp;
br -= brp2;
br += coef[j];
Любые другие идеи или критические замечания. Я не писал этот код, хотя я добавил константу в параметры функции, поскольку мне нравится const-correctness.
Я никогда не пробовал использовать регистры или другие причудливые трюки, чтобы ускорить работу раньше. Кто-нибудь думает, что что-то подобное может работать здесь?
Я знаю, что люди скажут: "Попробуй!" Поэтому я буду и обновлю то, что получаю, если это поможет любому, у кого есть подобные арифметические вопросы.
РЕДАКТИРОВАТЬ: результаты публикации, которые я тестировал из предложений
В порядке от самого быстрого до самого медленного, вот что я нашел до сих пор. Профилирование - VerySleepy. Компилятор - это Visual Studio 2008 Pro Ed. Параметры компиляции для библиотеки и моего приложения:
Debug, C7 format, /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
}
Я также попробовал сочетание как разворачивания цикла Andrew, так и использования указателя Drew, но это заняло 2,39 секунды, то же самое, что и неизменный код.
Основываясь на результатах, цикл-разворачивание - это путь до сих пор для моего использования.