很难以相信构造p[u+1]
发生在代码的最内层循环中的几个地方,我保持这样,在正确运行数天的操作中,对其进行微观优化会产生数小时的差异.
通常*((p+u)+1)
是最有效的.有时候*(p+(u+1))
效率最高.很少*((p+1)+u)
是最好的.(但通常优化器可以转换*((p+1)+u)
为*((p+u)+1)
后者更好,并且不能*(p+(u+1))
与其他任何一个转换).
p
是一个指针,u
是一个无符号的.在实际代码中,它们中的至少一个(更可能两者)在表达式被评估的点处已经在寄存器中.这些事实对我的问题至关重要.
在32位(在我的项目放弃支持之前)中,所有三个都具有完全相同的语义,并且任何一半不错的编译器只选择三者中最好的并且程序员永远不需要关心.
在这些64位用法中,程序员知道这三者具有相同的语义,但编译器不知道.就编译器所知,何时u
从32位扩展到64位的决定会影响结果.
什么是最简洁的方法告诉编译器这三个语义是相同的,编译器应该选择最快的?
在一个Linux 64位编译器中,我几乎在那里p[u+1L]
使编译器在通常最好*((p+u)+1)
和有时更好之间智能地选择*(p+( (long)(u) + 1) )
.在极少数情况下*(p+(u+1))
仍然比第二种更好,有点丢失.
显然,这在64位Windows中没有用.既然我们已经放弃了32位支持,那么可能p[u+1LL]
足够便携且足够好.但我能做得更好吗?
请注意,使用std::size_t
而不是unsigned
for u
将消除整个问题,但会在附近产生更大的性能问题.铸造u
到std::size_t
那里几乎是足够好,也许我能做到的最好.但对于一个不完美的解决方案来说,这是非常冗长的.
简单编码(p+1)[u]
使选择更可能是最佳选择p[u+1]
.如果代码的模板化程度较低且更稳定,我可以将它们全部设置为(p+1)[u]
然后进行配置文件,然后再将其切换回p[u+1]
.但是模板化往往会破坏这种方法(在配置文件的许多地方出现单一的源代码行,导致严重的时间,但不是单独的严重时间).
对此应该有效的编译器是GCC,ICC和MSVC.