不完全理解 C 中自定义编写的 'memcpy' 函数
Don't fully understand custom-written 'memcpy' function in C
所以我今天早些时候在浏览 Quake 引擎源代码时偶然发现了一些编写的实用函数。其中之一是 'Q_memcpy':
void Q_memcpy (void *dest, void *src, int count)
{
int i;
if (( ( (long)dest | (long)src | count) & 3) == 0 )
{
count>>=2;
for (i=0 ; i<count ; i++)
((int *)dest)[i] = ((int *)src)[i];
}
else
for (i=0 ; i<count ; i++)
((byte *)dest)[i] = ((byte *)src)[i];
}
我理解函数的整个前提,但不太理解源地址和目标地址之间按位或的原因。所以我的问题总结如下:
- 为什么 'count' 被用在相同的按位运算中?
- 为什么要检查结果的最后两位是否不同?
- 这整个检查有什么用?
我确定这是显而易见的,但请原谅我的无知,因为在编程方面我还没有真正深入研究更底层的方面。我只是觉得它很有趣,想了解更多。
它首先测试所有 3 个参数 是否可以被 4 整除。如果 - 且仅当 - 它们都是,它继续一次复制 4 个字节。
即这个未解码的是
if ((long) src % 4 == 0 && (long) dst % 4 == 0 && count % 4 == 0 )
{
count = count / 4;
for (i = 0; i < count; i++)
((int *)dest)[i] = ((int *)src)[i];
}
我不确定他们是否测试了他们的编译器并且它生成了错误的代码以进行测试,因此他们决定以如此复杂的方式编写它。在任何情况下,如果在 x
、y
或z
。因此,如果 (x | y | z) & 3
结果为 0,则 none 的数字设置了 2 个最低位中的任何一个,因此可以被 4 整除。
当然,现在使用 会相当愚蠢 - 最近的库实现中的标准库 memcpy
几乎肯定比这个更好。
因此,在最近的编译器上,您可以通过将 Q_memcpy
切换为 memcpy
来 优化 所有对 Q_memcpy
的调用。 GCC 可以生成诸如 64 位或 SIMD 之类的东西 memcpy
取决于要复制的区域的大小。
与 3 的按位 ORing 和 ANDing 是检查源、目标和 count
是否可以被 4 整除。如果是,该操作可以处理 4 字节的字,而这段代码假设int
为 4 个字节。否则操作按字节执行。
正在查明源指针和目标指针是否 int
对齐,以及 count
是否是精确的 int
字节大小。
如果这三件事都是真的,l.s。它们中的 2 位都是 0
(假设指针和 int
是 4 个字节)。因此该算法对三个值进行或运算,并隔离出 l.s。 2 位。
在这种情况下,它将 int
复制 int
。否则它将 char
复制 char
.
如果测试失败,更复杂的算法将通过 char
复制一些前导和尾随字节 char
,并通过 int
复制一些中间字节 int
。
所以我今天早些时候在浏览 Quake 引擎源代码时偶然发现了一些编写的实用函数。其中之一是 'Q_memcpy':
void Q_memcpy (void *dest, void *src, int count)
{
int i;
if (( ( (long)dest | (long)src | count) & 3) == 0 )
{
count>>=2;
for (i=0 ; i<count ; i++)
((int *)dest)[i] = ((int *)src)[i];
}
else
for (i=0 ; i<count ; i++)
((byte *)dest)[i] = ((byte *)src)[i];
}
我理解函数的整个前提,但不太理解源地址和目标地址之间按位或的原因。所以我的问题总结如下:
- 为什么 'count' 被用在相同的按位运算中?
- 为什么要检查结果的最后两位是否不同?
- 这整个检查有什么用?
我确定这是显而易见的,但请原谅我的无知,因为在编程方面我还没有真正深入研究更底层的方面。我只是觉得它很有趣,想了解更多。
它首先测试所有 3 个参数 是否可以被 4 整除。如果 - 且仅当 - 它们都是,它继续一次复制 4 个字节。
即这个未解码的是
if ((long) src % 4 == 0 && (long) dst % 4 == 0 && count % 4 == 0 )
{
count = count / 4;
for (i = 0; i < count; i++)
((int *)dest)[i] = ((int *)src)[i];
}
我不确定他们是否测试了他们的编译器并且它生成了错误的代码以进行测试,因此他们决定以如此复杂的方式编写它。在任何情况下,如果在 x
、y
或z
。因此,如果 (x | y | z) & 3
结果为 0,则 none 的数字设置了 2 个最低位中的任何一个,因此可以被 4 整除。
当然,现在使用 会相当愚蠢 - 最近的库实现中的标准库 memcpy
几乎肯定比这个更好。
因此,在最近的编译器上,您可以通过将 Q_memcpy
切换为 memcpy
来 优化 所有对 Q_memcpy
的调用。 GCC 可以生成诸如 64 位或 SIMD 之类的东西 memcpy
取决于要复制的区域的大小。
与 3 的按位 ORing 和 ANDing 是检查源、目标和 count
是否可以被 4 整除。如果是,该操作可以处理 4 字节的字,而这段代码假设int
为 4 个字节。否则操作按字节执行。
正在查明源指针和目标指针是否 int
对齐,以及 count
是否是精确的 int
字节大小。
如果这三件事都是真的,l.s。它们中的 2 位都是 0
(假设指针和 int
是 4 个字节)。因此该算法对三个值进行或运算,并隔离出 l.s。 2 位。
在这种情况下,它将 int
复制 int
。否则它将 char
复制 char
.
如果测试失败,更复杂的算法将通过 char
复制一些前导和尾随字节 char
,并通过 int
复制一些中间字节 int
。