У меня есть следующий код, в котором вычисляется сумма, основанная на очень большой серии.
Серия char *a
представляет собой массив char, который содержит только цифры (0..9).
Я хотел спросить, есть ли возможность сделать код быстрее. В настоящее время это горло бутылки в распределенном вычислительном приложении.
Небольшой код воспроизведения. Не настоящий код и более упрощенный.
int top = 999999999;
char *a;
a = (char*) calloc(top+1, sizeof(char));
// ... fill a with initial values ...
for (int i=0; i<10; ++i) {
unsigned long long int sum = 0;
for (m = 1, k = top; m < k; ++m, --k) {
// Here is the bottle neck!!
sum += a[m]*a[k];
}
printf("%d\n", sum);
// ... Add something at the end of a, and increase top ...
}
Я уже пробовал следующее:
-
Оптимизация кода с помощью
-O3
(gcc-компилятор). Теперь строка компилятора:gcc -c -Wall -fopenmp -Wno-unused-function -O3 -std=c99 -g0 -march=native -pipe -D_FILE_OFFSET_BITS=64 -m64 -fwhole-program -fprefetch-loop-arrays -funsafe-loop-optimizations -Wunsafe-loop-optimizations -fselective-scheduling -fselective-scheduling2 -fsel-sched-pipelining -fsel-sched-pipelining-outer-loops -fgcse-sm -fgcse-lm -fgcse-las -fmodulo-sched -fgcse-after-reload -fsee -DLIBDIVIDE_USE_SSE2 -DLIBDIVIDE_USE_SSE4_1 xxx.c -o xxx.o
-
Использование GNU openMP для разделения for-loop на несколько ядер
unsigned long long int halfway = (top>>1) + 1; // = top/2 + 1 // digits is defined as top+1 #pragma omp parallel // firstprivate/*shared*/(a, digits, halfway) for (unsigned long long int m = 1; m < halfway; ++m) { sum += a[m] * a[digits-m]; }
Результат: намного, намного быстрее, но требуется больше ядер, и я все равно хочу сделать это быстрее.
-
Отбрасывание
a[m]
доunsigned long long int
перед умножениемsum += (unsigned long long int)a[m] * a[k];
Результат: небольшое повышение производительности.
-
Использование таблицы поиска умножения, поскольку поиск массива выполняется быстрее, чем фактическое умножение.
sum += multiply_lookup[a[m]][a[k]]; // a[m]*a[k];
Результат: небольшое повышение производительности.
-
Я попытался найти математическое решение для сокращения операций, но кажется, что ничто не может быть оптимизировано математически.
У меня есть следующая идея для оптимизации:
I прочитали, что умножение поплавков (asm fmul
) намного быстрее, чем умножение целых чисел (asm mul
). Просто изменить int
на float
не поможет - но я думаю, что код может стать намного более эффективным, если работа выполняется с использованием наборов команд MMX или SSE или если работа выполняется FPU. Хотя у меня есть некоторые знания ассемблера, я не знаю об этих темах.
Однако, если у вас есть дополнительные идеи, как их оптимизировать, я рад их услышать.
Обновить. Дополнительная информация:
- После каждого цикла серия увеличивается на 1 элемент.
- Пока серия растет, увеличивается
top
. - Когда
top
достигает предела массива,a
будет увеличиваться на 100000 байт, используяrealloc()
. - Платформа: Debian Linux Jessie x64, на процессоре Intel (R) Xeon (R) X3440 @2.53 ГГц
Дополнительный вопрос вне темы: Знаете ли вы математическое имя этой суммы, где пары элементов серии умножаются извне внутрь?