在这部分代码中还有优化的余地吗?

时间:2019-03-08 08:44:58

标签: c

void HappyTest()
{
    for (int i = 0; i < 2100000000; i++) {
        int n = i;
        while (n >= 10) {
            int m = n, sum = 0;
            while (m != 0) {
                int t = m >= 10 ? m % 10 : m;
                sum += t * t;
                m /= 10;
            }
            n = sum;
        }
        //return n == 1 || n == 7;
        //if (i % 10000000 == 0) {
        //  cout << i << endl;
    }
}

VS2017 Debug Mode Performance analyzer

我使用vs2017的性能分析工具获取图中的数据,发现性能消耗主要是%和*运算。 这部分代码中还有优化的空间吗?

2 个答案:

答案 0 :(得分:0)

让我们先对代码进行较小的重写-例如:

int HappyTest()
{
    int n;
    for (int i = 0; i < 210000000; i++) {  // Reduced by a factor 10 compared to OP code
        n = i;
        while (n >= 10) {
            int m = n, sum = 0;
            while (m != 0) {
                int t = m >= 10 ? m % 10 : m;
                sum += t * t;
                m /= 10;
            }
            n = sum;
        }
    }
    return n;
}

int main(void) {
  printf("%d\n", HappyTest());
}

在我的系统上运行此程序需要10.7秒。

您的代码大约是:取一个数字,然后计算所有数字平方的总和。

因此您的乘法仅限于10个不同的乘法,即0 * 0、1 * 1,...,9 * 9

因此,一种优化的想法可能是将结果放入表中并进行表查找而不是乘法。喜欢:

int mul_tab_10[10] = {0, 1, 4, 9, 16, 25, 36, 49, 64, 81};

int HappyTest()
{
    int n;
    for (int i = 0; i < 210000000; i++) {
        n = i;
        while (n >= 10) {
            int m = n, sum = 0;
            while (m != 0) {
                int t = m >= 10 ? m % 10 : m;
                sum += mul_tab_10[t];
                m /= 10;
            }
            n = sum;
        }
    }
    return n;
}

在我的系统上运行此程序需要12.9秒。所以它

但是,如果我们进一步迈出这一步怎么办?不仅可以使用包含10个元素的表,还可以使用包含100个元素的表。喜欢:

int mul_tab_100[100] = {0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 1, 2, 5, 10, 17, 26, 37, 50, 65, 82, 4, 5, 8, 13, 20, 29, 40, 53, 68, 85, 9, 10, 13, 18, 25, 34, 45, 58, 73, 90, 16, 17, 20, 25, 32, 41, 52, 65, 80, 97, 25, 26, 29, 34, 41, 50, 61, 74, 89, 106, 36, 37, 40, 45, 52, 61, 72, 85, 100, 117, 49, 50, 53, 58, 65, 74, 85, 98, 113, 130, 64, 65, 68, 73, 80, 89, 100, 113, 128, 145, 81, 82, 85, 90, 97, 106, 117, 130, 145, 162};

int HappyTest()
{
    int n;
    for (int i = 0; i < 210000000; i++) {
        n = i;
        while (n >= 10) {
            int m = n, sum = 0;
            while (m != 0) {
                int t = m >= 100 ? m % 100 : m;  // Notice this change
                sum += mul_tab_100[t];
                m /= 100;                        // Notice this change
            }
            n = sum;
        }
    }
    return n;
}

在我的系统上运行此程序需要6.9秒。因此,性能得到了改善

再进行一步(即1000个元素)需要5.3秒,这又是改进

是的,您可以提高运行时性能。

答案 1 :(得分:0)

是的,还有效率的余地,但是我不确定代码的实际作用,因为它不会寻找带有快乐数字的重复循环。但是:

  • 摆脱不必要的int m

  • 对于值<10 ,但没有 m % 10除法,您避免了m / 10模数。可以在循环外计算最后一位数字

  • 使用缓存:您将在循环内一遍又一遍地检查相同的值(见下文)。

修改后的代码,尽管由于缺少花括号和注释的代码而导致不清楚

void HappyTest(void)
{
    for (int i = 0; i < 2100000000; i++) {
        int n = i;
        while (n >= 10) {
            int sum = 0;
            while (n >= 10) {       // while(m != 0)
                int t = n % 10;
                n /= 10;
                sum += t * t;
            }
            sum += n * n;           // final digit moved out of loop
            n = sum;
        }
        if(n == 1 || n == 7) {
            printf("%d\n", i);
        }
    }
}


回到缓存的想法。请注意,在第一个10位数的解析中,其位数的平方和不能超过9 * 9 * 10 = 810(对于32位int而言,实际上不能超过),该数字会反馈到第二次解析。因此可以对前810个数字进行不同的处理-它们的结果存储在数组中。其余的数字只需对数字进行一个解析,然后就可以查询结果。

#define LIMIT       2100000000
#define CACHE_SZ    810

void HappyTest(void)
{
    char cached[CACHE_SZ] = { 0 };

    // the first part also sets up the cache
    for (int i = 0; i < CACHE_SZ; i++) {
        int n = i;
        while (n >= 10) {
            int sum = 0;
            while (n >= 10) {       // while(m != 0)
                int t = n % 10;
                n /= 10;
                sum += t * t;
            }
            sum += n * n;           // final digit moved out of loop
            n = sum;
        }
        if(n == 1 || n == 7) {
            //printf("%d\n", i);
            cached[i] = 1;
            results++;
        }
    }

    // the second part continues more simply
    for (int i = CACHE_SZ; i < LIMIT; i++) {
        int n = i;
        int sum = 0;
        // only one parse of the number
        while (n >= 10) { 
            int t = n % 10;
            n /= 10;
            sum += t * t;
        }
        sum += n * n;
        // then look it up
        if(cached[sum]) {
            //printf("%d\n", i);
            results++;
        }
    }
}

这大约需要您函数花费时间的1/3(两者都不含printf)。

相关问题