我的代码需要处理包含多个字符串的大量结构。
实际上,整个数组将包含大约 25k 个结构,每个结构的大小约为 256 字节,因此整个数组需要大约 6 MiB 的堆空间。
// example
struct element {
char foo[32];
char bar[16];
...
}; // sizeof(struct element) = 256
我担心
calloc
的性能,因为它会将所有内存清零,而且我不需要初始化每个字节。所以我就这么做了element_arr = malloc(num_elements * sizeof(struct element))
。
我在运行时分配数组,因为我在编译时不知道
num_elements
。
为了让我的代码正常工作,我实际上只需要每个成员的第一个字节(
foo
、bar
等)为零,其余部分可以保持未初始化。
假设每个结构有 8 个字符串成员,所以我只需要 3% 的清零字节,其他 97% 的清除字节都是浪费,因为它们最终会被真实数据覆盖)。
我看到几个选项:
立即将所有内容归零,例如与
calloc
这确实(我希望)使
使用向量指令写入大块对齐的
零。
memset
每个 256 字节大小 struct element
,然后用实际数据填充。
为
struct element
的每个成员分配0。 (*element->foo = 0; ...
)
这转化为一系列 mov
指令,并在 -O3
进行优化。
语言方面写起来很麻烦(但是可以处理)。
mov byte ptr [rdi + 152], 0
mov byte ptr [rdi + 208], 0
mov byte ptr [rdi + 200], 0
mov byte ptr [rdi + 128], 0
...
arm64 看起来类似。
element_arr
的大小(例如 64 MiB)做出非常保守的假设,将其放置在内存的零初始化部分。 (操作系统需要将我的内存清零)char element_arr[64 * 1000 * 1000] = {0};
(检查num_elements < 250000
以确保)
选择什么选项有区别吗? 你有什么建议?
您可能太早考虑优化了。进行计算以仅初始化部分内存,或手动调用
memset()
,很容易比仅调用 calloc()
慢很多。
M_MMAP_MAX
(默认为 64KiB),因此内存将直接来自专用 mmap()
调用,并且一开始就已经被清零,无需做任何事情的需要。因此,在这里使用除 calloc()
之外的任何内容很可能没有意义。
仅当您使用某些外来的 libc 实现时才进行选择性清零,并执行适当的基准测试以确定其性能。如果您使用 glibc、musl 和其他主要 C 库,您应该避免在如此大的分配上手动归零并使用
calloc()
。 M_MMAP_MAX
将保证 mmap()
的内存归零。
你有什么建议?
设置测试硬度来评估性能,然后对各种代码选项进行评级。
如果不分析代码,仍然存在许多依赖于实现的问题。考虑一下操作系统可能会在空闲时间将内存池清零。例如。代码执行
scanf()
调用并等待输入。在此期间,也许兆字节的内存会被清零以供以后使用 - 有效地使程序花费零额外的时间来实现零内存。操作系统甚至可能使用程序启动之前创建的池中的清零内存。即使 calloc()
也不一定会使用内存
,因为真正的使用可能会推迟到代码访问内存为止。