Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/56.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ruby/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C函数优化_C_Optimization - Fatal编程技术网

C函数优化

C函数优化,c,optimization,C,Optimization,我有一个类似这样的函数,常量a1-e8(双精度浮点)在代码中,比如说硬编码或定义的。该函数接受-1.0到1.0范围内的双精度,需要按四分之一进行分割,如图所示 在汇编语言优化之前,我还可以进行其他代码优化来提高运行时性能吗?我试着用一个x2来保持x*x,并用e常数乘以x2*x2,但实际上这降低了性能。我还尝试将x的副本转换为整数,并使用switch语句,但这也降低了性能 double operation(double x) { if (x <= -0.75 && x

我有一个类似这样的函数,常量a1-e8(双精度浮点)在代码中,比如说硬编码或定义的。该函数接受-1.0到1.0范围内的双精度,需要按四分之一进行分割,如图所示

在汇编语言优化之前,我还可以进行其他代码优化来提高运行时性能吗?我试着用一个x2来保持x*x,并用e常数乘以x2*x2,但实际上这降低了性能。我还尝试将x的副本转换为整数,并使用switch语句,但这也降低了性能

double operation(double x) {
    if (x <= -0.75 && x >= -1.0) {
        return a1 + b1*x + c1*x*x + d1*x*x*x + e1*x*x*x*x;
    }
    else if (x <= -0.5) {
        return a2 + b2*x - c2*x*x - d2*x*x*x - e2*x*x*x*x;
    }
    else if (x <= -0.25) {
        return a3 - b3*x - c3*x*x - d3*x*x*x - e3*x*x*x*x;
    }
    else if (x <= 0.0) {
        return a4 - b4*x - c4*x*x - d4*x*x*x + e4*x*x*x*x;
    }
    else if (x <= 0.25) {
        return a5 + b5*x - c5*x*x + d5*x*x*x + e5*x*x*x*x;
    }
    else if (x <= 0.5) {
        return a6 + b6*x - c6*x*x + d6*x*x*x - e6*x*x*x*x;
    }
    else if (x <= 0.75) {
        return a7 - b7*x - c7*x*x + d7*x*x*x - e7*x*x*x*x;
    }
    else if (x <= 1.0) {
        return a8 - b8*x + c8*x*x - d8*x*x*x + f8*x*x*x*x;
    }
    return 0.0;
}
double操作(双x){
如果(x=-1.0){
返回a1+b1*x+c1*x*x+d1*x*x*x+e1*x*x*x*x*x*x;
}

如果(x在香草mac上使用叮当声:

double dcos(double a, double b, double c, double d, double e, double x) {
        return a + b * x + c * x * x + d * x * x * x + e * x * x * x * x;
}
生成10个mulsd,4个addsd 鉴于:

生成7个mulsd,3个addsd。 它可能在数值上不太稳定,但这是一个区别。在一个快速而肮脏的测试中,它减少了大约16%

bfm:tmp steve$ cc -O3 m.c m2.c -o m2
bfm:tmp steve$ cc -O3 m.c m1.c -o m1
bfm:tmp steve$ time ./m1
inf

real    0m4.136s
user    0m4.100s
sys 0m0.026s
bfm:tmp steve$ time ./m2
inf

real    0m3.501s
user    0m3.475s
sys 0m0.023s

除了using compile标志(-Ofast on Linux/Mint19)将性能提高约2.5(100000000次调用,大部分在该范围内)之外,很少有小的调整可以帮助:

  • 用查找替换if。请参见下文。减少查找正确案例的浪费时间。系数已根据公式的加减法进行了+/-调整
这将提供+25%的速度

原始代码:未优化:2.154 使用-Ofast:0.678进行优化 修改代码-Ofast:0.581

double operation(double x) {
    static double aa[] = { a1, a2, a3, a4, a5, a6, a7, a8 } ;
    static double bb[] = { b1, b2, -b3, -b4, b5, b6, -b7, -b8 } ;
    static double cc[] = { c1, -c2, -c3, -c4, -c5, -c6, -c7, c8 } ;
    static double dd[] = { d1, -d2, -d3, -d4, d5, d6, d7, -d8 } ;
    static double ee[] = { e1, -e2, -e3, e4, e5, -e6, -e7, e8 } ;


    if (x < -1.0 || x > 1.0) {
        return 0 ;
    }
    int p = x*4 + 4 ;
//    if ( p < 0 ) p = 1;
    return aa[p] + bb[p]*x + cc[p]*x*x + dd[p]*x*x*x + ee[p]*x*x*x*x;
}
double操作(双x){
静态双aa[]={a1,a2,a3,a4,a5,a6,a7,a8};
静态双bb[]={b1,b2,-b3,-b4,b5,b6,-b7,-b8};
静态双cc[]={c1,-c2,-c3,-c4,-c5,-c6,-c7,c8};
静态双dd[]={d1,-d2,-d3,-d4,d5,d6,d7,-d8};
静态双ee[]={e1,-e2,-e3,e4,e5,-e6,-e7,e8};
如果(x<-1.0 | | x>1.0){
返回0;
}
int p=x*4+4;
//如果(p<0)p=1;
返回aa[p]+bb[p]*x+cc[p]*x*x+dd[p]*x*x+ee[p]*x*x*x;
}
注:我相信原始代码有一个小的。它将使用(x)的系数 在汇编语言优化之前,我还可以进行其他代码优化来提高运行时性能吗

重新安排比较,以便基本上对正确的案例进行二进制搜索,而不是线性搜索,从而大大加快了速度:

double op2(double x) {
    if (x <= 0) {
        if (x <= -0.5) {
            if (x <= -0.75 && x >= -1.0) {
                return a1 + b1*x + c1*x*x + d1*x*x*x + e1*x*x*x*x;
            }
            return a2 + b2*x - c2*x*x - d2*x*x*x - e2*x*x*x*x;
        }
        else {
            if (x <= -0.25) {
                return a3 - b3*x - c3*x*x - d3*x*x*x - e3*x*x*x*x;
            }
            return a4 - b4*x - c4*x*x - d4*x*x*x + e4*x*x*x*x;
        }
    }
    else {
        if (x <= 0.5) {
            if (x <= 0.25) {
                return a5 + b5*x - c5*x*x + d5*x*x*x + e5*x*x*x*x;
            }
            return a6 + b6*x - c6*x*x + d6*x*x*x - e6*x*x*x*x;
        }
        else {
            if (x <= 0.75) {
                return a7 - b7*x - c7*x*x + d7*x*x*x - e7*x*x*x*x;
            }
            else if (x <= 1.0) {
                return a8 - b8*x + c8*x*x - d8*x*x*x + e8*x*x*x*x;
            }
        }
    }
    return 0.0;
}
结果是:

因此,
op3
比原来的
op1
快得多,但是
op2
在这种情况下仍然是赢家。不过,如果你有更多的情况,我认为你最终会达到这样一个点:将输入映射到整数的成本小于
op2
中比较的成本


查看这三个函数,您可以看到
op1
方法的复杂性是O(n),其中n是间隔数。
op2
方法是O(logn),因为n个间隔需要对数n级的比较。
op3
方法是O(1):将输入映射到间隔后,switch语句可以使用跳转表在恒定时间内查找正确的大小写。

您使用了哪些编译/优化标志?()-O3;没什么特别的。使用-cl快速放松数学,它们是相同的,并且在5倍时速度更快如果你有一个合理的现代处理器,你已经融合了乘法-加法指令。使用
-mfma
重试。霍纳规则会使你的答案错误。这似乎是一个“口袋”优化的情况。即优化太早了。通常这样的优化不值得付出努力。建议完成程序,使其正确工作,然后跟踪程序以查看瓶颈在哪里。然而,通常最好的优化是仔细评估整个算法,因为您在算法中寻找瓶颈HMI最初尝试过,但没有获得性能增加了我正在寻找的,但是Scott Hunter的因式分解方法,还有对IF/SER的二进制搜索,除了编译器优化标志之外,还有最大的性能提升。注释不是用于扩展的讨论;这个对话一直是。如果你喜欢任何答案,请考虑接受它,而不是做多个数组。s、 使用
struct{double a,b,c,d,e,pad[3];}的单个缓存线对齐数组可以获得更好的缓存局部性
因此,给定范围内的所有常量最终都在同一缓存线中…当然,如果使用Remez vs Taylor级数,我们不需要多个范围:Vurtun/nuklear有一个内部trig函数的示例。这是一个好主意。我尝试使用上述结构,以及内置的矢量化,以使mm256_*做这项工作-希望使用verctor乘法+水平加法。我无法得到正确的结果(或加速)-我有一些组装经验,但没有AVX/SSE内置的实际经验。希望有人能从这里继续。只是修复了代码以获得正确的数字。修改后的结构比上面的解决方案执行速度慢5%。不确定如何执行。我将把它留给在矢量化操作方面比我自己更有经验的人使用AVX、gcc-Ofast-mavx512f-4X加速对于-mavx512,您需要一台具有最新AVX支持的机器。非法指令表明您的主机没有AVX。我仍在努力为我执行此操作,只是一个说明,在if/else中手动实现二进制搜索,而不是实现int的开关/大小写(先乘以4,然后从x开始计算)带来了巨大的性能提升。我认为编译器会自动使用switch-case语句进行最佳搜索,但事实并非如此。您是否也可以对@dash-o answer variant进行基准测试?
double op2(double x) {
    if (x <= 0) {
        if (x <= -0.5) {
            if (x <= -0.75 && x >= -1.0) {
                return a1 + b1*x + c1*x*x + d1*x*x*x + e1*x*x*x*x;
            }
            return a2 + b2*x - c2*x*x - d2*x*x*x - e2*x*x*x*x;
        }
        else {
            if (x <= -0.25) {
                return a3 - b3*x - c3*x*x - d3*x*x*x - e3*x*x*x*x;
            }
            return a4 - b4*x - c4*x*x - d4*x*x*x + e4*x*x*x*x;
        }
    }
    else {
        if (x <= 0.5) {
            if (x <= 0.25) {
                return a5 + b5*x - c5*x*x + d5*x*x*x + e5*x*x*x*x;
            }
            return a6 + b6*x - c6*x*x + d6*x*x*x - e6*x*x*x*x;
        }
        else {
            if (x <= 0.75) {
                return a7 - b7*x - c7*x*x + d7*x*x*x - e7*x*x*x*x;
            }
            else if (x <= 1.0) {
                return a8 - b8*x + c8*x*x - d8*x*x*x + e8*x*x*x*x;
            }
        }
    }
    return 0.0;
}
double op3(double x) {
    int c = (int)((x + 1) * 4);    // mapping from double to int
    switch (c) {
        case 0: {
            return a1 + b1*x + c1*x*x + d1*x*x*x + e1*x*x*x*x;
        }
        case 1: {
            return a2 + b2*x - c2*x*x - d2*x*x*x - e2*x*x*x*x;
        }
        case 2: {
            return a3 - b3*x - c3*x*x - d3*x*x*x - e3*x*x*x*x;
        }
        case 3: {
            return a4 - b4*x - c4*x*x - d4*x*x*x + e4*x*x*x*x;
        }
        case 4: {
            return a5 + b5*x - c5*x*x + d5*x*x*x + e5*x*x*x*x;
        }
        case 5: {
            return a6 + b6*x - c6*x*x + d6*x*x*x - e6*x*x*x*x;
        }
        case 6: {
            return a7 - b7*x - c7*x*x + d7*x*x*x - e7*x*x*x*x;
        }
        case 7: {
            return a8 - b8*x + c8*x*x - d8*x*x*x + e8*x*x*x*x;
         }
        default: {
            return 0.0;
        }
    }
}