【问题标题】:How much memory use byte enumerations and does this optimize memory/speed in C #?多少内存使用字节枚举,这是否优化了 C# 中的内存/速度?
【发布时间】:2021-04-09 07:35:00
【问题描述】:

在 C# 中将字节枚举用于小型枚举是一种好处还是一种好的做法?

这会减少内存使用吗?

这会提高速度吗?

例子:

enum Fruits : byte { Apple, Orange, Banana }

与此相比有什么优势:

enum Fruits { Apple, Orange, Banana }

提前致谢。

【问题讨论】:

  • 对不起,这些都是 cmets 中的非问题。旨在通过让@riera 感觉“小于”来让您感觉更好。 OP 显然是新的,为什么不通过帮助来鼓励他们,而不是像上帝一样从头上掉下来。
  • @OlivierRogier 也许您可以重新提出问题,因为显然它不是基于意见的。您只是提供了一个明确的答案,我没有看到任何意见。
  • @Riera 我想为我同事的行为道歉。在这个行业非常猖獗。不要让它阻止你......
  • @OlivierRogier 你写了我需要的答案。我不明白你为什么关闭这个问题。
  • 我投票决定重新开放,以便@OlivierRogier 可以发布他的答案。

标签: c# performance memory enums


【解决方案1】:

使用 Visual Studio 机器代码窗口进行调查

对于byte enumint enum,我们可以看到在Windows x64 上的每种情况下内存都与4 bytes 对齐,即使使用Flags 属性也是如此。但是long enum 消耗8 bytes 每个值。

因此没有必要使用字节枚举,因为它消耗相同的内存并且速度相同,并且使用字节对齐应该只在 8 位机器上有用。

说明

这是因为 .NET 在 x32 和 x64 Intel/AMD 上使用 32 位寄存器来存储整数,即使是 AnyCPU 或 x64 目标也是如此。

.NET 编译器不是针对内存进行优化,而是针对速度进行优化,因为处理器内部总线大小在 x32 CPU 上优化为 (16/)32 位或在 x64 CPU 上优化为 (32/)64 位。所以 32 位访问是 x32 上的最佳速度,而 64 位是 x64 上最好的。任何其他尺寸“强制”处理器缩小/放大尺寸。我写了 (16/) 和 (32/) 是因为这些访问被优化为比其他访问更慢。

这是因为 .NET 编译器更注重速度而不是内存,特别是因为它是虚拟机,因此比本机代码慢。出于兼容性原因,它偏爱 32 位。实际上,32 位寄存器和内存访问在 x32/x64 机器上得到了更多优化,而较低的对齐方式会降低性能(除非我们有 8 位或 16 位处理器)。只有内存指针在 x32 上为 32,如果编译 AnyCPU 或针对 x64,则在 x64 上为 64。

所以现在任何小于 32 位的数据都应该对齐到 32 位,未来可能是 64 位。

因此,比整数短的枚举以及结构和类中小于或等于 4 字节大小的整数值是 4 字节对齐的,因此我们在 x32 或 x64 下的 .NET 机器上每个字节丢失 3 个字节,除非我们修改struct packing size(默认为 4 个字节)。

备注

结构或类实例上的marshal sizeof 给出了“已使用(非托管)数据大小”,但不是真正的保留内存,包括用于存储数据的丢失块。

示例

enum FruitsByte : byte { Apple, Orange, Banana }
enum FruitsInt { Apple, Orange, Banana }
enum FruitsLong : long { Apple, Orange, Banana }

对于字节

var fb1 = FruitsByte.Apple;
mov dword ptr [rbp+0A4h],ecx  

var fb2 = FruitsByte.Orange;
mov dword ptr [rbp+0A0h],1  

0xA4 - 0xA0 = 4 bytes

对于整数

var fi1 = FruitsInt.Apple;
mov dword ptr [rbp+9Ch],ecx  

var fi2 = FruitsInt.Orange;
mov dword ptr [rbp+98h],1  

0x9C - 0x98 = 4 bytes

长久

var fl1 = FruitsLong.Apple;
mov qword ptr [rbp+90h],rcx  

var fl2 = FruitsLong.Orange;
mov qword ptr [rbp+88h],rcx  

0x90 - 0x88 = 8 bytes

完整机器码

      var fb1 = FruitsByte.Apple;
00007FF7E93C0B19  xor         ecx,ecx  
00007FF7E93C0B1B  mov         dword ptr [rbp+0A4h],ecx  
      var fb2 = FruitsByte.Orange;
00007FF7E93C0B21  mov         dword ptr [rbp+0A0h],1  

      var fi1 = FruitsInt.Apple;
00007FF7E93C0B2B  mov         dword ptr [rbp+9Ch],ecx  
      var fi2 = FruitsInt.Orange;
00007FF7E93C0B31  mov         dword ptr [rbp+98h],1  

      var fl1 = FruitsLong.Apple;
00007FF7E93C0B3B  movsxd      rcx,ecx  
00007FF7E93C0B3E  mov         qword ptr [rbp+90h],rcx  
      var fl2 = FruitsLong.Orange;
00007FF7E93C0B45  mov         ecx,1  
00007FF7E93C0B4A  movsxd      rcx,ecx  
00007FF7E93C0B4D  mov         qword ptr [rbp+88h],rcx  

更多信息

Data structure alignment (Wikipedia)

x64 Architecture - CPU registers (MSDoc)

8086 to i486 Instructions Set

Intel® 64 and IA-32 Architectures Software Developer Manuals

【讨论】:

  • 比较大小 28 here 两种字节大小与 int 大小的枚举类型的结构。
  • 字节寻址同样有效,与对齐无关。而SizeOf 给出了实际的内存占用——例如在前面每个结构的末尾添加一个 int,大小将更改为 812not @987654345 @ 与 12,因为 int 对齐)。这在 Microsoft docs 中都有描述。因此,使用比需要更宽的枚举会浪费结构中的内存,而且“它消耗相同的内存”并不总是正确的。
  • "因此没有必要使用字节枚举,因为它消耗相同的内存" 这部分答案的问题是它被写成一个普遍的事实,它不是。
  • @divx 感谢您帮助我用更多细节改进答案,但您似乎错过了问题的重点以及我接触的微处理器技术以及编组。
猜你喜欢
  • 2010-09-13
  • 2021-08-30
  • 1970-01-01
  • 2022-11-10
  • 1970-01-01
  • 1970-01-01
  • 2014-05-11
  • 2012-01-28
  • 2018-05-02
相关资源
最近更新 更多