【发布时间】:2011-08-22 22:45:25
【问题描述】:
编辑:我编辑了问题及其标题以更准确。
考虑以下源代码:
#include <vector>
struct xyz {
xyz() { } // empty constructor, but the compiler doesn't care
xyz(const xyz& o): v(o.v) { }
xyz& operator=(const xyz& o) { v=o.v; return *this; }
int v; // <will be initialized to int(), which means 0
};
std::vector<xyz> test() {
return std::vector<xyz>(1024); // will do a memset() :-(
}
...如何避免 vector 分配的内存使用其第一个元素的副本进行初始化,这是一个 O(n) 操作为了速度我宁愿跳过,因为我的默认值构造函数什么都不做?
如果不存在通用解决方案,则可以使用 g++ 特定解决方案(但我找不到任何属性来做到这一点)。
编辑:生成的代码如下(命令行:arm-elf-g++-4.5 -O3 -S -fno-verbose-asm -o - test.cpp | arm-elf-c++ filt | grep -vE '^[[:space:]]+[.@].*$' )
test():
mov r3, #0
stmfd sp!, {r4, lr}
mov r4, r0
str r3, [r0, #0]
str r3, [r0, #4]
str r3, [r0, #8]
mov r0, #4096
bl operator new(unsigned long)
add r1, r0, #4096
add r2, r0, #4080
str r0, [r4, #0]
stmib r4, {r0, r1}
add r2, r2, #12
b .L4 @
.L8: @
add r0, r0, #4 @
.L4: @
cmp r0, #0 @ fill the memory
movne r3, #0 @
strne r3, [r0, #0] @
cmp r0, r2 @
bne .L8 @
str r1, [r4, #4]
mov r0, r4
ldmfd sp!, {r4, pc}
编辑:为了完整起见,这里是 x86_64 的程序集:
.globl test()
test():
LFB450:
pushq %rbp
LCFI0:
movq %rsp, %rbp
LCFI1:
pushq %rbx
LCFI2:
movq %rdi, %rbx
subq $8, %rsp
LCFI3:
movq $0, (%rdi)
movq $0, 8(%rdi)
movq $0, 16(%rdi)
movl $4096, %edi
call operator new(unsigned long)
leaq 4096(%rax), %rcx
movq %rax, (%rbx)
movq %rax, 8(%rbx)
leaq 4092(%rax), %rdx
movq %rcx, 16(%rbx)
jmp L4 @
L8: @
addq $4, %rax @
L4: @
testq %rax, %rax @ memory-filling loop
je L2 @
movl $0, (%rax) @
L2: @
cmpq %rdx, %rax @
jne L8 @
movq %rcx, 8(%rbx)
movq %rbx, %rax
addq $8, %rsp
popq %rbx
leave
LCFI4:
ret
LFE450:
EH_frame1:
LSCIE1:
LECIE1:
LSFDE1:
LASFDE1:
LEFDE1:
编辑:我认为结论是当你想避免不必要的初始化时不要使用std::vector<>。我最终展开了我自己的模板化容器,它的性能更好(并且有用于 neon 和 armv7 的专门版本)。
【问题讨论】:
-
纯粹出于好奇:为什么?
-
g++ 不会将值自动初始化为 0。一些编译器在调试模式下会这样做,但不会在优化模式下。
-
@ildjarn:因为用零填充内存是有代价的,我希望编译器在我保留未初始化的值时尊重我的选择。
-
您认为
v是值初始化的假设是不正确的:它不是值初始化的。编译器、库、运行时或操作系统可能会为您对内存进行零初始化,但这不是值初始化。 -
@jcayzac:出于好奇,为什么不简单地调用
reserve,而不是实际放置元素?或者,您如何使用vector之后未初始化?
标签: c++ optimization gcc default-value