繁体   English   中英

C语言中memset函数的复杂性

[英]Complexity of the memset function in C

我和一些朋友讨论了一段代码,我们讨论了在C中使用memset函数,如果我们初始化一个大小为N的数组,这个函数的Big-O表示法的顺序是什么?

在您可以直接访问页表并且以分层方式存储的系统上,通过将整个虚拟地址映射替换为对单个页面的写时复制引用,可以在O(log n)实现memset填充给定的字节值。 但请注意,如果您将来对该对象进行任何修改,则memset的正常O(n)成本将被推迟到页面错误,以在修改页面时实例化单独的页面副本。

您询问了复杂性,但您可能打算询问性能。

用符号O(n)表示的复杂性是与算法中的操作数量如何随着问题大小增长而被迫增长有关的概念。 O(n)表示必须执行与输入大小成比例的一些步骤。 它没有说明这个比例是多少。 memset是O(n)。 O(n 2 )表示必须执行与n 2成比例的一些步骤。 memset不是O(n 2 ),因为设置2n个字节的工作量只是n个字节的两倍,而不是工作量的四倍。

您可能对memset的性能更感兴趣,因为memset的库版本比您可能编写的C版本执行得更快。

库版本执行速度更快,因为它使用专门的指令。 最常见的现代处理器具有允许它们在一条指令中将16字节写入存储器的指令。 库实现者用汇编语言或接近它的东西编写像memset这样的关键函数,因此他们可以访问所有这些指令。

用C语言编写时,编译器很难利用这些指令。 例如,指向您正在设置的内存的指针可能不会与16个字节的倍数对齐。 memset作者将编写测试指针的代码,并为每种情况分支到不同的代码,目标是单独设置一些字节,然后使用一个对齐的指针,这样他们就可以使用存储16字节的快速指令。时间。 这只是库编写器在编写memset等例程时要处理的许多复杂问题之一。

由于这些复杂性,编译器无法轻松采用memset的C实现并将其转换为专家编写的快速代码。 当编译器在C代码中看到一次写入一个字节的循环时,它通常会生成一次写入一个字节的汇编语言。 优化器变得越来越聪明,但复杂性限制了它们允许执行的程度以及它们可以执行多少操作而无需生成大量代码来处理可能很少发生的情况。

复杂度为O(n)。 这是基本的东西。

一些C库提供了memset()矢量化版本。 除非您的编译器执行自动矢量化和循环展开,否则for循环将比矢量化memset()慢。 矢量化与否, memset()受存储器带宽的限制,最小时间与数组大小除以存储器带宽成正比,即当存储器带宽恒定时,它是O(n)操作。

在NUMA机器上,可以对非常大的阵列进行线程化,以实现NUMA节点数量级的加速。 有关基准测试,请参阅此答案

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM