- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
根据 http://www.codeguru.com/forum/showthread.php?t=463663 ,C#在3.5中的getHashCode
函数实现为:
public override unsafe int GetHashCode()
{
fixed (char* str = ((char*) this))
{
char* chPtr = str;
int num = 0x15051505;
int num2 = num;
int* numPtr = (int*) chPtr;
for (int i = this.Length; i > 0; i -= 4)
{
num = (((num << 5) + num) + (num >> 0x1b)) ^ numPtr[0];
if (i <= 2)
{
break;
}
num2 = (((num2 << 5) + num2) + (num2 >> 0x1b)) ^ numPtr[1];
numPtr += 2;
}
return (num + (num2 * 0x5d588b65));
}
}
我很好奇是否有人能想出一个返回相同结果但速度更快的函数。增加主应用程序的整体启动和资源开销是可以的。要求一次性初始化(每次应用程序执行,而不是每次调用或每个字符串)是可以的。
请注意,与 Microsoft 不同的是,考虑因素包括“以这种方式进行操作会使其他一切变慢,而且成本会使这种方法变得愚蠢!”可以忽略,因此即使假设 Microsoft 是完美的,也可以通过做一些“愚蠢的事情”来打败它。
这纯粹是出于好奇,不会在实际代码中使用。
我想到的想法示例:
最佳答案
使函数执行得更快的一种方法是将特殊情况考虑在内。具有可变大小输入的函数具有基于大小的特殊情况。
只有当并行的成本降低时,并行才有意义小于增益,对于这种计算,很可能字符串必须相当大才能克服成本 fork 一个并行线程。但是实现起来并不难;基本上你需要对此进行测试。长度超过经验确定阈值,然后 fork 多个线程来计算对子字符串进行散列,最后一步将子散列组合成最后的哈希。实现留给读者。
现代处理器也有SIMD指令,可以处理一条指令中的 32(或 64)字节。这会让你以一二处理 32(16 位字符) block 中的字符串每个 block 的 SIMD 指令;然后将 64 字节的结果折叠成最后一个哈希码。这可能非常快对于任何合理大小的字符串。这个的实现来自 C# 更难,因为人们不期望虚拟机提供对 SIMD 指令的简单(或便携)访问你需要的。实现也留给了读者。编辑:另一个答案表明 Mono 系统确实提供SIMD 指令访问。
话虽如此,展示的特定实现非常愚蠢。关键观察是循环在每次迭代中检查两次限制。可以通过提前检查结束条件案例来解决这个问题,并执行一个执行正确迭代次数的循环。通过使用可以做得更好 Duffs device跳入 N 次迭代的展开循环。这摆脱了N-1 次迭代的循环限制检查开销。那个修改将非常容易,并且肯定值得努力实现。
编辑:您还可以结合 SIMD 思想和循环展开思想,以便在一些 SIMD 指令中处理许多 8/16 字符 block 。
对于不能跳入循环的语言,可以做等同于Duff 的设备通过简单地剥掉最初的外壳。射门如何使用循环剥离方法重新编码原始代码如下:
public override unsafe int GetHashCode()
{
fixed (char* str = ((char*) this))
{
const int N=3; // a power of two controlling number of loop iterations
char* chPtr = str;
int num = 0x15051505;
int num2 = num;
int* numPtr = (int*) chPtr;
count = this.length;
unrolled_iterations = count >> (N+1); // could be 0 and that's OK
for (int i = unrolled_iterations; i > 0; i--)
{
// repeat 2**N times
{ num = (((num << 5) + num) + (num >> 0x1b)) ^ numPtr[0];
num2 = (((num2 << 5) + num2) + (num2 >> 0x1b)) ^ numPtr[1]; }
{ num = (((num << 5) + num) + (num >> 0x1b)) ^ numPtr[2];
num2 = (((num2 << 5) + num2) + (num2 >> 0x1b)) ^ numPtr[3]; }
{ num = (((num << 5) + num) + (num >> 0x1b)) ^ numPtr[4];
num2 = (((num2 << 5) + num2) + (num2 >> 0x1b)) ^ numPtr[5]; }
{ num = (((num << 5) + num) + (num >> 0x1b)) ^ numPtr[6];
num2 = (((num2 << 5) + num2) + (num2 >> 0x1b)) ^ numPtr[7]; }
{ num = (((num << 5) + num) + (num >> 0x1b)) ^ numPtr[8];
num2 = (((num2 << 5) + num2) + (num2 >> 0x1b)) ^ numPtr[9]; }
{ num = (((num << 5) + num) + (num >> 0x1b)) ^ numPtr[10];
num2 = (((num2 << 5) + num2) + (num2 >> 0x1b)) ^ numPtr[11]; }
{ num = (((num << 5) + num) + (num >> 0x1b)) ^ numPtr[12];
num2 = (((num2 << 5) + num2) + (num2 >> 0x1b)) ^ numPtr[13]; }
{ num = (((num << 5) + num) + (num >> 0x1b)) ^ numPtr[14];
num2 = (((num2 << 5) + num2) + (num2 >> 0x1b)) ^ numPtr[15]; }
numPtr += 16;
}
if (count & ((1<<N)-1))
{
{ num = (((num << 5) + num) + (num >> 0x1b)) ^ numPtr[0];
num2 = (((num2 << 5) + num2) + (num2 >> 0x1b)) ^ numPtr[1]; }
{ num = (((num << 5) + num) + (num >> 0x1b)) ^ numPtr[2];
num2 = (((num2 << 5) + num2) + (num2 >> 0x1b)) ^ numPtr[3]; }
{ num = (((num << 5) + num) + (num >> 0x1b)) ^ numPtr[4];
num2 = (((num2 << 5) + num2) + (num2 >> 0x1b)) ^ numPtr[5]; }
{ num = (((num << 5) + num) + (num >> 0x1b)) ^ numPtr[6];
num2 = (((num2 << 5) + num2) + (num2 >> 0x1b)) ^ numPtr[7]; }
numPtr += 8;
}
if (count & ((1<<(N-1))-1))
{
{ num = (((num << 5) + num) + (num >> 0x1b)) ^ numPtr[0];
num2 = (((num2 << 5) + num2) + (num2 >> 0x1b)) ^ numPtr[1]; }
{ num = (((num << 5) + num) + (num >> 0x1b)) ^ numPtr[2];
num2 = (((num2 << 5) + num2) + (num2 >> 0x1b)) ^ numPtr[3]; }
numPtr += 4;
}
if (count & ((1<<(N-2)-1))
{
{ num = (((num << 5) + num) + (num >> 0x1b)) ^ numPtr[0];
num2 = (((num2 << 5) + num2) + (num2 >> 0x1b)) ^ numPtr[1]; }
numPtr += 2;
}
// repeat N times and finally:
if { count & (1) }
{
{ num = (((num << 5) + num) + (num >> 0x1b)) ^ numPtr[0];
// numPtr += 1;
}
return (num + (num2 * 0x5d588b65));
}
}
我没有编译或测试这段代码,但思路是对的。这取决于编译器进行合理的常量折叠和地址算法。
我试图对此进行编码以保留原始的确切哈希值,但恕我直言,这并不是真正的要求。如果它不使用,它会更简单,更快一点num/num2 特技,但只是为每个字符更新了 num。
作为静态函数的更正版本(由 Brian 编写):
public static unsafe int GetHashCodeIra(string x)
{
fixed (char* str = x.ToCharArray())
{
const int N = 2; // a power of two controlling number of loop iterations
char* chPtr = str;
int num = 0x15051505;
int num2 = num;
int* numPtr = (int*)chPtr;
int count = (x.Length+1) / 2;
int unrolled_iterations = count >> (N+1); // could be 0 and that's OK
for (int i = unrolled_iterations; i > 0; i--)
{ // repeat 2**N times
{
num = (((num << 5) + num) + (num >> 0x1b)) ^ numPtr[0];
num2 = (((num2 << 5) + num2) + (num2 >> 0x1b)) ^ numPtr[1];
}
{
num = (((num << 5) + num) + (num >> 0x1b)) ^ numPtr[2];
num2 = (((num2 << 5) + num2) + (num2 >> 0x1b)) ^ numPtr[3];
}
{
num = (((num << 5) + num) + (num >> 0x1b)) ^ numPtr[4];
num2 = (((num2 << 5) + num2) + (num2 >> 0x1b)) ^ numPtr[5];
}
{
num = (((num << 5) + num) + (num >> 0x1b)) ^ numPtr[6];
num2 = (((num2 << 5) + num2) + (num2 >> 0x1b)) ^ numPtr[7];
}
numPtr += 8;
}
if (0 != (count & ((1 << N) )))
{
{
num = (((num << 5) + num) + (num >> 0x1b)) ^ numPtr[0];
num2 = (((num2 << 5) + num2) + (num2 >> 0x1b)) ^ numPtr[1];
}
{
num = (((num << 5) + num) + (num >> 0x1b)) ^ numPtr[2];
num2 = (((num2 << 5) + num2) + (num2 >> 0x1b)) ^ numPtr[3];
}
numPtr += 4;
}
if (0 != (count & ((1 << (N - 1) ))))
{
{
num = (((num << 5) + num) + (num >> 0x1b)) ^ numPtr[0];
num2 = (((num2 << 5) + num2) + (num2 >> 0x1b)) ^ numPtr[1];
}
numPtr += 2;
}
// repeat N times and finally:
if (1 == (count & 1))
{
{
num = (((num << 5) + num) + (num >> 0x1b)) ^ numPtr[0];
// numPtr += 1;
}
}
return (num + (num2 * 0x5d588b65));
}
}
关于c# - 更快的字符串 GetHashCode(例如使用多核或 GPU),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1650395/
我有一个实现 IEquatable<> 的类 A,使用它的字段(比如 A.b 和 A.c)来实现/覆盖 Equals() 并覆盖 GetHashCode(),并且一切正常,99% 的时间。 A 类是继
为什么会这样17m.GetHashCode() == 17d.GetHashCode()(m =十进制,d =两倍) 此外,如预期的那样17f.GetHashCode() != 17d.GetHash
我找到了一个 GetHashCode() 的实现,看起来像这样 Guid _hashCode = Guid.NewGuid(); public override int GetHash
我想使用 Distinct()使用我的数据,声明为 IEnumerable> .在这种情况下,我必须实现自己的 IEqualityComparer还有我的问题: 以下实现之间有什么区别吗? publi
我认为这些集合的 GetHashCode 函数不将它们的哈希码基于列表中的项目是很奇怪的。 我需要它来工作以提供脏检查(您有未保存的数据)。我已经编写了一个覆盖 GetHashCode 方法的包装类,
明明有更好的hash方法 有位朋友对我吐槽前几天我列举的在源生成器的生成db映射实体的优化点 提前生成部分 hashcode 进行比较 所示代码 public static void Gener
我正在为在 .net 上运行的语言编写编译器,我希望它做的一件事是自动生成 GetHashCode 方法,但我有几个问题: 这是否可能,编译器是否足够了解所涉及的类型以合理地实现该方法? 我应该为值类
昨天我浏览了一些 .net 源代码,看到了一些 GetHashcode 的实现,其中包含以下内容: (i1 << 5) + i ^ i2 我了解代码在做什么以及为什么。我想知道的是他们为什么使用 (i
对于 Delphi 项目(使用 RAD Studio XE7 构建),我想创建一个画笔字典。每个字典项都包含一个 TMyBrush 对象作为键,该对象描述要检索的画笔,以及一个 GDI+ 画笔作为值。
我有以下类(class): public class Foo { int year; string name; int category; } 这是一些示
我有这个问题。 public class Foo : object { public override bool Equals(obj a, objb) { return
有时我需要没有字段(消息头、模式等)的值对象,例如: abstract class RequestHeader { } sealed class FirstRequestHeader : Reques
我正在使用 Linq-to-Sql 查询 SQL Server 数据库。此查询返回我的数据库中实体的列表。我的基础数据没有改变。 收到列表后,我会对其调用 GetHashCode 以测试是否相等。奇怪
我在这里阅读了一些与 GetHashCode 正确实现相关的问题。我没有找到的是什么时候我应该实现这个方法。 在我的具体案例中,我构建了一个简单的不可变结构: public struct MyStru
我正在使用 unity,而 unity 中没有元组,所以我创建了自己的元组类来工作,因为我的字典需要它。 Dictionary , Tile> 我创建的 Tile 类与解决这个问题并不相关(至少我认为
我在 Microsoft 文档中找到了以下内容: Two objects that are equal return hash codes that are equal. However, the r
我试着关注 Guidelines来自 MSDN,也引用了 This great question但下面的行为似乎并不像预期的那样。 我试图表示类似于 FQN 的结构,其中就好像 P1 在 P2 之前列
我最近用几种不同的方式问过这个问题,但没有得到告诉我如何使用 的字典的答案。当我持有对更改的内容的引用时需要处理 T.GetHashCode() .出于此问题的目的,“状态”指的是在 Equals(
我有以下类(class) public class ResourceInfo { public string Id { get; set; } public string Url {
首先,我使用 GetHashCode算法描述,here .现在,想象以下(人为的)示例: class Foo { public Foo(int intValue, double doubleV
我是一名优秀的程序员,十分优秀!