散列函数:有没有办法进一步优化我的代码?

时间:2017-10-17 04:58:05

标签: c++ hash

以上是哈希函数。

我写了下面的代码。我不确定我是否可以使用另一种聪明的方法来提高效率。我使用的理解是我根本不需要执行mod,因为unsigned int通过溢出来处理它。

int myHash(string s)
{
    unsigned int hash = 0;
    long long int multiplier = 1;
    for(int i = s.size()-1;i>-1;i--)
    {
        hash += (multiplier * s[i]);
        multiplier *= 31;
    }
    return hash;
}

3 个答案:

答案 0 :(得分:1)

我猜你可以让参数不必复制函数调用的字符串,改为使用const string &s,或者如果碰巧使用C ++ 17则使用std::string_view。否则它看起来很快就应该将其余部分留给编译器了。尝试使用-O2或等效的编译器进行优化。

答案 1 :(得分:1)

我会避免使用long long作为乘数。至少如果你不知道100%你的处理器在32位乘法的相同时间内进行64位乘法运算。真正的现代顶级处理器可能做到了,老款和较小的处理器几乎肯定比32位处理器需要更长的时间来执行64位mul操作。

即使在不擅长乘法的处理器上,乘以31实际上也可以非常快,因为x *= 31可以转换为x = x * 32 - x;x = (x << 5) - x; - 事实上它可能是值得的尝试[如果你没有将代码编译成汇编程序并看到编译器已经这样做了]。

除此之外,还会想到处理器或编译器特定的优化。例如,循环展开。或者使用内联汇编程序或内部函数来使用向量指令(取决于不同处理器体系结构和不同代的可用性)。像最近版本的gcc或clang这样的现代编译器可能会对这段代码进行矢量化,但要给出“正确”选项。

与所有优化项目一样,使用代表性工作负载衡量时间,记录您更改的内容。查看生成的代码,尝试确定是否有更好的方法。并且不要忘记这是OVERALL程序的性能至关重要的事实。如果你花费80%的时间在这个功能上,那么一定要优化它。如果你花费20%的时间,优化一下,如果你花费2%的时间,除非你可以做些改善它的事情,它不会给你太多。我已经看到人们在一些代码中编写代码来节省几个时钟周期的结果,这些代码在循环中需要几百万个周期才能进一步开启。并使用一些小巧的技巧来节省2个字节,占用半兆字节。它只是造成混乱,不值得做。

答案 2 :(得分:0)

让我先说这可能不值得做 - 你的哈希函数不太可能成为你程序中的瓶颈,所以使哈希函数更精细以试图提高它的效率可能只是在不使程序速度明显加快的同时,更难理解和维护。所以不要这样做,除非你确实已经确定你的程序花了很大一部分时间计算字符串哈希值,并确保你有一个很好的基准程序,你可以在“之前”和“之后”运行这个更改来验证它实际上确实加快了速度,否则你可能只是在追逐彩虹。

也就是说,更快速地散列长字符串的一种可能方法是一次处理字符串而不是一次处理一个字符,如下所示:

unsigned int aSlightlyFasterHash(const string & s)
{
   const unsigned int numWordsInString      = s.size()/sizeof(unsigned int);
   const unsigned int numExtraBytesInString = s.size()%sizeof(unsigned int);

   // Compute the bulk of the hash by reading the string a word at a time
   unsigned int hash = 0;
   const unsigned int * iptr = reinterpret_cast<const unsigned int *>(s.c_str());
   for (unsigned int i=0; i<numWordsInString; i++)
   {
      hash += *iptr;
      iptr++;
   }

   // Then any "leftover" bytes at the end we will mix in to the hash the old way
   const unsigned char * cptr = reinterpret_cast<const unsigned char *>(iptr);
   unsigned int multiplier = 1;
   for(unsigned int i=0; i<numExtraBytesInString; i++)
   {
       hash += (multiplier * *cptr);
       cptr++;
       multiplier *= 31;
   }
   return hash;
}

请注意,上面的函数将返回与您提供的散列函数不同的散列值。

将循环迭代次数减少四倍;当然,这个功能的执行很可能受到RAM带宽而不是CPU周期的限制,所以如果现代CPU的速度没有明显加快,那就太惊讶了。如果RAM带宽确实是瓶颈,那么你可以做的不多,因为你必须读取字符串的内容才能计算字符串的哈希码;没有解决这个问题(除非提前预先计算哈希代码并将其存储在某个地方,但只有在事先了解了所有要使用的字符串时才有效。)