稀奇古怪的优化技巧

查表法加速条件判断

// Converts value in the range [0, 100) to a string.
const char* digits2(size_t value) {
  // GCC generates slightly better code when value is pointer-size.
  return &"0001020304050607080910111213141516171819"
         "2021222324252627282930313233343536373839"
         "4041424344454647484950515253545556575859"
         "6061626364656667686970717273747576777879"
         "8081828384858687888990919293949596979899"[value * 2];
}

函数中的字符串字面量是一个长字符串,包含了从 0 到 99 的所有可能的两位数字符串。每两个字符表示一个两位数,即 “00” 表示 0,“01” 表示 1,以此类推,“99” 表示 99。

函数的返回值是一个指向字符串中特定两位数字符串的指针。返回的指针指向的字符串是根据参数 value 的值计算得出的。具体地,通过将 value 乘以 2,可以得到在字符串中的偏移量,从而获取相应的两位数字符串。

因此,这个函数的作用是将范围在 [0, 100) 内的值转换为对应的两位数字符串。例如,当 value 的值为 0 时,返回的指针指向字符串 “00”,当 value 的值为 99 时,返回的指针指向字符串 “99”。此函数避免了加法运算的产生,通过一次间接寻址显著提高了执行效率。

循环展开

template <typename T> auto count_digits_fallback(T n) -> int {
  int count = 1;
  for (;;) {
    // Integer division is slow so do it for a group of four digits instead
    // of for every digit. The idea comes from the talk by Alexandrescu
    // "Three Optimization Tips for C++". See speed-test for a comparison.
    if (n < 10) return count;
    if (n < 100) return count + 1;
    if (n < 1000) return count + 2;
    if (n < 10000) return count + 3;
    n /= 10000u;
    count += 4;
  }
}

在每次迭代中,通过一系列的条件判断来确定 n 的范围,并根据范围的不同返回相应的位数。

首先,如果 n 小于 10,则表示 n 是个位数,直接返回 count 的值作为位数。

然后,如果 n 在 10 和 100 之间,则表示 n 是两位数,返回 count + 1 的值作为位数。

类似地,如果 n 在 100 和 1000 之间,则返回 count + 2 的值作为位数。

如果 n 在 1000 和 10000 之间,则返回 count + 3 的值作为位数。

如果 n 大于等于 10000,则通过将 n 除以 10000(无符号)来将其缩小为一个较小的范围,并将 count 的值增加 4,以表示跳过了 4 位数。

循环将继续进行,以便对 n 的更高位数进行计算,直到找到适当的范围并返回位数为止。

因此,这个函数的作用是计算整数值的十进制位数。它使用了一种优化技巧,通过对整数值进行分组,每组四位进行整数除法,以减少除法操作的次数,从而提高性能。背后的原理是while循环中每次循环如果依赖上次循环的结果,会极大地降低cpu流水线的并行程度,通过循环展开,可以显著提高cpu流水线的指令并行程度,从而提高执行效率。

(这个差距真的很大。。。。我测了一下,大概4倍

#include 
#include 
#include 
#include 

template <typename T>
auto count_digits_fallback(T n) -> int
{
    int count = 1;
    for (;;) {
        // Integer division is slow so do it for a group of four digits instead
        // of for every digit. The idea comes from the talk by Alexandrescu
        // "Three Optimization Tips for C++". See speed-test for a comparison.
        if (n < 10)
            return count;
        if (n < 100)
            return count + 1;
        if (n < 1000)
            return count + 2;
        if (n < 10000)
            return count + 3;
        n /= 10000u;
        count += 4;
    }
}

template <typename T>
auto count_digits_no_fallback(T n) -> int
{
    int count = 1;
    while(n) {
        n /= 10;
        count ++;
    }
    return count;
}

uint64_t get_time()
{
    return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
}

int main()
{
    uint64_t __now_time = get_time();
    for (int i = 0; i < 10000000; i++)
        count_digits_fallback(i);
    std::cout << get_time() - __now_time << std::endl;
    __now_time = get_time();
    for (int i = 0; i < 10000000; i++)
        count_digits_no_fallback(i);
    std::cout << get_time() - __now_time << std::endl;
}

你可能感兴趣的:(C++,开发语言,c++)