OI中C++常数问题及其优化

21 篇文章 1 订阅

转载的其他大神的,感觉很受用.http://blog.csdn.net/leader_one/article/details/78430083

C++一些常数常见坑
  I/O读入和输出 
  如果量小倒也没什么,如果大规模读入或者输出,C++自带的方式是很慢的 
  ->首先,拒绝cin/cout,实在是太慢了,受不了 
  ->接着scanf/printf,较慢,中小规模是可以的,但是百万级的I/O常数影响就大了 
  ->所以考虑读入输出优化,百万级的I/O可以和scanf/printf相差0.X秒,和cin/cout则有几秒差距 
  以int类型为例
 

inline int read()
{
    int X=0,w=1; char ch=0;
    while(ch<'0' || ch>'9') {if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0' && ch<='9') X=(X<<3)+(X<<1)+ch-'0',ch=getchar();
    return X*w;
}

这是读入,利用getchar()来进行字符读入是比较快的 
输出同理,putchar()

inline void write(ll x)
{
    if(x==0){ putchar('0'),putchar('\n');return;}
    if(x<0){ putchar('-'),x = -x; }
    char s[22],l = 0;
    while(x!=0) s[++l] = x%10+48,x /= 10;
    fow(i,l,1) putchar(s[i]);
    putchar('\n'); //这里输出换行了,可以改
}

寻址

由于没有开O2优化,会导致一些本来没有区别的变得比较明显。 
多维数组把大的数放前面: 
例如 int f[10000][1000][100] 而不是 f[100][1000][10000],跑起来差距0.Xs。 
有时比算法的差距还大(开了O2后差别不明显)当然,比赛时一般没有O2,所以要注意–这是大坑


变量类型 
例如 int和long long 
int是4B的,32位,而long long是8B的,64位,所以在大规模运算时时间消耗上会有很大差别 
能用小的就尽量别用大的 
还有C++自带的string,常数实在是大,所以还是建议自己打char[ ]


C++自带STL 
如果条件允许,最好还是自己手动实现,因为C++自带的STL常数很迷,有时可能大得惊人甚至导致TLE 
所以,能自己手动实现最好还是自己写 
当然,如果时间不允许或者不会写,用用也没关系
 

位运算优化 
可以使用一些位运算来做一些运算的常数优化 
例如:

x*10 => (x<<3)+(x<<1) 
x!=y => x^y 
x!=-1 => ~x 
x*2 => x<<1 (其他2的幂数同理)
x*2+1 => x<<1|1 
x/2 => x>>1 
(x+1)%2 => x^1 
x%2 => x&1 
x%2==0 => ~(x&1)

函数/过程值得传递 
例如 int calc(int a,int b) 
如果只是传入一两个int、char什么的差距倒不明显,但如果是个string…显然就大了许多 
可以使用全局变量或者&(直接用地址)来优化

循环内的问题 

for(int i = 0; i <= n; i++)
{
  work1();
  work2();
}


for(int i = 0; i <= n; i++) work1();
for(int i = 0; i <= n; i++) work2();

运行效率哪一个快呢?

应该大多数人都觉得是第一种快,因为它少了一遍变量枚举- 
其实这是片面的,如果work1( )和work2( )运算量都比较大的话,是第二种更快 
这要由计算机的硬件说起。 
由于CPU只能从内存在读取数据,而CPU的运算速度远远大于内存,所以为了提高程序的运行速度有效地利用CPU的能力,在内存与CPU之间有一个叫Cache的存储器,它的速度接近CPU。而Cache中的数据是从内存中加载而来的,这个过程需要访问内存,速度较慢。

这里先说说Cache的设计原理,就是时间局部性和空间局部性。时间局部性是指如果一个存储单元被访问,则可能该单元会很快被再次访问,这是因为程序存在着循环。空间局部性是指如果一个储存单元被访问,则该单元邻近的单元也可能很快被访问,这是因为程序中大部分指令是顺序存储、顺序执行的,数据也一般也是以向量、数组、树、表等形式簇聚在一起的。

看到这里你可能已经明白其中的原因了。如果work1和work2的代码量很大,例如都大于Cache的容量,则在代码1中,就不能充分利用Cache了,因为每循环一次,都要把Cache中的内容踢出,重新从内存中加载另一个函数的代码指令和数据,而代码2则更很好地利用了Cache,利用两个循环语句,每个循环所用到的数据几乎都已加载到Cache中,每次循环都可从Cache中读写数据,访问内存较少,速度较快,理论上来说只需要完全踢出work1的数据1次即可。
 

局部变量和全局变量 
之前我也是一直觉得定义全局变量是要比定义局部变量要快的… 
其实不然,还是要从硬件设计说起

因为局部变量是存在于堆栈中的,对其空间的分配仅仅是修改一次寄存器的内容即可(即使定义一组局部变量也是修改一次)。而局部变量存在于堆栈中最大的好处是,函数能重复使用内存,当一个函数调用完毕时,退出程序堆栈,内存空间被回收,当新的函数被调用时,局部变量又可以重新使用相同的地址。当一块数据被反复读写,其数据会留在CPU的一级缓存(Cache)中,访问速度非常快。而静态变量却不存在于堆栈中。

全局变量是用类似于递归调用的方式实现的,所以可处理的东西更多一点,时间换空间嘛。

当然,大变量(例如大数组)还是全局定义吧,局部绝对是会炸的。
 

 

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值