22

目前,我正在使用针对 32 位 MIPS 平台的代码库(C、C++ 混合)。该处理器是一个相当现代的处理器[只是提到我们拥有大量的处理能力和内存]。

代码库使用 uint8[1 字节宽无符号整数]、uint16[2 字节宽无符号整数]、uint32[4 字节宽无符号整数]等数据类型。

我知道在将代码移植到不同平台时如何使用这些构造。

我的问题是:

  1. 使用 uint16 的用途/好处是什么,而 uint32 也足够了(如果有的话)?

  2. 使用较短的数据类型(考虑数据对齐)是否会节省内存使用?

  3. 如果是为了节省几个字节的内存,在现代硬件中这样做是否明智?

4

9 回答 9

23

使用 uint16 的用途/好处是什么,而 uint32 也足够了(如果有的话)?

如果这些uint16s是数组或结构的一部分,则可以节省内存,并且可能能够处理比uint32s相同数组或结构更大的数据集。这真的取决于你的代码。

可能会使用数据协议和文件格式,但使用uint16s它可能不正确uint32s。这取决于格式和语义(例如,如果您需要将值从 65535 环绕到 0,uint16则会自动执行此操作,而uint32不会自动执行此操作)。

OTOH,如果这些uint16s只是单个局部或全局变量,用 32 位变量替换它们可能没有显着差异,因为它们可能由于对齐而占用相同的空间,并且它们作为 32 位参数传递(在堆栈或在寄存器中)无论如何都在 MIPS 上。

使用较短的数据类型(考虑数据对齐)是否会节省内存使用?

可能会有所节省,尤其是当uint16s它们是许多结构的一部分或大数组的元素时。

如果是为了节省几个字节的内存,在现代硬件中这样做是否明智?

是的,您降低了内存带宽(这总是一件好事),并且当您操作较少的数据时,您通常会降低各种缓存未命中(数据缓存和 TLB)。

于 2013-02-25T08:22:46.147 回答
9

首先,如果您定义了诸如 uint16 之类的类型,它们是在哪里定义的?它们不是标准类型,因此将在某些专有标头中定义-可能是您的标头,也可能由某些第三方库提供;在这种情况下,您必须问自己该代码的可移植性如何,以及您是否正在创建在其他应用程序中可能没有意义的依赖项。

另一个问题是,许多库(不明智的 IMO)定义了具有各种名称的此类类型,例如 UINT16、uint16、U16 UI16 等,这在确保类型一致性和避免名称冲突方面变得有些噩梦。如果定义了此类名称,理想情况下应将它们放置在命名空间中或赋予库特定前缀以指示它们被定义为与哪个库一起使用,例如rtos::uint16to rtos_uint16.

由于 ISO C99 标准库在 stdint.h 中提供了标准位长度特定类型,因此您应该优先使用它们而不是专有或第三方头文件中定义的任何类型。这些类型有一个_t后缀,例如uint16_t. 在 C++ 中,它们可能被放置在std::命名空间中(尽管这不是给定的,因为在 C99 中引入了标头)。

1] 使用 uint16 的用途/好处是什么,而 uint32 也足够(如果有的话)?

除了我之前对 prefer stdint.h's 的建议之外uint16_t,使用长度特定类型至少有两个正当理由:

  1. 匹配特定的硬件寄存器宽度。
  2. 跨不同架构强制执行通用且兼容的 API。

2] 使用较短的数据类型(考虑数据对齐)会节省内存使用吗?

可能,但如果内存不是您的问题,那不是使用它们的好理由。对于大型数据对象或数组可能值得考虑,但全局应用很少值得付出努力。

3]如果是为了节省几个字节的内存,在现代硬件中这样做是否明智?

见[2]。然而, “现代硬件”并不一定意味着大量资源;例如,有很多只有几 Kb RAM 的 32 位 ARM Cortex-M 设备。这更多地与芯片空间、成本和功耗有关,而不是与设计或架构的时代有关。

于 2013-02-25T10:03:36.673 回答
4

cstdint有很多typedefs 用于不同的目的。

  • intN_t对于特定宽度
  • int_fastN_t对于最快的整数,它至少有 N 位
  • int_leastN_t对于最小的整数,它至少有 N 位
  • 他们的unsigned等价物

