std::copy、std::equal、std::fill 相对于 memcpy、memset、memcmp 的实际优势

Practical advantages of std::copy, std::equal, std::fill over memcpy, memset, memcmp

提问人:kingsjester 提问时间:11/17/2023 最后编辑:kingsjester 更新时间:11/17/2023 访问量:156

问:

一些帖子比较了 C++ 风格函数和 C 风格函数之间的性能,例如:std::copy, std::equal, std::fillstd::memcpy, std::memcmp, std::memset

但是,从实用的角度来看,我还没有找到用这些新功能替换旧函数的合理性。当然,功能列表并非详尽无遗。

这些新版本的实际优势是什么?

如果性能不是选择的标准,那么是否希望(在时间允许的情况下)用 C++ 版本替换这些 C 样式函数的所有出现?

我试图理解为什么引入了一些功能,以及是否有必要进行替换。c++

C++ memcpy memset memcmp

评论

3赞 HolyBlackCat 11/17/2023
按位复制/比较不适用于许多类类型,例如容器或包含一个类作为成员的任何类型。“无需仅对标签编辑投反对票”我没有投反对票。
1赞 HolyBlackCat 11/17/2023
不用担心。反对票可能来自那些认为谷歌答案太容易的人。
2赞 HolyBlackCat 11/17/2023
@Fareanor 有“性能较差”的来源吗?表示几乎相同的性能。
1赞 HolyBlackCat 11/17/2023
@Fareanor 我在这里看到什么?我看到“旨在成为内存到内存复制的最快库例程”,但我也看到“几个 C++ 编译器将合适的内存复制循环转换为 std::memcpy 调用”。
1赞 molbdnilo 11/17/2023
如果元素类型比 宽,则只能用字节具有相同位模式的值填充它(尝试使用两个 and 用 1 () 填充数组,看看会发生什么)。 对结构数组毫无用处,因为它还比较了填充。charmemsetint1memsetstd::fillmemcmp

答:

2赞 463035818_is_not_an_ai 11/17/2023 #1

memset有两个巨大的局限性。

首先是容器。对于使用连续内存的容器,只能使用多个元素。它可以适用于大多数其他容器,但不适用于大多数其他容器。memsetstd::arraystd::vector

接下来是元素。您只能简单地复制对象。已经是“简单”的东西,因为不能轻易复制。memsetstd::string

memset在某些情况下可以使用。您提到的其他函数更普遍适用,当它们可以被编译器替换时可以做到这一点。memset

评论

0赞 James Kanze 11/17/2023
在某些情况下,编译器可以替换为。然而,这几乎总是悲观的,因为直接以内联方式生成代码通常比设置函数调用所需的代码快几倍,并且可能更小。std::copymemset
0赞 Red.Wave 11/17/2023
@JamesKanze很长一段时间后从你那里读到的。请不要重复OP的错别字! 通常对应于 。std::copystd::memcpy
3赞 James Kanze 11/17/2023 #2

正如评论中所指出的,等工作,其中如,等。仅在极少数情况下有效。特别是,不适用于任何具有非平凡构造函数的东西,因此不适用于任何标准容器。即使在 C 样式或 s 的 C 样式数组上使用时,它也只能进行浅层复制,因此根据对象的语义,它甚至可能不适用于包含指针的 C 样式。std::copystd::memcpystd::memcpystructstructstruct

关于性能,当然没有什么是可以保证的。但是编译器可以访问 的源代码(因为它是一个模板),并且通常能够内联它——对于 C 样式,这通常会导致复制更大的块(机器字,而不是字节)。在 的情况下,通常会有一个函数调用,它隐藏了有关底层上下文的任何信息,并且需要逐字节复制(导致循环中的次数更多),或者需要处理对齐和可能的奇数字节的复杂逻辑。C标准确实允许成为一个宏,解析为一个特殊的符号,触发某种编译器优化,但我认为这在C++中是不合法的。一些早期的(C)标准C编译器确实将其识别为关键字,以便生成内联代码。但这一切都是很久以前的事了(在第一个 C 标准之前)。std::copystructstd::memcpymemcpymemcpy

在今天的 C++ 代码中,绝对没有理由使用 ,永远。std::memcpy

评论

0赞 HolyBlackCat 11/18/2023
关于的部分非常具有误导性。编译器可以而且确实对函数有特殊的了解,而不需要它们就是宏或关键字,事实上,所有三个大编译器都可以优化。GCC 和 Clang 甚至在 ,而 MSVC 需要: gcc.godbolt.org/z/vqE7jqG3Tmemcpymemcpy-O0/O2
0赞 James Kanze 11/18/2023
@HolyBlackCat我在生成的代码中没有看到它:或.(我没有安装 clang。也许优化只存在于 C 中(我知道一些 C 编译器在 1980 年代就有了它。我不希望它在 C++ 编译器中出现,因为我不希望 C++ 代码使用 .但也许这取决于上下文——我只是写了一个简单的函数,除了调用它的参数之外什么也没做。g++ -S -O3cl /c /O2 /Famemcpymemcpy
0赞 HolyBlackCat 11/18/2023
你的意思是你没有看到?这表明编译器对其进行了优化,因此它必须对它有特殊的了解才能做到这一点。memcpy
0赞 James Kanze 11/19/2023
@HolyBlackCat我的意思是我没有看到优化。通过优化,两个编译器在对寄存器进行了一些摆弄之后,都会以跳转到结束函数(因为调用是我的测试函数中的最后一件事),以便参数位于它们应该在的位置。memcpymemcpy
1赞 James Kanze 11/20/2023
有趣。所以它确实取决于上下文。(我相信我提到过它可能会。我的测试涉及在任意缓冲区上调用它,它没有经过优化。因此,当调用站点上提供的信息时,编译器会或多或少地处理相同的信息。std::copystd::memcpystd::copy
1赞 Caleth 11/17/2023 #3

但是,从实用的角度来看,我还没有找到用这些新功能替换旧函数的合理性。

这些函数更通用。 并且仅针对 TriviallyCopyable 的连续类型范围定义,并且(通常)仅对没有填充的 TriviallyCopyable 类型的连续范围有意义。注意,单个物体是长度为1的连续范围。memcpymemsetmemcmp

只有当它们执行相同的操作时,性能才具有可比性,并且根据假设规则,实现可以选择以完全相同的方式实现对的调用,就像它可以证明范围不重叠或其他方式时一样。与 1 类似。std::copymemcpymemmovestd::fillmemsetstd::equalmemcmp

  1. 稍作修改,因为它不在乎少或多,只在乎相等

评论

0赞 kingsjester 11/17/2023
谢谢,这就是我一直在找的。它还回答了这个问题:“如果性能不是选择的标准,那么是否希望(在时间允许的情况下)用C++版本替换这些 C 样式函数的所有出现? -> 是的,用于在连续类型和 TriviallyCopyguable 类型到非连续类型或非 TriviallyCopyable 类型之间的可移植性。