数字比较作为 uint 是否比 c# 中的普通 int 更快
Is numbers comparisons as uint faster than normal int in c#
我正在查看一些 .Net 核心库是如何实现的,引起我注意的许多事情之一是 Dictionary<TKey, TValue>
class 一些数字比较完成了对 (uint)
尽管在我天真的眼中这并没有影响逻辑。
例如
do { // some magic } while (collisionCount <= (uint)entries.Length);
collisionCount
在 0
处初始化并始终递增 (collisionCount++
),因此 entries
是一个数组,其长度也不会为负数 see source code
相对于
if ((uint)i >= (uint)entries.Length) { // some code }
其中 i
在某些情况下执行以下操作时可能会变成负值,see debug img
i = entry.next;
因此将其用作正数会改变程序流程(由于二进制补码)
查看 class 代码的摘录:
// Some code and black magic
uint hashCode = (uint)key.GetHashCode();
int i = GetBucket(hashCode);
Entry[]? entries = _entries;
uint collisionCount = 0;
if (typeof(TKey).IsValueType)
{
i--;
do
{
if ((uint)i >= (uint)entries.Length) // <--- Workflow impact
{
goto ReturnNotFound;
}
entry = ref entries[i];
if (entry.hashCode == hashCode && EqualityComparer<TKey>.Default.Equals(entry.key, key))
{
goto ReturnFound;
}
i = entry.next;
collisionCount++;
} while (collisionCount <= (uint)entries.Length);
}
// More cool stuffs
是否有任何性能提升或这是什么原因?
链接的词典源包含此评论;
// Should be a while loop https://github.com/dotnet/runtime/issues/9422
// Test in if to drop range check for following array access
if ((uint)i >= (uint)entries.Length)
{
goto ReturnNotFound;
}
entry = ref entries[i];
此处的uint
比较并不快,但有助于加快数组访问速度。链接的 github 问题讨论了 运行time 编译器的限制,以及该循环结构如何允许进一步优化。由于此 uint
比较已明确执行,因此编译器可以证明 0 <= i < entries.Length
。这允许编译器省略数组边界测试并抛出 IndexOutOfRangeException
,否则将需要
换句话说,在编写这段代码时,执行了性能分析。编译器不够聪明,无法尽可能快地生成更简单、更易读的代码,运行。因此,对编译器的局限性有深刻理解的人调整了代码以使其更好。
我正在查看一些 .Net 核心库是如何实现的,引起我注意的许多事情之一是 Dictionary<TKey, TValue>
class 一些数字比较完成了对 (uint)
尽管在我天真的眼中这并没有影响逻辑。
例如
do { // some magic } while (collisionCount <= (uint)entries.Length);
collisionCount
在 0
处初始化并始终递增 (collisionCount++
),因此 entries
是一个数组,其长度也不会为负数 see source code
相对于
if ((uint)i >= (uint)entries.Length) { // some code }
其中 i
在某些情况下执行以下操作时可能会变成负值,see debug img
i = entry.next;
因此将其用作正数会改变程序流程(由于二进制补码)
查看 class 代码的摘录:
// Some code and black magic
uint hashCode = (uint)key.GetHashCode();
int i = GetBucket(hashCode);
Entry[]? entries = _entries;
uint collisionCount = 0;
if (typeof(TKey).IsValueType)
{
i--;
do
{
if ((uint)i >= (uint)entries.Length) // <--- Workflow impact
{
goto ReturnNotFound;
}
entry = ref entries[i];
if (entry.hashCode == hashCode && EqualityComparer<TKey>.Default.Equals(entry.key, key))
{
goto ReturnFound;
}
i = entry.next;
collisionCount++;
} while (collisionCount <= (uint)entries.Length);
}
// More cool stuffs
是否有任何性能提升或这是什么原因?
链接的词典源包含此评论;
// Should be a while loop https://github.com/dotnet/runtime/issues/9422 // Test in if to drop range check for following array access if ((uint)i >= (uint)entries.Length) { goto ReturnNotFound; } entry = ref entries[i];
此处的uint
比较并不快,但有助于加快数组访问速度。链接的 github 问题讨论了 运行time 编译器的限制,以及该循环结构如何允许进一步优化。由于此 uint
比较已明确执行,因此编译器可以证明 0 <= i < entries.Length
。这允许编译器省略数组边界测试并抛出 IndexOutOfRangeException
,否则将需要
换句话说,在编写这段代码时,执行了性能分析。编译器不够聪明,无法尽可能快地生成更简单、更易读的代码,运行。因此,对编译器的局限性有深刻理解的人调整了代码以使其更好。