P.S. 感谢@ComeIntoPower 提供了宝贵的修改意见。

OI中C++的常数优化详解

Part1 寄存器与cache

内存的访问是非常慢的,除了内存,还有寄存器(register)、高速缓存cache,它们的访问速度比内存更快。

电脑的存储分级可以用一个非常形象的栗子来说明:
我们可以以经典的阅读书籍为例。我在读的书,捧在手里(寄存器),我最近频繁阅读的书,放在书桌上(缓存),随时取来读。当然书桌上只能放有限几本书。我更多的书在书架上(内存)。如果书架上没有的书,就去图书馆(磁盘)。我要读的书如果手里没有,那么去书桌上找,如果书桌上没有,去书架上找,如果书架上没有去图书馆去找。可以对应寄存器没有,则从缓存中取,缓存中没有,则从内存中取到缓存,如果内存中没有,则先从磁盘读入内存,再读入缓存,再读入寄存器。

这是一张有两级cache的计算机的存储结构图。

寄存器具有最高的访问速度,在变量前加关键词register即将其加入寄存器。但如图,寄存器的空间是有限的,不应该滥用register,应该仅在访问最频繁的几个变量(如循环变量)前加register。

cache即高速缓存,一般分为3级(有些电脑为两级),访问速度逐级递减。访问变量时,CPU会优先在cache而不是内存中查找,如果cache中不存在此变量,则会进入内存查找,这称为cache miss。如图,内存访问的开销是巨大的,所以cache miss是一个重要的常数问题。

那么如何减少cache miss?

对于cache miss优化,有如下几点:

尽量让某个数组的大小能够卡进cache

与register一样,cache的大小同样有限。一些过大的内存是不可以进入cache的。

基数排序时,以256为基数会比256*256更快。因为256大小的四个数组可以轻松进入cache。

详见 洛谷题库 WC2017挑战 Subtask1

保证时空局部性

什么是时空局部性?

时间局部性:当一个变量被使用时,它会在短时间内再次被使用。
空间局部性:当一个变量被使用时,它的内存附近的变量会再次被使用。

保证这两样东西的良好有益于减少cache miss。

怎样优化空间局部性?

  • 将一些关系密切,例如经常连着使用的变量尽量定义在一起,或用结构体封装起来。
  • 适当调整变量定义顺序
  • 保证内存连续访问。例如:Floyd和矩阵乘法的程序中,将第三层循环作为第一层会大大提高速度。

怎样优化时间局部性?

  • 尽量使用局部变量。因为堆栈的数据访问十分频繁。

指令缓存

同样是空间局部性的原理,两个相互关联(例如调用对方)的函数应该定义得足够靠近,这能使它们有机会同时被加载到指令cache中。

cache的生活应用:DevC++编译程序后第一遍运行很慢,这是因为编译后的程序没有进入缓存。运行一次后,相关指令进入cache,就会提高运行速度。同样地,对程序进行多次测速求平均时,如果程序访问到了一些大数组,且它们之前没有进入cache,则应该忽略掉第一次运行,取i=2到n次的一段进行平均。

总结:cache优化的原则:紧凑有关联的代码,分离无关联的部分。

摘自骆爷pdf的一句话:“这也是编写优美代码的原则。”

Part2 指针优化数组连续访问

指针用法:

for(register int i=1;i<=n;++i)work(a[i]);
应化为for(register int *S=a,*E=a+n;S!=E;)work(*++S);

这样能够大幅度提高速度。为啥呢?
a[i]本质上是*(a+i),64位平台上是long long相加,比指针的前自加运算慢得多。
注:初始化和数组复制方面,memset和memcpy比指针具有更高的速度,因为它们实际上调用了rep movsq指令,这比一般的mov指令快很多。

