还能这样玩——关于一些OI的黑(sao)科(cao)技(zuo)优化

免责声明

本文仅供参考,由读者引发的一切后果,任何责任由读者自行承担。后果包括但不限于:
1. 优化后程序变慢。
2. 某些 O J / O I OJ/OI OJ/OI上导致的编译错误。
3. 文章中的某些错误引起的争论。

一、 G C C GCC GCC系列

关于GCC的黑科技想必大家也就知道这几句吧:

	#pragma GCC optimize (3)
	#pragma GCC optimize (2)
	#pragma GCC optimize (1)

俗称“吸氧”。


在这里小编就简单地讲一下 O 1 O1 O1, O 2 O2 O2, O 3 O3 O3, O s Os Os(怎么还有个 O s Os Os)

1. O 1.O 1.O系列

( 1 ) (1) (1). O 1 O1 O1

包含下列选项

   -fauto-inc-dec
   -fcprop-registers
   -fdce
   -fdefer-pop
   -fdelayed-branch
   -fdse
   -fguess-branch-probability
   -fif-conversion2
   -fif-conversion
   -finline-small-functions
   -fipa-pure-const
   -fipa-reference
   -fmerge-constants -fsplit-wide-types
   -ftree-builtin-call-dce
   -ftree-ccp
   -ftree-ch
   -ftree-copyrename
   -ftree-dce
   -ftree-dominator-opts
   -ftree-dse
   -ftree-fre
   -ftree-sra
   -ftree-ter
   -funit-at-a-time
   -fomit-frame-pointer

( 2 ) (2) (2). O 2 O2 O2

除了加载-O1的选项外,还加载

    -fthread-jumps
    -falign-functions -falign-jumps
    -falign-loops -falign-labels
    -fcaller-saves
    -fcrossjumping
    -fcse-follow-jumps -fcse-skip-blocks
    -fdelete-null-pointer-checks
    -fexpensive-optimizations
    -fgcse -fgcse-lm
    -findirect-inlining
    -foptimize-sibling-calls
    -fpeephole2
    -fregmove
    -freorder-blocks -freorder-functions
    -frerun-cse-after-loop
    -fsched-interblock -fsched-spec
    -fschedule-insns -fschedule-insns2
    -fstrict-aliasing -fstrict-overflow
    -ftree-switch-conversion
    -ftree-pre
    -ftree-vrp

( 3 ) (3) (3). O 3 O3 O3

除了加载-O2外,还加载

    -finline-functions
    -funswitch-loops’?
    -fpredictive-commoning
    -fgcse-after-reload
    -ftree-vectorize

( 4 ) (4) (4). O s Os Os

在研究编译驱动的makefile的时候,发现GCC的命令行里面有一个-Os的优化选项。

遍查GCC文档,发现了-O0,-O1,-O2,-O3,就是没有发现-Os。

祭出GOOGLE,搜了一下,终于发现这篇文章说明了-Os的作用:

http://www.linuxjournal.com/article/7269

原来-Os相当于-O2.5。是使用了所有-O2的优化选项,但又不缩减代码尺寸的方法。
除了包含-O2的开关外,-Os还会使得下列开关禁用。

	-falign-functions 
    -falign-jumps 
    -falign-loops
    -falign-labels 
    -freorder-blocks 
    -freorder-blocks-and-partition
    -fprefetch-loop-arrays 
    -ftree-vect-loop-version

另外,对于多个-O选项的情形,最后一个加载的为有效。比如gcc –O1 –Os –O3 –o test test.c,有效的优化开关为-O3。

一般来说,用的最多的是-O3和-Os,如果遇到程序运行不正常的问题,请降低优化级别,如把-O3改为-O2(情况很少见)。

2. 2. 2.针对目标机器

( 1 ) (1) (1)-march=cpu-type

为cpu-type所针对的机器开启需要的指令集。

cpu-type可以为pentium4、core2、athlon-4等(具体参见文档),比如-march=core2时,则会开启core2所支持的MMX、SSE、SSE2、SSE3、SSSE3指令集。

另外还支持native类型,为编译器所在目前的CPU类型优化指令集,指定-march=native

( 2 ) (2) (2) -mfpmath=unit

选择浮点运算单元。
unit可以为387和sse

387为x86系列的默认值,使用标准的387浮点协处理器。

sse为x64的默认值,使用sse指令集。

一般你的程序如果有大量的浮点运算的话,在P4和K8以上级别的处理器上推荐开启-mfpmath=sse

( 3 ) (3) (3)加载指定指令集。

可以使用-msse2或-msse4.1加载指定的指令集。

3. 3. 3.其他比较有效的选项

( 1 ) (1) (1)-ftracer

执行尾部复制以扩大超级块的尺寸,它简化了函数控制流,从而允许其它的优化措施做的更好。单独使用没啥意义,和其他优化选项一起使用很有效。

( 2 ) (2) (2)-ffast-math

违反IEEE/ANSI标准以提高浮点数计算速度,是个危险的选项,仅在编译不需要严格遵守IEEE规范且浮点计算密集的程序考虑采用。不考虑精度时使用这个选项速度会加快。

( 3 ) (3) (3)-fivopts

在trees上执行归纳变量优化。

