《C++之那些年踩过的坑(附录一)》
C++之那些年踩过的坑(附录一)
作者:刘俊延(Alinshans)
本系列文章针对我在写C++代码的过程中,尤其是做自己的项目时,踩过的各种坑。以此作为给自己的警惕。
【版权声明】转载请注明原文来自:http://www.cnblogs.com/GodA/p/6639526.html
本来上个月就开始动笔了,直到现在才发出来,实在太多事情。可能有些小朋友不知道写这一篇随笔的起因,那么你可以看一下我之前写的。
上一篇的最后,我提到了一个问题:代码优化。并留了一个小测试:无符号数与有符号数的性能比较。不知道有没有人去实验。我做了一个简单的测试:
#include <iostream>
#include <chrono> int main()
{
/*unsigned */int a = ;
auto t1 = std::chrono::system_clock::now();
for (int i = ; i < ; ++i)
{
a += ;
a -= ;
a *= ;
a /= ;
}
auto t2 = std::chrono::system_clock::now();
auto runtime = std::chrono::duration_cast<std::chrono::nanoseconds>(t2 - t1);
std::cout << "a = " << a <<"\n";
std::cout << runtime.count() << "ns";
}
在上述代码在 VS 的 Debug 模式下运行(Release就优化掉了),稳定后运行时间在 2800ns,然后把注释去掉,再次运行,稳定后运行时间还是 2800ns。在我在电脑上,计算有符号类型和无符号类型几乎是没有差别的,我相信在绝大多数的电脑上也是相同的结果。
类似的还有浮点数的计算比整型数慢?对于这个问题,可以看一下这个问题:https://www.zhihu.com/question/26494062 。
首先我要声明:我不是反对代码优化。对于有很多流传广泛的所谓的优化技巧,我觉得我们应该应该抱着学习探索的心态,而不是一味地追求一些没有什么意义的东西。有些优化,确实很精妙。但很多所谓的技巧,看起来的意思就是:做编译器的那群人都是傻逼。想优化我们的程序,这是正常的、应该的想法,但我们应该用科学的方法,而不是听了一些奇淫技巧,却不知道里面发生了什么。
其实很简单,探究性能瓶颈靠 profiling,探究代码背后的不为人知的故事看 assembly。我们先讲后面一个。
我想大多数人还是在 Windows 下编程,所以用的肯定也是宇宙最强 IDE VS。在 VS 下看汇编很简单,随便设置一个断点,然后按调试下面的开始调试,然后在打开在调试下的窗口找到反汇编,就可以看了。比如我们研究有符号数和无符号数,先写一个程序:
int main()
{
/*unsigned */int a = ;
a = a / ;
std::cout << a;
}
然后按照刚刚的方法(在 Debug 下)看反汇编:
/*unsigned */int a = ;
00B4104E mov dword ptr [a],75BCD15h
a = a / ;
00B41055 mov eax,dword ptr [a]
00B41058 cdq
00B41059 mov ecx,0Dh
00B4105E idiv eax,ecx
00B41060 mov dword ptr [a],eax
然后在把注释去掉试试:
unsigned int a = ;
00C3104E mov dword ptr [a],75BCD15h
a = a / ;
00C31055 mov eax,dword ptr [a]
00C31058 xor edx,edx
00C3105A mov ecx,0Dh
00C3105F div eax,ecx
00C31061 mov dword ptr [a],eax
几乎是一模一样的,最大的差别就是有符号数使用 idiv指令(带符号除法),无符号数使用 div指令(不带符号除法),而这两种指令,CPU周期都是一样的。http://www.agner.org/optimize/instruction_tables.pdf。
当然我不是说不用无符号数,而是说我们用什么要看场合,而不是你觉得用了性能更好,除非是被大众认可的或者你经过严谨的测试的。像对于某些书籍或者什么地方说,只要确定范围不为负数的,就用无符号类型,我是不认可的。如果你讲范围,那如果一个有符号类型不够用,那么通常(相同bit下)它对应的无符号类型也不够用。比如你 int32_t 不够用,就应该用 int64_t,如果还不够,考虑写个 BigInteger类吧 :) 。不过对于无符号和有符号类型,它们之间的性能在当代确实是几乎没有什么差别。那具体什么场合用什么呢?这个也不一定,比如一般来说:
- 对于位储存、位运算、模运算等,使用无符号类型
- 对于一般运算使用有符号类型
其实我对于无符号有符号是觉得很那个什么的。。平时我们说一个数,要么就是整数,要么就是小数得了,还要去分有没有符号那真的是。不过因为是 C++ 所以也就没什么奇怪的了。
好像有点偏离我想说的主题了(拽回来
我并不是想专门讲这个什么有符号无符号,而是想借这个题引出(我的)一些看法:
- 过早的优化是万恶之源
- 不要试图帮编译器优化
- 优化时不要去猜测,想当然得去优化自己“觉得”性能差的地方
- 探究性能的瓶颈靠 profiling
我们一点一点讲。
对于一个需求,我们应该先完成功能,若性能达不到要求之后,在确定瓶颈之后再去优化。过早优化,不仅让代码不直接,还容易出bug,还可能对性能几乎没有影响。而且,我们优化时,应该关注大方向,确定大方向是正确的。比如写一个算法,我们首先应该确保 Big-O 的时间复杂度能达标,可以用 O(n) 的就不用 O(nlogn),可以用 O(nlogn) 的就不用 O(n²),而不是先在那里扣 i++ 还是 ++i。另外,不要想着去帮编译器优化,因为编译器是一堆比你强不知道多少的人写出来的,而对于一般人,想着去帮编译器优化,大部分是无效的,少部分是错的。比如有人学了一点点 std::move,就老是想着 move move move 去提高性能,举个栗子,(不同的)容易写出这样的代码:
template <typename T>
T create()
{
auto object = new T();
return std::move(object);
}
确实运行不会错,但是,代码背后做的事情不一定就跟你想的一样,往往跟你想象的还不一样。有些情况编译器可以采用更好的办法,结果因为你那么一搞,迫不得已只能采用次一点的办法。可以看看 这个问题,不赘述了。
还有比如说用异或来交换两个变量,有人就会想,用位运算,不仅不需要创建临时变量,而且位运算一般不是更快嘛!对于这个问题,陈硕大大早有讲到,在 https://cloud.github.com/downloads/chenshuo/documents/CppPractice.pdf 的第 9 章。要是没有看过的劳烦读者自行前往。
如果你已经看了上面的链接,那么你也就知道了,你(几乎)不会知道编译器做了什么,编译器可以做的优化超出你的想象(不过有的时候人能明显看出来的优化编译器却做不到,但影响不大),在我的系列文章(二)中也反复强调了,不要试图帮助编译器去优化。若你想探究一小段代码背后的细节,就去看反汇编吧!
然后关于探究性能瓶颈,我还是想贴上一个回答:https://www.zhihu.com/question/56727144/answer/150555866
最后我希望我放的链接都有认真看呀!为什么我贴那么多链接呢?因为我说的话没有权威没人信啊!大神说的总有一点参考价值了吧!(
好了,暂时写那么多先。
才学疏浅,如有不当地方还请海涵,感谢指点!
《C++之那些年踩过的坑(附录一)》的更多相关文章
- 简单物联网:外网访问内网路由器下树莓派Flask服务器
最近做一个小东西,大概过程就是想在教室,宿舍控制实验室的一些设备. 已经在树莓上搭了一个轻量的flask服务器,在实验室的路由器下,任何设备都是可以访问的:但是有一些限制条件,比如我想在宿舍控制我种花 ...
- 利用ssh反向代理以及autossh实现从外网连接内网服务器
前言 最近遇到这样一个问题,我在实验室架设了一台服务器,给师弟或者小伙伴练习Linux用,然后平时在实验室这边直接连接是没有问题的,都是内网嘛.但是回到宿舍问题出来了,使用校园网的童鞋还是能连接上,使 ...
- 外网访问内网Docker容器
外网访问内网Docker容器 本地安装了Docker容器,只能在局域网内访问,怎样从外网也能访问本地Docker容器? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Docker容器 ...
- 外网访问内网SpringBoot
外网访问内网SpringBoot 本地安装了SpringBoot,只能在局域网内访问,怎样从外网也能访问本地SpringBoot? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装Java 1 ...
- 外网访问内网Elasticsearch WEB
外网访问内网Elasticsearch WEB 本地安装了Elasticsearch,只能在局域网内访问其WEB,怎样从外网也能访问本地Elasticsearch? 本文将介绍具体的实现步骤. 1. ...
- 怎样从外网访问内网Rails
外网访问内网Rails 本地安装了Rails,只能在局域网内访问,怎样从外网也能访问本地Rails? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Rails 默认安装的Rails端口 ...
- 怎样从外网访问内网Memcached数据库
外网访问内网Memcached数据库 本地安装了Memcached数据库,只能在局域网内访问,怎样从外网也能访问本地Memcached数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装 ...
- 怎样从外网访问内网CouchDB数据库
外网访问内网CouchDB数据库 本地安装了CouchDB数据库,只能在局域网内访问,怎样从外网也能访问本地CouchDB数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Cou ...
- 怎样从外网访问内网DB2数据库
外网访问内网DB2数据库 本地安装了DB2数据库,只能在局域网内访问,怎样从外网也能访问本地DB2数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动DB2数据库 默认安装的DB2 ...
- 怎样从外网访问内网OpenLDAP数据库
外网访问内网OpenLDAP数据库 本地安装了OpenLDAP数据库,只能在局域网内访问,怎样从外网也能访问本地OpenLDAP数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动 ...
随机推荐
- Struts文件下载
/* 文件下载的先决条件 * 1. 在xml配置文件中必须配置一个type="stream"的result, result中不需要填写任何内容 * 2. 在Action中编写一个接 ...
- 3314: [Usaco2013 Nov]Crowded Cows
3314: [Usaco2013 Nov]Crowded Cows Time Limit: 1 Sec Memory Limit: 128 MBSubmit: 111 Solved: 79[Sub ...
- 3298: [USACO 2011Open]cow checkers
3298: [USACO 2011Open]cow checkers Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 65 Solved: 26[Su ...
- 3361: [Usaco2004 Jan]培根距离
3361: [Usaco2004 Jan]培根距离 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 16 Solved: 13[Submit][Sta ...
- swift -- 类中的方法
一. 引用类型 类 在类中定义方法 class Person { //属性 var name : String = "" //方法 //实例方法 : 在类里面创建一个方法 fun ...
- Android 快速开发系列 ORMLite 框架最佳实践
比较靠谱的Helper的写法: 1.DatabaseHelper package com.zhy.zhy_ormlite.db; import java.sql.SQLException; impor ...
- 重磅消息:微信小程序支持长按二维码进入
之前微信小程序一般通过以下入口进入: 而用户经常使用“长按二维码”识别应用的功能一直未开放,据酷客多了解,微信安卓6.5.6内测版已经支持长按二维码识别和进入小程序,意味着把小程序二维码分享给朋友,或 ...
- SharePoint 切换用户的小技巧
前言 从SharePoint 2013开始,SharePoint就已经去掉了”Sign in as Different User”这个功能,也就是无法切换用户登录.当然,后来我们通过修改CONTROL ...
- iOS开发之UIPopoverController
1.概述 是iPad开发中常见的一种控制器(在iPhone上不允许使用),跟其他控制器不一样的是,它直接继承自NSObject,并非继承自UIViewController,它只占用部分屏幕空间来呈现信 ...
- 【react-router】从Link组件和a标签的区别说起,react-router如何实现导航并优化DOM性能?
(注:参考自官方英文文档V3.X版本) react-router是伴随着react框架出现的路由系统,它也是公认的一种优秀的路由解决方案.在使用react-router时候,我们常常会使用其自带的路径 ...