对于高维数组,例如inta[d][h][w]int a[d][h][w]inta[d][h][w],则a[i][j][k]a[i][j][k]a[i][j][k]的访问相对而言十分慢。我们可以将它降成一维数组,用a[i∗h∗w+j∗w+k]a[i*h*w+j*w+k]a[i∗h∗w+j∗w+k]代替,从而更快地访问。另外也可以利用代数方法减少乘法次数,将其化为a[(i∗h+j)∗w+k]a[(i*h+j)*w+k]a[(i∗h+j)∗w+k]。更高维的数组也可以用这种方法优化。

更多内容请参考:
http://nadeausoftware.com/articles/2012/06/c_c_tip_how_loop_through_multi_dimensional_arrays_quickly

Part3 内联函数、递归、递推与堆栈开销

inline函数能够提高效率,因为它能够减少堆栈开销、减少传参耗时。

#define宏函数与inline函数具有相同速度和效果,但会在函数体中反复计算参数,这当参数是一个式子时是很不利的。请读者根据具体情况自行选择。
例:#define sq(x) ((x)*(x))//平方函数

同样的道理,递推比递归更优秀。它减少了堆栈开销,会更快速,同时避免了爆栈的危险。
同样地,手动bfs比dfs更为高效,且内存开销也更小,尤其是多次dfs一棵树时。

Part4 优化STL的动态分配内存

一些STL的速度瓶颈即std::allocator对内存的动态分配,这对于push_back等操作不利。

我们可以手写这个struct,用足够大小的内存池来代替动态分配内存。这里我们用派生于std::allocator的myalloc结构体来代替它:

#include<bits/stdc++.h>
using namespace std;
#define reg register
static char space[10000000],*sp=space;
template<typename T>
struct myalloc:allocator<T>{myalloc(){}template<typename T2>myalloc(const myalloc<T2> &a){}template<typename T2>myalloc<T>& operator=(const myalloc<T2> &a){return *this;}template<typename T2>struct rebind{typedef myalloc<T2> other;};inline T* allocate(size_t n){T *result=(T*)sp;sp+=n*sizeof(T);return result;}inline void deallocate(T* p,size_t n){}
};

完成后,即可这样定义STL容器:

list<int,myalloc<int> > L;vector<double,myalloc<double> > vec;

实测表明,这确实能够优化STL相当一大部分的常数。

由于为了竞赛中方便打,该模板没有编写内存释放函数(网上的模板十分冗长),因此,当内存过大时不要用此模板,太大会RE,不太大也可能变慢。

如果平时想要使用这一类的优化,请参考这个十分详细的内存池教程:https://blog.csdn.net/u010183728/article/details/81531392

觉得不错请留个赞奥