( 4 ) (4) (4)-ftree-parallelize-loops=n

使循环并行化。只当循环无数据依赖时使用,在多核CPU上时使用才会有利。

( 5 ) (5) (5)-ftree-loop-linear

在trees上进行线型循环转换。它能够改进缓冲性能并且允许进行更进一步的循环优化。

( 6 ) (6) (6)-fforce-addr

必须将地址复制到寄存器中才能对他们进行运算。由于所需地址通常在前面已经加载到寄存器中了,所以这个选项可以改进代码。

( 7 ) (7) (7)-floop-interchange

交换循环变量。
例如:

    DO J = 1, M
        DO I = 1, N
            A(J, I) = A(J, I) * C
        ENDDO
    ENDDO
    会改变为

	DO I = 1, N
    	DO J = 1, M
        	A(J, I) = A(J, I) * C
    	ENDDO
	ENDDO

改变后,如果N比缓冲区大的话,会更有效率。这是因为Fortran里数组是以列主元为排列方式的。当然这个选项并不仅仅用于Fortran,Gcc家族的编译器都有效。

( 8 ) (8) (8)-fvisibility=hidden

设置默认的ELF镜像中符号的可见性为隐藏。使用这个特性可以非常充分的提高连接和加载共享库的性能,生成更加优化的代码,提供近乎完美的API输出和防止符号碰撞。我们强烈建议你在编译任何共享库(Dll)的时候使用该选项。

-fvisibility-inlines-hidden

默认隐藏所有内联函数,从而减小导出符号表的大小,既能缩减文件的大小,还能提高运行性能,强烈建议你在编译任何共享库的时候使用该选项。

( 9 ) (9) (9)-minline-all-stringops

默认时GCC只将确定目的地会被对齐在至少4字节边界的字符串操作内联进程序代码。该选项启用更多的内联并且增加二进制文件的体积,但是可以提升依赖于高速 memcpy, strlen, memset 操作的程序的性能。

( 10 ) (10) (10)-m64

生成专门运行于 64 64 64位环境的代码,不能运行于 32 32 32位环境,仅用于 x 86 _ 64 x86\_64 x86_64[含 E M T 64 EMT64 EMT64]环境。

( 11 ) (11) (11)-fprefetch-loop-arrays

生成数组预读取指令,对于使用巨大数组的程序可以加快代码执行速度,适合数据库相关的大型软件等。具体效果如何取决于代码。不能和-Os一起使用。

( 12 ) (12) (12)-pipe

在编译过程的不同阶段之间使用管道而非临时文件进行通信,可以加快编译速度。建议使用。

4. 4. 4.推荐选项开关

综上,比较安全开关

    -pipe -O3(-Os) 
    -march=native 
    -mfpmath=sse 
    -msse2 -ftracer 
    -fivopts
    -ftree-loop-linear
    -fforce-addr

如果不需要多高的精度,比如GUI框架之类,加入

    -ffast-math

如果是编译的是共享库(. d l l dll dll,. a a a)加入

    -fvisibility=hidden
    -fvisibility-inlines-hidden

注意某些比赛不能使用 O 1 , O 2 , O 3 , O s O1,O2,O3,Os O1,O2,O3,Os

二、底层优化()

1. I / O I/O I/O优化

I / O I/O I/O优化是卡常中最常用的技巧,当数据较大的时候,读入输出占用了很多时间。

( 1 ) (1) (1)读入优化

流输入方式很方便,不需要记忆占位符,但每次读入时,它都要检测是否和stdin的同步(是否被freopen改变/是否被scanf读取),因此它是可以和scanf混用的。但也导致了它每次都要从数据开始位置跳转到当前读入的位置,浪费了大量时间,可以用std::ios::sync_with_stdio(false)关闭两者的同步以加快速度,这样做之后会比scanf还快!但必须注意,调用后不能再用freopen,但是还可以用fstream

当然,更好的方法是用getchar自己写读入函数。

inline void read(int &sum) 
{
   
    char ch=getchar();
    int tf=0;
    sum=0;
    while((ch<'0'||ch>'9')&&(ch!='-'))
        ch=getchar();
    tf=((ch=='-')&&(ch=getchar()));
    while(ch>='0'&&ch<='9')
        sum=sum*10+(ch-48),ch=getchar();
    (tf)&&(sum=-sum);
}

这样效率有了很大的提升,而且可以和scanf混用(字符串等),第7行和第10行的代码后面会说。

我们知道,getchar是逐字符读取的,在stdio.h中,有一个fread函数,能整段读取,比getchar还快,并且支持freopen(完美兼容)和fopen(需要把下面的所有stdin改成你的文件指针)

函数原型:size_t fread(void *buffer,size_t size,size_t count,FILE *stream);

作用:从stream中读取count个大小为size个字节的数据,放到数组buffer中,返回成功了多少个大小为size个字节的数据。

inline char nc() 
{
   
    static char buf[1000000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
}
//#define nc getchar
inline void read(int &sum) 
{
   
    char ch=nc();
    int tf=0;
    sum=0;
    while((ch<'0'||ch>'9')&&(ch!='-'))
        ch=nc();
    tf=((ch=='-')
  • 8
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值