Я пытаюсь ускорить работу одной программы с помощью предварительной выборки. Моя программа предназначена только для тестирования. Вот что он делает:
- Он использует два буфера int одинакового размера
- Он считывает одно за другим все значения первого буфера.
- Считывает значение по индексу во втором буфере
- Суммирует все значения, взятые из второго буфера
- Он выполняет все предыдущие шаги для большего и большего
- В конце печатаю количество произвольных и непроизвольных ЦП.
В самый первый раз значения в первых буферах содержат значения своего индекса (см. Функцию createIndexBuffer
в коде чуть ниже).
В коде моей программы будет более понятно:
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <sys/time.h>
#define BUFFER_SIZE ((unsigned long) 4096 * 100000)
unsigned int randomUint()
{
int value = rand() % UINT_MAX;
return value;
}
unsigned int * createValueBuffer()
{
unsigned int * valueBuffer = (unsigned int *) malloc(BUFFER_SIZE * sizeof(unsigned int));
for (unsigned long i = 0 ; i < BUFFER_SIZE ; i++)
{
valueBuffer[i] = randomUint();
}
return (valueBuffer);
}
unsigned int * createIndexBuffer()
{
unsigned int * indexBuffer = (unsigned int *) malloc(BUFFER_SIZE * sizeof(unsigned int));
for (unsigned long i = 0 ; i < BUFFER_SIZE ; i++)
{
indexBuffer[i] = i;
}
return (indexBuffer);
}
unsigned long long computeSum(unsigned int * indexBuffer, unsigned int * valueBuffer)
{
unsigned long long sum = 0;
for (unsigned int i = 0 ; i < BUFFER_SIZE ; i++)
{
unsigned int index = indexBuffer[i];
sum += valueBuffer[index];
}
return (sum);
}
unsigned int computeTimeInMicroSeconds()
{
unsigned int * valueBuffer = createValueBuffer();
unsigned int * indexBuffer = createIndexBuffer();
struct timeval startTime, endTime;
gettimeofday(&startTime, NULL);
unsigned long long sum = computeSum(indexBuffer, valueBuffer);
gettimeofday(&endTime, NULL);
printf("Sum = %llu\n", sum);
free(indexBuffer);
free(valueBuffer);
return ((endTime.tv_sec - startTime.tv_sec) * 1000 * 1000) + (endTime.tv_usec - startTime.tv_usec);
}
int main()
{
printf("sizeof buffers = %ldMb\n", BUFFER_SIZE * sizeof(unsigned int) / (1024 * 1024));
unsigned int timeInMicroSeconds = computeTimeInMicroSeconds();
printf("Time: %u micro-seconds = %.3f seconds\n", timeInMicroSeconds, (double) timeInMicroSeconds / (1000 * 1000));
}
Если я его запустил, то получу следующий результат:
$ gcc TestPrefetch.c -O3 -o TestPrefetch && ./TestPrefetch
sizeof buffers = 1562Mb
Sum = 439813150288855829
Time: 201172 micro-seconds = 0.201 seconds
Быстро и быстро !!! Насколько мне известно (я могу ошибаться), одна из причин наличия такой быстрой программы заключается в том, что при последовательном доступе к двум своим буферам данные могут быть предварительно загружены в кэш ЦП.
Мы можем сделать его более сложным, чтобы данные были (почти) предварительно загружены в кэш ЦП. Например, мы можем просто изменить функцию createIndexBuffer
в:
unsigned int * createIndexBuffer()
{
unsigned int * indexBuffer = (unsigned int *) malloc(BUFFER_SIZE * sizeof(unsigned int));
for (unsigned long i = 0 ; i < BUFFER_SIZE ; i++)
{
indexBuffer[i] = rand() % BUFFER_SIZE;
}
return (indexBuffer);
}
Попробуем еще раз программу:
$ gcc TestPrefetch.c -O3 -o TestPrefetch && ./TestPrefetch
sizeof buffers = 1562Mb
Sum = 439835307963131237
Time: 3730387 micro-seconds = 3.730 seconds
Более чем в 18 раз медленнее !!!
Теперь мы подошли к моей проблеме. Учитывая новую функцию createIndexBuffer
, я хотел бы ускорить функцию computeSum
с помощью предварительной выборки
unsigned long long computeSum(unsigned int * indexBuffer, unsigned int * valueBuffer)
{
unsigned long long sum = 0;
for (unsigned int i = 0 ; i < BUFFER_SIZE ; i++)
{
__builtin_prefetch((char *) &indexBuffer[i + 1], 0, 0);
unsigned int index = indexBuffer[i];
sum += valueBuffer[index];
}
return (sum);
}
Конечно, мне также нужно изменить свой createIndexBuffer
, чтобы он выделял буфер, содержащий еще один элемент
Я перезапускаю свою программу: не лучше! Поскольку предварительная выборка может быть медленнее, чем одна итерация цикла "для", я могу предварительно выбрать не один элемент перед, а два элемента перед
__builtin_prefetch((char *) &indexBuffer[i + 2], 0, 0);
не лучше! две итерации цикла? не лучше? Три? ** Я пробовал до 50 (!!!), но не могу повысить производительность моей функции computeSum
.
Могу ли я помочь понять, почему Большое спасибо за вашу помощь?