我试图通过使用预取来加速单个程序。我的程序的目的只是为了测试。以下是它的作用:
第一次,第一个缓冲区中的值包含其索引的值(参见下面代码中的函数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
的性能。
我能帮你理解为什么非常感谢你的帮助吗
很抱歉我给你的不是我代码的正确版本。正确的版本是,你所说的:
__builtin_prefetch((char *) &valueBuffer[indexBuffer[i + prefetchStep]], 0, 0);
然而,即使是正确的版本,不幸的是它并没有更好
预取通常是一个完整的缓存线。这通常是64字节。因此,随机示例总是为4字节整数获取64字节。16倍于您实际需要的数据,这非常适合减速18倍。因此,代码只受内存吞吐量而不是延迟的限制。
我相信上面的代码是由CPU自动优化的,没有任何空间进行手动优化。
1.主要问题是indexBuffer
是按顺序访问的。硬件预取器检测到它并自动预取更多值,而无需手动调用预取。因此,在迭代i期间,值indexBuffer[i1]
,indexBuffer[i2]
,。。。已在缓存中。(顺便说一句,不需要在数组末尾添加人工元素:预取指令会自动忽略内存访问错误)。
您真正需要做的是预取valueBuffer
:
__builtin_prefetch((char *) &valueBuffer[indexBuffer[i + 1]], 0, 0);
2.但是在这样简单的场景中,添加上面的代码行也没有帮助。访问内存的成本是数百个周期,而添加指令是〜1个周期。您的代码已经花费了99%的时间在内存访问上。添加手动预取将使这一周期更快,也不会更好。
如果你的数学要复杂得多(试试看),比如使用一个包含大量未优化的out除法(每个20-30个循环)的表达式,或者调用一些数学函数(log,sin),那么手动预取会非常有效。
3.但即使这样也不能保证有帮助。循环迭代之间的依赖性非常弱,它仅通过sum
变量实现。这允许CPU推测性地执行指令:它可能会同时开始获取valueBuffer[i]
,同时仍然执行valueBuffer[i]
的数学运算。
问题内容: 我正在尝试调试使用很多指针的二进制文件。有时为了快速查看输出以找出错误,我打印了对象的地址及其对应的值,但是对象地址是随机的,这违背了快速检查的目的。有没有一种方法可以暂时/永久禁用此功能,以便每次运行程序时都获得相同的值。 哎呀。操作系统是 问题答案: 在Ubuntu上,可以使用…禁用它。 在Windows上,这篇文章可能会有所帮助… http://blog.didiersteven
我有一个python程序,其中我需要加载和反序列化一个1GB的pickle文件。它需要一个良好的20秒,我想有一个机制,使腌菜的内容是随时可用的使用。我看过shared_memory,但是所有使用它的例子似乎都涉及到numpy,而我的项目没有使用numpy。使用或其他方式实现此目标的最简单、最干净的方法是什么? 这就是我现在(每次运行时)加载数据的方式: 我一直在使用,但对于一个包含许多文件的大型
问题内容: 是否有Python文件类型可用于访问随机行而不遍历整个文件?我需要在一个大文件中搜索,无法将整个内容读取到内存中。 任何类型或方法将不胜感激。 问题答案: 这似乎只是设计的目的。一个对象为文件创建一个类似于字符串的接口: 如果您想知道,还可以将对象分配给:
问题内容: 允许从向量中进行加权选择,即 选择概率为0.2的1,概率为0.5的2和概率为0.3的3。 如果我们想对每个行都是概率向量的2D数组(矩阵)以向量化的方式快速进行操作,该怎么办?也就是说,我们想要一个来自随机矩阵的选择向量吗?这是超级慢的方式: : 这篇文章表明,并且可能是一种潜在的方法,而且很快。但是虽然可以沿numpy数组的一个轴执行此操作,但是该函数一次只能在单个数组上运行。同样,
给定一个随机数生成器random(7),它可以以相等的概率生成数字1,2,3,4,5,6,7(即每个数字出现的概率为1/7)。现在我们要设计一个随机数(5),它能以相等的概率(1/5)生成1,2,3,4,5。 有一种方法:每次我们随机运行(7),只有当它生成1-5时才返回。如果是6或7,再运行一次,直到它是1-5。 我有点困惑。第一个问题是: 如何用数学方法证明每个数字发生的概率是1/5?例如,假
/spark/python/pyspark/sql/dataframe.py“,第1313行,在withColumn assert isinstance(col,Column)中,”col应该是Column“assertionerror:col应该是Column 如何使用自定义函数或randint函数为列生成随机值?