C++卡常数之内存优化相关推荐

  1. 关于android性能,内存优化 http://www.cnblogs.com/zyw-205520/archive/2013/02/17/2914190.html

     随着技术的发展,智能手机硬件配置越来越高,可是它和现在的PC相比,其运算能力,续航能力,存储空间等都还是受到很大的限制,同时用户对手机的体验要  求远远高于PC的桌面应用程序.以上理由,足以需要 ...

  2. Android之——性能与内存优化

    转载请注明出处:http://blog.csdn.net/l1028386804/article/details/46987951 写出高效代码的两条基本的原则:(1)不要做不必要的事:(2)不要分配 ...

  3. android开发内存优化的那些事儿

    一.Android应用程序内存优化   在开发Android App的过程中,经常会遇到内存方面的压力,比如OOM,或者频繁GC.本文不打算涵盖内存优化的所有方面,只是介绍一下我自己遇到的问题和解决方 ...

  4. Android优化之内存优化倒计时篇

    本文来自网易云社区 作者:聂雷震 本篇文章介绍的内容是如何在安卓手机上实现高效的倒计时效果,这个高效有两个标准:1.刷新频率足够高,让用户觉得这个倒计时的确是倒计时,而不是幻灯片:2.不能占用太多的内 ...

  5. ANDROID内存优化(大汇总——中)

    转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持! 写在最前: 本文的思路主要借鉴了2014年AnDevCon开发者大会的一个演讲PPT,加上 ...

  6. 【Android 内存优化】Bitmap 硬盘缓存 ( Google 官方 Bitmap 示例 | DiskLruCache 开源库 | 代码示例 )

    文章目录 一.Google 官方 Bitmap 相关示例参考 二.磁盘缓存类 DiskLruCache 三.磁盘缓存初始化 四.存储数据到磁盘缓存中 五.从磁盘缓存中读取数据 六. Android 1 ...

  7. 【Android 内存优化】Android 原生 API 图片压缩代码示例 ( PNG 格式压缩 | JPEG 格式压缩 | WEBP 格式压缩 | 动态权限申请 | Android10 存储策略 )

    文章目录 一. 图片质量压缩 二. 图片尺寸压缩 三. Android 10 文件访问 四. 完整源码示例 上一篇博客 [Android 内存优化]图片文件压缩 ( Android 原生 API 提供 ...

  8. 【Android 内存优化】垃圾回收算法 ( 内存优化总结 | 常见的内存泄漏场景 | GC 算法 | 标记清除算法 | 复制算法 | 标记压缩算法 )

    文章目录 一. 内存优化总结 二. 常见的内存泄漏场景 三. 内存回收算法 四. 标记-清除算法 ( mark-sweep ) 五. 复制算法 六. 标记-压缩算法 一. 内存优化总结 内存泄漏原理 ...

  9. Android性能优化 - 内存优化

    性能优化系列阅读 Android性能优化 性能优化 - 消除卡顿 性能优化- 内存优化 性能分析工具 - TraceView Android性能分析工具 为什么内存优化? 在一个商业项目中,很有可能因 ...

  10. Android的内存优化的几种方案

    相信一步步走过来的Android从业者,每个人都会遇到OOM的情况.如何避免和防范OOM的出现,对于每一个程序员来说确实是一门必不可少的能力.今天我们就谈谈在Android平台下内存的管理之道,开始今 ...

最新文章

  1. 【Codeforces】1136C Nastya Is Transposing Matrices (矩阵转置)
  2. 解决使用mybatis分页插件PageHelper的一个报错问题
  3. 工具04_SQL Trace/DBMS_SYSTEM
  4. 【Android】Uri、UriMatcher、ContentUris详解
  5. C++shell排序(附完整源码)
  6. 每日一笑 | 在托运行李时,怎样才能不会因为超重被罚钱?
  7. ubuntu开机出现错误“Error found when loading /root/.profile”解决
  8. HTML中id和name的区别(js中的注意事项)
  9. ctbs mysql_C/C++/Java
  10. canvas 画正方形和圆形
  11. Linux如何不格式化挂载硬盘,linux下格式化硬盘与挂载硬盘
  12. python爬大学生就业数据分析_Python 网络爬虫数据分析实战
  13. 使用maven编译打包用javac还是eclipse的jdt的问题
  14. C++学习笔记 C++11 std::chrono知识
  15. gitlab备份与恢复
  16. 关于迅盘Turbo Memory的ReadyDrive功能被禁用的思考
  17. 智能电子表格,为你轻松制作财务报表
  18. vulnhub靶场之 LordOfTheRoot_1.0.1
  19. n核CPU为什么计算速度达不到单核n倍
  20. 92天倒计时,蓝桥杯省赛备赛攻略来啦~

热门文章

  1. 《Using OpenRefine》翻译~14
  2. shell 脚本教程 入门级
  3. 如何做一个阿里云物联网安卓原生APP
  4. 【微机原理与接口技术学习实践】汇编语言程序设计实现——.ASM文件、.OBJ文件、.EXE文件综合
  5. IDEA使用有道翻译插件
  6. linux4755代表什么权限,CentOS下chmod 755和4755的区别是什么?
  7. 自制hdmi线一头改vga图_VGA连接线接口定义及引线焊接教程,VGA线不够长时可用网线代替?...
  8. Python学习资料篇
  9. 网页链接在线提取工具-免费网页链接在线提取软件
  10. 企业选择WMS仓库管理系统免费版是否更好