我有一个大的数据块,如果块被视为64位无符号整数的数组,其他一些操作将是最快的,而如果被视为32位无符号整数的数组,则其他操作将是最快的. “最快”,我的意思是运行代码的机器的平均速度最快.我的目标是在运行代码的所有环境中接近最优,我认为如果我使用void指针将其转换为两种类型之一以进行解除引用,这是可能的.这让我想到了我的问题:
1)如果我使用void指针,将它转换为两种类型中的一种用于解除引用要比直接使用所需类型的指针慢?
2)我对标准的理解是否正确,这样做不会违反抗锯齿规则,并且不会产生任何未定义或未指定的行为?我使用的32位和64位类型存在并且没有填充(这是一个静态断言).
3)我是否正确理解抗锯齿规则基本上有两个目的:类型安全和编译器保证以实现优化?如果是这样,如果我正在讨论的代码将被执行的所有情况都没有发生其他解引用,那么我是否可能在任何重要的编译器优化上松懈?
我用’c11’标记了这个,因为我需要从c11标准证明行为是明确定义的.任何对标准的引用都将不胜感激.
最后,我想谈谈在回答中提出的关于“过早优化”的可能问题.首先,这个代码是在一个多样化的计算集群上运行的,性能是至关重要的,而且我知道即使一个指令在解除引用时减速也会很重要.其次,在所有硬件上进行测试需要时间,我不需要完成项目.有许多不同类型的硬件,我现场有限的时间来实际使用硬件.但是,我相信这个问题的答案将使我能够做出正确的设计选择.
编辑:答案和评论指出这种方法存在别名问题,我直接在c11标准中验证.一组联合需要在32位情况下进行两次地址计算和解除引用,所以我更喜欢数组联合.然后问题变成:
1)使用union成员作为数组而不是指向内存的指针是否存在性能问题?即,工会会员访问是否有成本?请注意,声明指向数组的两个指针违反了抗锯齿规则,因此需要直接通过联合进行访问.
2)当通过一个数组然后通过另一个数组访问时,数组的内容是否保证不变?
最佳答案 您的问题有不同的方面.首先,解释具有不同类型的内存有几个问题:
>别名
>对齐
>填充
别名是一个“本地”问题.在函数内部,您不希望指向具有不同目标类型的同一对象.如果您确实修改了这样的指向对象,编译器可能会假装不知道该对象可能已经更改并错误地优化您的程序.如果你没有在函数内部执行此操作(例如,在开头执行转换并继续使用该解释),则应该可以使用别名.
现在,对齐问题经常被忽视,因为许多处理器现在对对齐问题非常宽容,但这不是便携式的,也可能会对性能产生影响.因此,您必须确保您的阵列以适合您访问它的所有类型的方式对齐.这可以通过C11中的_Alignas来完成,较旧的编译器也具有允许这样做的扩展. C11对aligment增加了一些限制,例如,它总是2的幂,这使你能够编写关于这个问题的可移植代码.
整数类型填充是现在很少见的(只有例外是_Bool)但是要确保你应该使用已知不会出现问题的类型.在您的情况下,这些是[u] int32_t和[u] int64_t,已知它们具有所请求的位数,并且具有签名类型的二进制补码表示.如果平台不支持它们,您的程序将无法编译.