你应该根据你的情况来选择。在 a 中存储数千std::vector而不进行大量计算?intN_t可能是你的男人。需要对少量整数进行快速计算?int_fastN_t可能是你的家伙。

于 2013-02-25T08:16:30.953 回答
2

答。1.软件有一定的要求和规范,严格要求在编码/解码或其他某些用途时只取8/16位参数。因此,即使您将大于 127 的值分配到 u8 中,它也会自动为您修剪数据。

答。2. 我们不应该忘记,我们的编译器在优化方面远远超出智能,无论是内存还是复杂性。因此,始终建议尽可能使用较小的内存。

答。3. 当然,在现代硬件上节省内存是有意义的。

于 2013-02-25T08:03:27.813 回答
2

必须检查生成的机器代码/汇编程序以验证是否有任何代码保存。在 RISC 类型架构中,典型的立即数是 16 位的,但使用 uint16_t 无论如何都会消耗一个完整的 32 位寄存器——因此,如果使用 int 类型,但承诺使用接近零的值将产生相同的结果并且更便携。

IMO 节省内存在现代平台上也是值得的。更严格的代码会带来更好的电池寿命和更流畅的用户体验。但是,我建议仅在使用(大)数组或变量映射到某些实际硬件资源时对大小进行微观管理。

附言。编译器很聪明,但编写它们的人目前正在使它们变得更好。

于 2013-02-25T08:23:45.113 回答
1

使用uint16_t而不是uint32_t节省内存。它也可能是硬件限制(例如,某些外围控制器确实发送 16 位!)但是,由于缓存和对齐考虑(您确实必须进行基准测试),它可能不值得使用它。

于 2013-02-25T07:58:36.890 回答
1

使用 uint16 的用途/好处是什么,而 uint32 也足够了(如果有的话)?

unsigned char16 位值的 CPU。如果不使用 typedef(uint16 只是适当类型的 typedef),则对此类代码进行单元测试会很困难。

此外,通过使用这些 typedef,可以更轻松地在不同平台上构建,而不会出现很多问题。

使用较短的数据类型(考虑数据对齐)是否会节省内存使用?

不,这不是重点。如果uint16是 typedef unsigned short,那么你可以unsigned short在任何地方使用,但你可能会在不同的平台上得到不同的类型。

当然,使用更小的类型会减少内存消耗。例如,使用 uint16 而不是 uint32,但前提是您使用数组。

如果是为了节省几个字节的内存,在现代硬件中这样做是否明智?

这取决于平台:

  • 更少的内存使用意味着更少的缓存未命中
  • 如果支持,有处理 16 位数据的 SIMD 函数
于 2013-02-25T08:01:34.690 回答
1

您的问题的答案归结为一个关键概念:数据有多大?如果您要处理大量数据,那么使用较小数据类型的好处是显而易见的。可以这样想:简单地计算新发现的最大已知素数可能会使您在典型工作站上耗尽内存。该数字本身需要超过千兆字节才能存储。这不包括计算实际数字。如果您要使用粗数据类型而不是细数据类型,那么您可能会看到 2 GB。一个简单的例子,但仍然是一个很好的例子。

于 2013-02-25T08:02:38.687 回答
1

使用精确宽度的整数类型(如int32_t和朋友)有助于避免在int和具有不同大小的平台之间出现符号扩展错误long。例如,这些可能在应用位掩码或位移位时发生。例如,如果您在 a 上执行这些操作long,并且您的代码适用于 32-bit long,则它可能会中断 64-bit long。另一方面,如果您使用 a uint32_t,则无论平台如何,您都确切知道会得到什么结果。

它们对于在平台之间交换二进制数据也很有用,您只需要担心存储数据的字节顺序而不是位宽;如果你写int64_t一个文件,你知道在另一个平台上你可以读取它并将它存储到一个int64_t. 如果您在一个平台上写出 along而不是 64 位,那么您可能最终需要long long在另一个平台上使用 a,因为long只有 32 位。

节省内存通常不是原因,除非您谈论的是非常有限的环境(嵌入的东西)或大型数据集(例如具有 5000 万个元素的数组等)。

于 2013-02-25T08:07:47.413 回答