BinaryReader.ReadUInt64() 的效率
Efficiency in BinaryReader.ReadUInt64()
当我在 .NET BinaryReader 中遇到这段代码时,我只是想优化一些代码 class:
[CLSCompliant(false)]
[__DynamicallyInvokable]
public virtual ulong ReadUInt64()
{
this.FillBuffer(8);
return (ulong) (uint) ((int) this.m_buffer[4] | (int) this.m_buffer[5] << 8 | (int) this.m_buffer[6] << 16 | (int) this.m_buffer[7] << 24) << 32 | (ulong) (uint) ((int) this.m_buffer[0] | (int) this.m_buffer[1] << 8 | (int) this.m_buffer[2] << 16 | (int) this.m_buffer[3] << 24);
}
我想知道为什么 ulong 被分成两部分移动然后合并,而不是只移动 0,8,16,..,56。
我还想了解这里使用的多重转换。
编辑:当我在安装了 ReSharper 的 VS2013 中键入此内容时,它显示如下:
如您所见,第二个ulong变暗了,这意味着它甚至没有被使用。似乎缺少一对括号。这样对吗?
首先,让我们以更易读的形式重写:
(ulong)(uint)(
(int) this.m_buffer[4] |
(int) this.m_buffer[5] << 8 |
(int) this.m_buffer[6] << 16 |
(int) this.m_buffer[7] << 24)
<< 32 |
(ulong)(uint)(
(int) this.m_buffer[0] |
(int) this.m_buffer[1] << 8 |
(int) this.m_buffer[2] << 16 |
(int) this.m_buffer[3] << 24)
现在很清楚这是做什么了。
它将低 32 位和高 32 位组合成两个单独的 uint
值(通过将字节转换为 int
后移动字节),然后转换两个结果 int
ulong
的值,将高 32 位移动 32,最后将它们组合在一起得到结果。
之所以这样做,是因为移动 32 位值比移动 64 位值更快。如果代码将所有字节转换为 ulong
然后移动它们,它会更慢。
另请注意,它组合字节的顺序适用于 "little endian" 格式。
唯一明显奇怪的是第一个演员 (ulong)(uint)
,可能只是 (ulong)
。如果你删除所有不必要的转换,它看起来像这样:
(ulong)(
buffer[4] |
buffer[5] << 8 |
buffer[6] << 16 |
buffer[7] << 24)
<< 32 |
(uint)(
buffer[0] |
buffer[1] << 8 |
buffer[2] << 16 |
buffer[3] << 24);
现在您可能想知道为什么不能只转换为 ulong
而不是 uint
。
这个问题的答案如下。假设代码是这样的:
(ulong)(
buffer[4] |
buffer[5] << 8 |
buffer[6] << 16 |
buffer[7] << 24)
<< 32 |
(ulong)( // This seems more natural...
buffer[0] |
buffer[1] << 8 |
buffer[2] << 16 |
buffer[3] << 24);
表达式的第一部分使用 ulong.operator<<()
转换为 (ulong)
。这个returns一个(ulong)
.
但是表达式的第二部分没有移位,当它与第一个表达式进行 OR 运算时会提示编译器警告:
警告 CS0675:按位或运算符用于符号扩展操作数;首先考虑转换为较小的无符号类型
Eric Lippert 有 a blog about this warning 你应该读一读。
无论如何,因为在第一部分转换为 (ulong)
和第二部分转换为 (uint)
时看起来很奇怪,我相当确定他们放入了完整的显式转换以防止混淆reader.
的部分
当我在 .NET BinaryReader 中遇到这段代码时,我只是想优化一些代码 class:
[CLSCompliant(false)]
[__DynamicallyInvokable]
public virtual ulong ReadUInt64()
{
this.FillBuffer(8);
return (ulong) (uint) ((int) this.m_buffer[4] | (int) this.m_buffer[5] << 8 | (int) this.m_buffer[6] << 16 | (int) this.m_buffer[7] << 24) << 32 | (ulong) (uint) ((int) this.m_buffer[0] | (int) this.m_buffer[1] << 8 | (int) this.m_buffer[2] << 16 | (int) this.m_buffer[3] << 24);
}
我想知道为什么 ulong 被分成两部分移动然后合并,而不是只移动 0,8,16,..,56。
我还想了解这里使用的多重转换。
编辑:当我在安装了 ReSharper 的 VS2013 中键入此内容时,它显示如下:
如您所见,第二个ulong变暗了,这意味着它甚至没有被使用。似乎缺少一对括号。这样对吗?
首先,让我们以更易读的形式重写:
(ulong)(uint)(
(int) this.m_buffer[4] |
(int) this.m_buffer[5] << 8 |
(int) this.m_buffer[6] << 16 |
(int) this.m_buffer[7] << 24)
<< 32 |
(ulong)(uint)(
(int) this.m_buffer[0] |
(int) this.m_buffer[1] << 8 |
(int) this.m_buffer[2] << 16 |
(int) this.m_buffer[3] << 24)
现在很清楚这是做什么了。
它将低 32 位和高 32 位组合成两个单独的 uint
值(通过将字节转换为 int
后移动字节),然后转换两个结果 int
ulong
的值,将高 32 位移动 32,最后将它们组合在一起得到结果。
之所以这样做,是因为移动 32 位值比移动 64 位值更快。如果代码将所有字节转换为 ulong
然后移动它们,它会更慢。
另请注意,它组合字节的顺序适用于 "little endian" 格式。
唯一明显奇怪的是第一个演员 (ulong)(uint)
,可能只是 (ulong)
。如果你删除所有不必要的转换,它看起来像这样:
(ulong)(
buffer[4] |
buffer[5] << 8 |
buffer[6] << 16 |
buffer[7] << 24)
<< 32 |
(uint)(
buffer[0] |
buffer[1] << 8 |
buffer[2] << 16 |
buffer[3] << 24);
现在您可能想知道为什么不能只转换为 ulong
而不是 uint
。
这个问题的答案如下。假设代码是这样的:
(ulong)(
buffer[4] |
buffer[5] << 8 |
buffer[6] << 16 |
buffer[7] << 24)
<< 32 |
(ulong)( // This seems more natural...
buffer[0] |
buffer[1] << 8 |
buffer[2] << 16 |
buffer[3] << 24);
表达式的第一部分使用 ulong.operator<<()
转换为 (ulong)
。这个returns一个(ulong)
.
但是表达式的第二部分没有移位,当它与第一个表达式进行 OR 运算时会提示编译器警告:
警告 CS0675:按位或运算符用于符号扩展操作数;首先考虑转换为较小的无符号类型
Eric Lippert 有 a blog about this warning 你应该读一读。
无论如何,因为在第一部分转换为 (ulong)
和第二部分转换为 (uint)
时看起来很奇怪,我相当确定他们放入了完整的显式转换以防止混淆reader.