C++ 如何使用1个非常大的数组快速初始化
我有一个能量数组:C++ 如何使用1个非常大的数组快速初始化,c++,arrays,performance,C++,Arrays,Performance,我有一个能量数组: int* arr = new int[BIGNUMBER]; 如何用一个数字快速完成它。通常我会这样做 for(int i = 0; i < BIGNUMBER; i++) arr[i] = 1 for(int i=0;i
int* arr = new int[BIGNUMBER];
如何用一个数字快速完成它。通常我会这样做
for(int i = 0; i < BIGNUMBER; i++)
arr[i] = 1
for(int i=0;i
但我认为这需要很长时间
我可以使用
memcpy
或类似功能吗?您可以尝试使用标准功能:
使用memset或memcpy
memset(arr, 0, BIGNUMER);
尝试使用memset
memset(arr, 1, BIGNUMBER);
安迪·普劳尔的
std::uninitialized_fill_n()
解决方案的一些可能替代方案,仅供子孙后代使用:
- 如果你很幸运,并且你的值是由所有相同的字节组成的,那么我会这样做
- 有些实现提供了16位版本的memsetw,但这并不普遍
- GCC有一个扩展,可以填充范围
- 我曾经使用过一些ARM系统,这些系统的库加速了CPU和DMA的word fill变体,并在汇编中手工编码——如果您不太关心可移植性的话,您可以看看您的平台是否提供了这些功能
- 根据处理器的不同,即使是查看SIMD内部函数的循环也可能会带来提升;一些SIMD单元具有加载/存储管道,这些管道针对这样的数据移动进行了优化。另一方面,在寄存器类型之间移动可能会受到严厉的惩罚
未初始化的填充
以外的任何东西来权衡可移植性或可读性
您可能对前面的问题感兴趣:
std::vector<int> v(BIGNUMBER, 1); // 'BIGNUMBER' elements, all with value 1.
std::向量v(BIGNUMBER,1);/'BIGNUMBER'元素,所有元素的值均为1。
如前所述,绩效需要衡量。这种方法提供了自动释放内存的额外好处。在启用优化的Linux/x86 gcc下,您的代码将编译为以下内容:
rax = arr
rdi = BIGNUMBER
400690: c7 04 90 01 00 00 00 movl $0x1,(%rax,%rdx,4)
将立即int(1)
移动到rax+rdx
400697: 48 83 c2 01 add $0x1,%rdx
增量寄存器
40069b: 48 39 fa cmp %rdi,%rdx
Cmp-rdi到rdx
40069e: 75 f0 jne 400690 <main+0xa0>
40069e:75 f0 jne 400690
如果已达到BIGNUMBER,请跳回开始
在我的机器上,每GB大约需要1秒,但我打赌大部分时间都是在物理内存中分页以支持未初始化的分配。只需将循环展开8或16次即可。像
memcpy
这样的函数速度很快,但它们确实是为了方便,而不是为了比您可能编写的任何函数都快:
for (i = 0; i < BIGNUMBER-8; i += 8){
a[i+0] = 1; // this gets rid of the test against BIGNUMBER, and the increment, on 7 out of 8 items.
a[i+1] = 1; // the compiler should be able to see that a[i] is being calculated repeatedly here
...
a[i+7] = 1;
}
for (; i < BIGNUMBER; i++) a[i] = 1;
(i=0;i{
a[i+0]=1;//这将消除针对BIGNUMBER的测试,以及8项中7项的增量。
a[i+1]=1;//编译器应该能够看到此处重复计算a[i]
...
a[i+7]=1;
}
对于(;i
编译器可能可以为您展开循环,但为什么要冒险呢?memset(arr,1,sizeof(int)*BIGNUMBER) 尽量避免假设某件事需要多长时间才能完成。你真的试过这样做吗?需要多长时间?通常情况下,bigname
是什么?如果这是您的瓶颈,那么您的代码结构错误。我猜你填充数组是有原因的,所以你必须有另一个(可能更慢的)循环。无论如何,memcpy
可能会慢一些,因为它需要进行内存查找,而不是使用寄存器/常量。仅供参考:我尝试了基于memcpy的算法,但没有得到任何有意义的速度差异。@RobertKilar:以前尝试使用std::uninitialized\u fill\n()
函数,并检查它是否适合您。最有可能的是,该函数最终调用了memcpy()
,用于普通类型,所有内容都被内联。在这种情况下,uninitialized\u fill\n
比更明显的fill\n
提供了什么,不适用于普通类型-只是名称更清楚地表明将填充未初始化的内存区域。我使用BIGNUMBER=1测试了OP解决方案和std::uninitialized_fill
,但对于非普通类型,这将是未定义的行为,因为内存已经用有效对象初始化。您必须使用fill\n
。这是我的(小)反对意见,它对所有类型都更加一致。但我也理解你的推理。memset
不适用于非零数字,因为它在字节级别上工作memcpy
可能会比手动循环慢,因为它需要同时引用其他内存writing@Dave:您是指非字节值memset
肯定可以处理任意无符号字符。@Dave还说,memcpy的速度会慢一些,因为您必须从某个地方复制,这意味着您必须用1填充数组,然后在该数组上使用memcpy。如果仅仅用1来填充数组,那似乎很愚蠢。@user268396不,我的意思是memset(arr,1,BIGNUMBER)
将用16843009而不是1来填充数组。@Dave:这在读取时是一个微妙的类型问题,因为int
的字节数比无符号字符的字节数多。然而,实际上,您的评论似乎暗示memset(arr,0xA,BIGNUMBER)
将无法用AAAA
填充数组(假设为32位整数)。哪一个最有效memset
不适用于非零数字,因为它在字节级别工作。那么您必须在char
数组上使用过它。在int
数组上(如问题中所述),它将产生错误的结果。e、 g.memset(arr,1,…)
将数字设置为16843009(假设为32位
40069e: 75 f0 jne 400690 <main+0xa0>
for (i = 0; i < BIGNUMBER-8; i += 8){
a[i+0] = 1; // this gets rid of the test against BIGNUMBER, and the increment, on 7 out of 8 items.
a[i+1] = 1; // the compiler should be able to see that a[i] is being calculated repeatedly here
...
a[i+7] = 1;
}
for (; i < BIGNUMBER; i++) a[i] = 1;