Assignment与mempcy——在这种情况下会更快

Assignment vs mempcy - which will be faster in this case

本文关键字:这种情况下 mempcy Assignment      更新时间:2023-10-16

两者中哪一个更快:?

1.

char* _pos ..;
short value = ..;
*((short*)_pos = va;

2.

char* _pos ..;
short value = ..;
memcpy(_pos, &value, sizeof(short));

与所有"哪个更快?"问题一样,您应该对其进行基准测试,以便自己查看。如果这很重要,那就问问为什么,然后选择你想要的。

无论如何,您的第一个示例在技术上是未定义的行为,因为您违反了严格别名。所以,如果你不得不在没有基准测试的情况下进行选择,那就选择第二个。


要回答实际问题,哪个更快可能取决于pos的排列。如果它正确对齐,那么1可能会更快。如果不是,那么2可能会更快,这取决于编译器对它的优化方式。(如果硬件不支持错位访问,1甚至可能崩溃。)

但这都是猜测。你真的需要对它进行基准测试才能确定
至少,您应该查看已编译的程序集:

:     *(short *)_pos = value;
mov WORD PTR [rcx], dx

与。

:     memcpy(_pos, &value, sizeof(short));
mov WORD PTR [rcx], dx

在这种情况下(在MSVC中),它显示了具有默认优化的完全相同的程序集。因此,您可以期望性能相同。

gcc处于-O1或更高的优化级别时,以下两个函数在x86上编译为完全相同的机器代码:

void foo(char *_pos, short value)
{
        memcpy(_pos, &value, sizeof(short));
}
void bar(char *_pos, short value)
{
        *(short *)_pos = value;
}

编译器可能以相同的方式实现它们
如果它做得很天真,分配会更快

出于任何实际目的,它们都会很快完成,你不需要担心。

还要注意,您可能存在对齐问题s(_pos可能在2个字节上未对齐,这可能会在某些处理器上崩溃)和类型双关问题(编译器可能会假设_pos所指向的内容没有更改,因为您使用short *编写)。

这有关系吗?第一种情况可能会为您节省一些周期(取决于编译器的复杂程度和优化)。但是,它值得在易用性和可维护性方面受到打击吗?

由于过早优化,引入了许多错误。您应该首先确定瓶颈,如果这个任务就是瓶颈,则对每个选项进行基准测试(注意其他人已经提到的对齐和其他问题)。

问题取决于实现。在实践中,对于只复制sizeof(短)字节的操作,如果速度较慢,那么它将是memcpy。对于相当大的数据集,如果要更快,通常会是memcpy。

如前所述,#1调用未定义的行为。

我们可以看到,简单的作业肯定比两者更容易读写,也不容易出错。清晰性和正确性应该放在第一位,即使在性能关键的领域也是如此,原因很简单,优化正确的代码比修复优化的、不正确的代码更容易。如果这真的是一个C++问题,那么对这样的代码(强制转换或memcpy,将类型系统推到x射线和位周围复制)的需求应该非常非常少。

如果您确信不会出现对齐问题,并且您确实发现这是一个瓶颈情况,那么继续执行第一个操作。

如果你不喜欢给memcpy打电话,那么做一些类似的事情:

*pos = static_cast<char>(value & 0xff );
*(pos+1) = static_cast<char>(value >> 8 );

尽管如果要这样做,那么就使用无符号值。

上面的代码确保您也能得到小的endian。(如果您想要big-endian,显然可以颠倒赋值的顺序)。如果数据以某种二进制blob的形式传递,您可能需要一致的endian-ness,我认为这就是您试图创建的。

如果你想创建二进制Blob,你可能希望使用类似谷歌协议缓冲区的东西。还有boost::serialize,它包括二进制序列化。

您可以通过使用并集来避免破坏别名规则和调用函数:

union {
    char*  c;
    short* s;
} _pos;
short value = ...
_pos->s = value;