我的目标不是开始微观优化,所以如果这就是这个问题,我很乐意放弃这个问题。但我即将开始做出一些设计决定,并希望获得更多信息。
我正在阅读和处理一种文件格式,其中包含以明确定义的格式记录的大量数据结构。我在代码中将它们表示为结构。
现在,如果我将结构与#pragma pack(1)
打包成一个1字节的对齐,我可以直接读取IO流上的结构到struct指针。这很方便。如果我不打包结构,我可以逐个fread
字段或一次fread
个块,逐个reinterpret_cast
结构字段,这可能是快点老了。
作为参考,结构将由数千个读取(可能),并且可以对它们进行一些数字运算。它们主要由无符号16位整数(约60%),无符号32位整数(约30%)和一些64位整数组成。
所以现在的问题是,我......
fread
?答案 0 :(得分:4)
最终,解决方案A和解决方案B之间的性能差异只能通过基准测试来确定。在互联网上询问会给你可变的结果,可能会或可能不会反映你的情况下的现实。
当您“错位”数据时会发生什么,处理器需要对一个数据执行多次读取[同样适用于写入]。确切地说,需要多少额外时间取决于处理器 - 某些处理器不会自动执行,因此运行时系统将捕获“错误读取”并在某些仿真层中执行读取[或者,在某些处理器中,只是将其杀死“未对齐的内存访问”的过程]。显然,采用陷阱并进行一些读取操作然后返回调用代码对性能有相当大的影响 - 它可能比对齐读取操作更容易花费数百个周期。
在x86的情况下,它“就像你期望的那样工作”,但通常会额外增加1个时钟周期(假设数据已经在L1缓存中)。在现代处理器中,一个时钟周期不是很多,但如果循环长度为10000000000000次并且读取未对齐数据n次,则现在已经将n * 10000000000000个时钟周期添加到执行时间,这可能很重要。
其他替代方案也会对性能产生影响。做大量的小读取可能比做一次大读取慢很多。从性能角度来看,转换功能可能更好。
再一次,请不要把它作为“给定”,你真的需要比较不同的解决方案(或者选择一个,如果性能不吸,并且代码看起来不太可怕,请保留它在那)。我相信你能找到你认为“最好”的三种解决方案中的每一种解决方案。
另外请记住,#pragma pack
是特定于编译器的,并且要实现允许您在“Microsoft”和“gcc”解决方案之间进行选择的宏并不容易。编辑:似乎更新的gcc版本支持此选项 - 但不是所有编译器都支持。
答案 1 :(得分:2)
根据您对另一个答案的评论,您的代码旨在与平台无关,并且明确指定了文件格式的字节序。在这种情况下,直接读取打包的struct
会失去很大的清晰度,因为它需要一个读取后的endian-cleanup步骤,否则会导致与文件格式不同的endian-ness架构上的数据不正确。 / p>
假设您总是知道字节数(可能来自文件中的结构类型指示符),我建议使用工厂模式,其中创建的对象的构造函数知道如何从属性中拉出内存缓冲区属性的字节(如果文件足够小你可以把整个东西读成一个缓冲区而不是循环/ factory-create / deserialize-into-object-via-constructor。这样你就可以控制endian-ness并允许编译器所需的struct对准。
答案 2 :(得分:1)
如果您只是使用打包并直接读取结构,代码将是最清晰的。这也可能是最快阅读。不幸的是,它也可能是错误的来源,特别是如果结构的布局在未来发生变化。
元素的对齐可能是一个问题,也可能不是,这取决于许多因素。如果元素按大小排序,首先是最大元素,那么对齐可能不是问题。如果源通过直接写入整个结构来生成字节流,那么它也可能正确地对齐该系统,并且可能在您的结束时完美地工作。 x86架构可以很好地处理错位,只有轻微的减速最坏情况;即使是由缓存结构最小化,其中一次加载整个缓存行,保证大部分字节已经在缓存中。其他架构可能根本无法处理错位,但如果发生这种情况,你会很快知道。
如果您需要与源不同的字节序,则可以在结构的每个元素上调用一个函数来单独修复它们。在这一点上,直接读取的简单性和清晰度将会降低,而使用其他方法可能会更好。