【发布时间】:2017-04-18 10:18:23
【问题描述】:
我正在尝试通过使用预取来加速单个程序。我的程序的目的只是为了测试。它的作用如下:
- 它使用两个大小相同的 int 缓冲区
- 它一个一个地读取第一个缓冲区的所有值
- 它读取第二个缓冲区中索引处的值
- 它将从第二个缓冲区获取的所有值相加
- 它完成了之前的所有步骤,越来越大
- 最后,我打印自愿和非自愿CPU的数量
第一次,第一个缓冲区中的值包含其索引的值(参见下面代码中的函数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
又快又快!!! 据我所知(我可能错了),拥有这么快的程序的原因之一是,当我按顺序访问我的两个缓冲区时,可以在 CPU 缓存中预取数据。
我们可以使其更复杂,以便(几乎)将数据预取到 CPU 缓存中。例如,我们可以只更改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,以便它分配一个具有更多元素的缓冲区
我重新启动我的程序:不是更好!由于预取可能比一次“for”循环迭代慢,我可能预取不是之前的一个元素,而是之前的两个元素
__builtin_prefetch((char *) &indexBuffer[i + 2], 0, 0);
不是更好!两个循环迭代? 不是更好?三? **我一直尝试到 50 (!!!) 但我无法提高我的函数的性能computeSum。
我可以帮助我了解原因吗 非常感谢您的帮助
【问题讨论】:
标签: performance gcc compiler-optimization cpu-cache prefetch