zoukankan      html  css  js  c++  java
  • 编译期字符串Hash

    转载请注明出处为KlayGE游戏引擎,本文地址为http://www.klayge.org/?p=647 


    老早就想写这篇了,但一直没空,现在抽个空写写吧。

    Motivation

    编译期字符串Hash的想法源于一个常见做法,字符串比较。比如在KlayGE的effect系统中,有很多if ("cull_mode" == state_name)这样的比较。加速这个操作的方法之一就是hash。但是如果改成if (0x8F1E5E5F == state_name_hash)又非常不直观。如果能有一个编译期的hash方法,先称之为CT_HASH,就可以 if(CT_HASH("cull_mode") == state_name_hash),既保证直观,又保证开发效率。

    尝试

    要在编译期做事情首先都会想到template,但是C++的template没法提供字符串的操作,就算boost::mpl::string,也必须把一个一个字符分开塞给它才行,就像这样:

    boost::mpl::string<'c', 'u', 'l', 'l', '_', 'm', 'o', 'd', 'e'>

    仍然不直观,所以template这条路走不通。

    剩下的就是宏了。

    inline size_t HASH_FUNCTION(size_t seed, char ch)
    {
    return seed ^ (ch + 0x9e3779b9 + (seed << 6) + (seed >> 2));
    }

    #define HASH_RECURSE_00(seed, str) (*(str + 1) == 0 ? HASH_FUNCTION((seed), *(str)) : HASH_RECURSE_01(HASH_FUNCTION((seed), *(str)), (str + 1)))
    #define HASH_RECURSE_01(seed, str) (*(str + 1) == 0 ? HASH_FUNCTION((seed), *(str)) : HASH_RECURSE_02(HASH_FUNCTION((seed), *(str)), (str + 1)))
    #define HASH_RECURSE_02(seed, str) (*(str + 1) == 0 ? HASH_FUNCTION((seed), *(str)) : HASH_RECURSE_03(HASH_FUNCTION((seed), *(str)), (str + 1)))
    #define HASH_RECURSE_03(seed, str) (*(str + 1) == 0 ? HASH_FUNCTION((seed), *(str)) : HASH_RECURSE_04(HASH_FUNCTION((seed), *(str)), (str + 1)))
    #define HASH_RECURSE_04(seed, str) (*(str + 1) == 0 ? HASH_FUNCTION((seed), *(str)) : HASH_RECURSE_05(HASH_FUNCTION((seed), *(str)), (str + 1)))
    #define HASH_RECURSE_05(seed, str) (*(str + 1) == 0 ? HASH_FUNCTION((seed), *(str)) : HASH_RECURSE_06(HASH_FUNCTION((seed), *(str)), (str + 1)))
    #define HASH_RECURSE_06(seed, str) (*(str + 1) == 0 ? HASH_FUNCTION((seed), *(str)) : HASH_RECURSE_07(HASH_FUNCTION((seed), *(str)), (str + 1)))
    #define HASH_RECURSE_07(seed, str) (*(str + 1) == 0 ? HASH_FUNCTION((seed), *(str)) : HASH_RECURSE_08(HASH_FUNCTION((seed), *(str)), (str + 1)))
    #define HASH_RECURSE_08(seed, str) (*(str + 1) == 0 ? HASH_FUNCTION((seed), *(str)) : HASH_RECURSE_09(HASH_FUNCTION((seed), *(str)), (str + 1)))
    #define HASH_RECURSE_09(seed, str) (*(str + 1) == 0 ? HASH_FUNCTION((seed), *(str)) : HASH_RECURSE_10(HASH_FUNCTION((seed), *(str)), (str + 1)))
    #define HASH_RECURSE_10(seed, str) (*(str + 1) == 0 ? HASH_FUNCTION((seed), *(str)) : HASH_RECURSE_11(HASH_FUNCTION((seed), *(str)), (str + 1)))
    #define HASH_RECURSE_11(seed, str) (*(str + 1) == 0 ? HASH_FUNCTION((seed), *(str)) : HASH_RECURSE_12(HASH_FUNCTION((seed), *(str)), (str + 1)))
    #define HASH_RECURSE_12(seed, str) (*(str + 1) == 0 ? HASH_FUNCTION((seed), *(str)) : HASH_RECURSE_13(HASH_FUNCTION((seed), *(str)), (str + 1)))
    #define HASH_RECURSE_13(seed, str) (*(str + 1) == 0 ? HASH_FUNCTION((seed), *(str)) : HASH_RECURSE_14(HASH_FUNCTION((seed), *(str)), (str + 1)))
    #define HASH_RECURSE_14(seed, str) (*(str + 1) == 0 ? HASH_FUNCTION((seed), *(str)) : HASH_RECURSE_15(HASH_FUNCTION((seed), *(str)), (str + 1)))
    #define HASH_RECURSE_15(seed, str) (*(str + 1) == 0 ? HASH_FUNCTION((seed), *(str)) : HASH_RECURSE_16(HASH_FUNCTION((seed), *(str)), (str + 1)))
    #define HASH_RECURSE_16(seed, str) (*(str + 1) == 0 ? HASH_FUNCTION((seed), *(str)) : HASH_RECURSE_17(HASH_FUNCTION((seed), *(str)), (str + 1)))
    #define HASH_RECURSE_17(seed, str) (*(str + 1) == 0 ? HASH_FUNCTION((seed), *(str)) : HASH_RECURSE_18(HASH_FUNCTION((seed), *(str)), (str + 1)))
    #define HASH_RECURSE_18(seed, str) (*(str + 1) == 0 ? HASH_FUNCTION((seed), *(str)) : HASH_RECURSE_19(HASH_FUNCTION((seed), *(str)), (str + 1)))
    #define HASH_RECURSE_19(seed, str) HASH_FUNCTION((seed), *(str))

    #define CT_HASH(str) HASH_RECURSE_00(0, (str))

    这里的HASH_FUNCTION来自于boost,其本身是否出现冲突的情况不属于本片讨论的范围。值得注意的是,如果HASH_FUNCTION用宏的方式来写,那么HASH_RECURSE到9个之后就会造成编译器heap溢出,必须写成函数的形式。

    编译期?

    这样的CT_HASH是编译期的吗?是也不是。在Release下,看汇编的结果可以发现

    cout << CT_HASH("cull_mode") << endl;

    被编译成了

    00DC1103  mov         eax,dword ptr [__imp_std::endl (0DC2054h)]
    00DC1108  mov         ecx,dword ptr [__imp_std::cout (0DC2058h)]
    00DC110E  push        eax
    00DC110F  push        8F1E5E5Fh
    00DC1114  mov         dword ptr [ebp-18h],0Fh
    00DC111B  mov         dword ptr [ebp-1Ch],ebx
    00DC111E  mov         byte ptr [ebp-2Ch],bl
    00DC1121  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0DC2048h)]
    00DC1127  mov         ecx,eax
    00DC1129  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0DC2044h)]

    也就是说CT_HASH("cull_mode")被编译成了常量8F1E5E5Fh。看似成功了,但是对于另一个更严格的常量测试

    switch (state_name_hash)
    {
    case CT_HASH("cull_mode"):
    ...
    }

    却失败了。也就是说CT_HASH("cull_mode")其实并不是编译期常量,而是优化器产生的。

    结论

    想要做一个编译期字符串Hash的尝试HLL地失败了:(,只能做到“优化期”常量的效果。如果各位看客有何高见的话不妨讨论讨论!

  • 相关阅读:
    大公司?小公司?
    git 学习笔记
    django学习笔记
    web servieces 学习小栗子
    python列表推导式
    什么叫事务,事务的特性
    监听问题汇总
    oracle数据库导入导出
    ORA-12514: TNS: 监听程序当前无法识别连接描述符中请求的服务--解决办法(转)
    目标修正
  • 原文地址:https://www.cnblogs.com/gongminmin/p/1935449.html
Copyright © 2011-2022 走看看