是什么在64位系统在64位系统中复制unsigned int 2次和unsigned long 1次之间有什么区别?
*(unsigned*)d = *(unsigned*)s;
d+=4; s+=4;
*(unsigned*)d = *(unsigned*)s;
d+=4; s+=4;
和
*(unsigned long*)d = *(unsigned long*)s;
d+=8; s+=8;
之间的区别?
是什么在64位系统在64位系统中复制unsigned int 2次和unsigned long 1次之间有什么区别?
*(unsigned*)d = *(unsigned*)s;
d+=4; s+=4;
*(unsigned*)d = *(unsigned*)s;
d+=4; s+=4;
和
*(unsigned long*)d = *(unsigned long*)s;
d+=8; s+=8;
之间的区别?
只要没有任何不愉快的发生在相对于填充位或严格别名规则的,并假设类型的大小是按预期,和条件是所述存储器区域不重叠,并且被正确地对准,则它们每一从一个地方复制8个字节到另一个地方。
当然,除了实际效果有可能在性能和/或代码大小的差。
如果你看到的东西休息,然后看所发出的实际代码,这可能会告诉你出了什么问题。除非你有很多优化开启,甚至可能进行优化,否则我不会立即明白为什么这些优化不会与AMD64,Ubuntu和gcc相当。
事情我已经提到,可能会出错:
unsigned
的unsigned long
有填充位,如果这样的话有可能是位它们是一个或两个陷阱表示的模式,只要您解除引用就可能爆炸。s
和d
是转换指针到双到uint8_t*
的结果,你看看所产生的双,然后在一个或这两种情况下,你可能看不到变化的影响,因为你有一个非法的类型 - 双关语。该类型的sizeof(long) == 4
那么这两个是不等价不应适用于此。 long
在64位Windows系统上是32位,而不是64位Linux系统。d == s + 4
,那么这两个代码片段有不同的效果。因此,除非编译器知道d
和s
指向完全不同的地方(这就是C99 restrict
的用途),否则不会看到第一个优化成为第二个优化。s
或d
正确对齐int
而不是long
那么就有区别。 (编辑:显然你可以启用或禁用x86-64上未对齐访问的硬件异常)。如果需要精确地复制一个八字节,为什么不使用的memcpy()?
memcpy(d, s, 8);
使用GCC,它会散发出内嵌代码,而不是调用库函数,所以它应该是为你的手写入内存复制速度更快。
加奖金,你的代码将在ILP32系统工作,LP64(64位大部分的Unix)和LLP64(Win64的),甚至与严格对齐要求的系统。
“它应该像你手写的内存拷贝一样快 - ”在类似x86的架构上可能是真的。在像ARM,虽然,这确实有严格的对准要求,该版本与石膏可以使用需要对齐的访问速度更快的操作码,而'memcpy'必须要么使用速度较慢的操作码,与未对齐指针的应对,或者至少进行一些检查在使用快速之前。无论哪种方式,'memcpy'都会增加一些额外的开销,但是当然如您所说,如果'd'和's'事实上没有正确对齐,则使用强制转换的版本不起作用。 –
如果性能并不重要,你应该只使用memcpy()
作为另一个答案。
如果到*s
写入后不久出现这种代码,匹配类型;如果此代码在从*d
读取之前发生,请匹配类型。这将确保从存储到加载转发(将数据从存储直接转移到加载,而无需等待存储将数据写回到数据高速缓存中)可以在尽可能多的CPU上工作。如果存储和加载的地址和大小匹配并保持一致,则存储到加载转发几乎总是有效的,并且根据CPU可能会更频繁地运行。如果存储到加载转发失败,惩罚倾向于大约10个时钟周期。
如果您可以通过添加额外的移位/和/或操作避免储存至加载转送的问题,这往往要快。
如果您更有效地用C的类型系统,避免石膏,将可避免许多存储至加载转送的问题。
尝试铸造为(无符号long long *)
区别?在哪个意义上? – sidyll
@sidyll,如果我将其更改为无符号long,则代码会中断。 – YOU
@YOU:在什么平台上? – Mat