【破烂集】神奇的优化

之前沉迷于研究各种优化,看了很多网上关于常数优化的文章,突然豁然开朗,发现很多之前都没有想到过的细节,看了文章有理有据的论证之后,我发现原来有那么多可以改进的地方(举个例子,在xdoj如果开了O3优化可以快近一倍,这个O3优化可以视为把所有细节做到极致)我也进行了一些测试,现在就让我将我发现的地方一一说明。

1++i代替i++(没用)
看了文章,说i++需要另外存储之前的值,所以会比++i慢一些,所以在循环这种没什么区别的地方用++i是更好的选择。
实测,没什么用处

2位运算优化
这个优化实际上效果比较明显,看了文章说本质上是因为汇编语言更简便了,而且因此不是所有的位运算都会快,如果用了太多照样比不过傻写的函数,比如abs,max,min这些,根本不需要用位运算(我是在想不出来那些人是怎么找出来用位运算计算这些函数的方法的,但是的确有,然而没用),令人意外的是看似很高大上的亦或swap实际上还不如简单的三元swap,位运算比较有优势的其实是除法和取模(这两个运算算是比较费时的),乘法据说差不多,汇编语言都一样,所以无所谓用不用。

3取模优化
之前看很多大佬喜欢写类似这样的程序

int mul(int x, int y) {
    LL z = 1LL * x * y;
    return z - z / mod * mod;
}

看似很不错,然而测试结果还不如直接暴力取模,首先这个写法跟取模就是一样的(否则怎么取呢?)多此一举了,其次还有判断和调用的时间,本身唯一的收益只剩下在判断小于的时候不取模的时间而已,本身乘法不超的可能性就很小,大于还要取模,没有任何节省。所以乘法的函数没用,然而加法减法还是很有用的,因为加法和减法本身超限可能性更小,而且即使超出范围了也可以用加减代替取模,显然加减的时间要远小于取模,所以怎么都是有收益的。

int add(int a , int b)
{
    return (a += b) >= Mod ? a - Mod : a;
}
int sub(int a , int b)
{
    return (a -= b) < 0 ? a + Mod : a;
}

4判断与三目判别式(没用)
根据文章理论,if会有一个尝试,默认一个对错,而三目没有,所以如果对错情况相差很大的时候建议把情况多的放在if中,小的放else中,平均用三目,只有if不需要三目(当然if也有更好的替代品)
(实测,没什么用,感觉用什么随意)

5且与或的妙用(没用)
if(A) B; == (A) && (B)
if(A) B; else C; == (A) && (B , 1) || (C)
所以理论上所有if语句都可以用且与或优化替代,时间更少
(实测没什么用)

6指针代替数组
很多图论问题用链式向前星或是构造树的时候,我一开始比较喜欢用数下标代替指针,不过这篇文章说数组每次调用需要进行一个地址加法,所以会更慢,想想的确很有道理,之前看正版的zkw最小费用流的时候,我把作者复杂而难懂的指针都换成数组了,结果时间也慢了100多ms,果然很是调用的问题。

7数组大的开前面(???)
玄学??感觉有点道理,因为数组调用肯定是大的在前面转移的指针小一些,不过没有实测过,姑且相信吧。

8进程优化
之前火聚聚给我们展示过这个巧妙的优化,一开始还不太相信,后来实测才发现的确是更快,不过这个如何激发多线程我个人感觉挺迷的。放一个大佬的展示样例吧。

void Init(int *d, int n){
    for(int i = 0; i < n; i++)
        d[i] = 0;
}


void Init2(int *d, int n){  
    int i; 
    for(i = 0; i < n; i+= 4){ 
        d[i] = 0;
        d[i + 1] = 0;
        d[i + 2] = 0;
        d[i + 3] = 0;
    }
    for(; i < n; i++)
        d[i] = 0;
}

第二个的确是比第一个快一些的。

9逗号(微乎其微)
听说写逗号会变快,又是个玄学优化,反正看很多大佬都喜欢写逗号,之前以为只是为了偷懒不用写大括号,没想到还有效率问题,那我就得好好研究一下了。不过平常写代码还是不建议写逗号,我记得一本书,叫什么标准化c语言说过那是很不好的写法。
(实测,有用,但是微乎其微,大约3e6次快1ms)

10line(没用)
又是个玄学优化,感觉写了也没感觉快了多少,不过反正写了写不了吃亏,写不了上当,就短短一个单词,之前看很多大佬把函数都写上心inline,所以还是值得拥有的。
(实测,没什么用)

11引用优化
记得之前做fft的题的时候为了偷懒把operator中的const cp & const换成了cp,倒是没错,但是效率降低了不少,改回来就恢复了,这个在很多书中都有说过,因为引用就相当于一个地址,所以在变量本身很庞大,复制效率很低的时候用引用会快不少,这个优化还是很显著的,不过int double这些就不需要了,反正也差不了太多,主要是结构体和vector这种。

12long long优化
long long由于长度是int的两倍,所以做很多运算的时间都远高于int,所以不到不必要的时候不要做long long运算,这个优化还是很显著的,好几次靠着这个在多校之类的比赛上解决卡场问题了,算是最需要重视的一个问题。

13register
据说在局部变量中加了这个调用会快很多,所以适合给需求大的变量,不过由于寄存器本身不大,所以不能赋太多,否则就不会存在寄存器里面了。

14邪术

# pragma GCC optimize("-O3")

程序开头加这个,普遍快1/3到1/2,然而比赛可能用不了,也有可能会自动开

先说这么多吧,对于比较玄学的,我也会去查证,有了新的也会有补充。

猜你喜欢

转载自blog.csdn.net/qq_40772738/article/details/82383985