2013-09-28 19 views
2

我做了很多在我的计划散列的,所以我决定砍了constexpr功能,可以在做编译期至少一些散列的我时间。在成功实现constexpr散列函数之后,我对代码进行了剖析,实际上需要时间 - 这很奇怪,因为计算应该在编译时发生,而不是运行时发生。使用G ++ 4.7.3。与常量字符数组参数剖析constexpr显示运行时执行

下面是一个有点gprof的输出,以及一个演示程序完成一个非constexpr实施以来,constexpr的功能是很难读,也表明它的工作原理。

我从以下链接所采取的意见和作出的字符数组constexpr以及常量: why is a const array not accessible from a constexpr function?

注:有些东西已经从代码中删除,以简化这个演示,如测试和断言。

1.)我的constexpr函数是否在运行时执行? (看起来很明显他们是)

2.)如果是这样,为什么?我该如何让它在编译时而不是运行时执行?

gprof的:

Each sample counts as 0.01 seconds. 
    % cumulative self    self  total   
time seconds seconds calls us/call us/call name  
50.00  0.06  0.06 600012  0.09  0.09 string_length(char const*, unsigned int) 
36.36  0.10  0.04 50001  0.80  2.20 HASHOAT_CONSTEXPR(char const*, unsigned int, unsigned int, unsigned int) 
    9.09  0.10  0.01 1100022  0.01  0.01 oat_part_two(unsigned int const&) 
    4.55  0.11  0.01 50001  0.10  0.10 oat_part_six(unsigned int const&) 
    0.00  0.11  0.00 1650033  0.00  0.00 oat_part_one(unsigned int const&, char) 
    0.00  0.11  0.00 550011  0.00  0.00 oat_part_three(unsigned int const&) 
    0.00  0.11  0.00 200004  0.00  0.00 oat_part_four(unsigned int const&) 
    0.00  0.11  0.00 100002  0.00  0.00 oat_part_five(unsigned int const&) 
    0.00  0.11  0.00  1  0.00  0.00 HashOAT(char const*, unsigned int) 

演示程序:

#include <cstdio> 
#include <cstring> 

// "One-at-a-time" Hash 

// the non-constexpr implementation: 
unsigned int HashOAT(const char *key, const unsigned int size = 1009); // size must be prime 
unsigned int HashOAT(const char *key, const unsigned int size) { 
    unsigned int h = 0; 
    const std::size_t len = strlen(key); 

    for (std::size_t i = 0; i < len; ++i) { 
     h += static_cast< unsigned int >(key[i]); 
     h += (h << 10); 
     h ^= (h >> 6); 
    } 

    h += (h << 3); 
    h ^= (h >> 11); 
    h += (h << 15); 

    return h % size; 
} 

constexpr unsigned int HASHOAT_CONSTEXPR(const char* str, const std::size_t size=1009, const std::size_t idx=0, const std::size_t h=0); 
constexpr unsigned int oat_part_one(const std::size_t& h, const char c); 
constexpr unsigned int oat_part_two(const std::size_t& h); 
constexpr unsigned int oat_part_three(const std::size_t& h); 
constexpr unsigned int oat_part_four(const std::size_t& h); 
constexpr unsigned int oat_part_five(const std::size_t& h); 
constexpr unsigned int oat_part_six(const std::size_t& h); 

constexpr unsigned int oat_part_one(const std::size_t& h, const char c) { 
    return (h + static_cast<unsigned int>(c)); 
} 

constexpr unsigned int oat_part_two(const std::size_t& h) { 
    return (h << 10); 
} 

constexpr unsigned int oat_part_three(const std::size_t& h) { 
    return (h >> 6); 
} 

constexpr unsigned int oat_part_four(const std::size_t& h) { 
    return (h << 3); 
} 

constexpr unsigned int oat_part_five(const std::size_t& h) { 
    return (h >> 11); 
} 

constexpr unsigned int oat_part_six(const std::size_t& h) { 
    return (h << 15); 
} 

constexpr std::size_t string_length(const char* str, std::size_t index = 0) { 
    return (str == nullptr || str[index] == '\0') ? 0 : 1 + string_length(str, index+1); 
} 

constexpr unsigned int HASHOAT_CONSTEXPR(const char* str, const std::size_t size, const std::size_t idx, const std::size_t h) { 
    return (
     (idx == string_length(str)) ? (
      (
       (
        (h + oat_part_four(h))^
        oat_part_five(h + oat_part_four(h)) 
       ) + 
       oat_part_six(
        (h + oat_part_four(h))^
        oat_part_five(h + oat_part_four(h)) 
       ) 
      ) % size 
     ) : (
      HASHOAT_CONSTEXPR(str, size, idx+1, 
       (
        oat_part_one(h, str[idx]) + 
        oat_part_two(h + static_cast< unsigned int>(str[idx])) 
       )^
       oat_part_three(oat_part_one(h, str[idx]) + 
          oat_part_two(oat_part_one(h, str[idx])) 
       ) 
      ) 
     ) 
    ); 
} 

int main (void) { 
    constexpr const char* str="Some String"; 
    printf("Hash: %i\n", HashOAT(str)); 
    printf("Hash: %i\n", HASHOAT_CONSTEXPR(str)); 

    // make the program take some time so we can see if the constexpr function is actually taking run-time 
    for (int i=0; i<50000; ++i) { 
     HASHOAT_CONSTEXPR(str); 
    } 
    return 0; 
} 

回答

0

20天已经通过从没有任何人回答,所以我决定挖得更深一些。

它编译(使用g ++ -O#)时发生,我尝试不同的优化级别

我碰到的for循环迭代几百万次(一个相当旧的计算机上),并定时在优化执行级别0,1,2,3和4。

我还编译成ASM(与G ++ -S),并检查了程序产生组件。

我得出的结论是,constexpr函数或可能特别复杂的constexpr函数在低于2的任何优化级别上被视为正常函数。在2级或更高级别,G ++在编译时评估函数,不要进入可执行文件(从检查程序集,asm文件要短得多)。完全优化的可执行文件在一秒钟内完成了执行,而未优化的可执行文件花费了大约十倍的时间。此外,优化的可执行文件在使用gprof进行分析时,在其输出中未显示任何constexpr函数。

底线是constexpr仅获取在编译时优化级别2或更高编译时进行评价。

+0

这里是最近的讨论(阅读:参数)约在Slashdot constexpr,与VC2013,但仍然具有现实意义: http://developers.slashdot.org/comments.pl?sid=4349323&cid=45158845 – Brandon