设为首页收藏本站|繁體中文

Project1

 找回密码
 注册会员
搜索
查看: 3260|回复: 10
打印 上一主题 下一主题

[胡扯] 编译器。编译器?编译器!

[复制链接]

Lv3.寻梦者

梦石
0
星屑
1803
在线时间
133 小时
注册时间
2013-10-6
帖子
193
跳转到指定楼层
1
发表于 2018-3-4 22:05:27 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式

加入我们,或者,欢迎回来。

您需要 登录 才可以下载或查看,没有帐号?注册会员

x
本帖最后由 不死鸟之翼 于 2018-3-4 22:05 编辑

今天刷知乎又看到有人在纠结写C++要不要把乘法换成移位之类的,美名其曰“优化”…我比编译器聪明系列
其实,通常编译器产生的优化机器码比人的自作聪明的产物还要快…
所以,还是老老实实地用最原始的写法吧

这里大概说说C++函数返回值的事情。

在很多语言比如Java中,在函数中创建一个对象并返回的时候通常是引用语义,也就是返回了局部对象的引用。
但是C++中直接return一个对象是值语义的,也就是说:
假设一个巨大的类型叫kls,我们写
CPP 代码复制
  1. kls get_cls() {
  2.         kls k;
  3.         return k;
  4. }
  5. kls x=get_kls();

的时候,从语法上是将局部变量k复制给了x。显然,如果kls很大的话,复制对象的开销也会很大。
避免开销的方法当然就是避免无谓的复制。
理论上C++也是可以返回“引用”的(把返回类型改为kls&),但是由于C++对象生存期的限制,函数结束之后,栈上的k会被销毁
尝试使用返回的无效的k引用会导致未定义行为

一种选择是把k构造在堆上,然后返回指针
CPP 代码复制
  1. kls* get_cls() {
  2.         kls* k = new kls;
  3.         return k;
  4. }

这样函数结束后,对象会继续存在。但是C++不像Java/Ruby它们有垃圾回收,所以需要手动delete掉返回的对象,否则内存泄漏

为了避免手动管理内存的麻烦,我们可以利用C++的确定性析构,使用带引用计数的智能指针
CPP 代码复制
  1. shared_ptr<kls> get_cls() {
  2.         return make_shared<kls>();
  3. }
  4. auto ptr = get_cls();

智能指针之间的赋值操作会增加kls对象的引用计数,当指针本身超出生存期后(例如,局部作用域)由于C++的确定性析构,智能指针会被立刻销毁(Java、Ruby等语言是无法控制对象在何时被销毁的,而由垃圾回收控制)
当没有智能指针指向对象后,它会被自动delete掉

那教练,有没有…不要指针的操作?我喜欢值语义,对象摆在这才安心嘛

有。现代C++多了一种引用,叫右值引用。右值的定义很复杂,但简单地说,它是指计算中产生的临时对象,通常无法用一个实体来指代它(例如c="a"+"b"的运算,字符串"ab"在赋值给c以前是作为右值)
所以,对于即将消逝的右值(亡值),我们可以在对象复制发生的时候,将它内部持有的资源“偷窃”或者说“转移”到新对象里,这样就避免了巨大的复制开销,而右值将成为一个空壳,最后被销毁。

我们编写以下代码,重载了两个复制构造函数(用来自定义对象复制时的操作),在MSVC的Debug模式下(关闭编译器优化),能够观察到一些输出。

代码复制
  1. 0 construction
  2. 1 copy construction from rvalue ref 0
  3. 0 destruction
  4. 1 destruction

get_cls中的临时对象0持有的数据可以在main中的1的构造时被转移,然后0在函数结束后被销毁。最后,main结束后1也被销毁。
虽然避免了一些复制开销,但是还是要再创建一个对象啊…

然后,我们切换到MSVC的Release模式,允许编译器优化。然后再运行一遍…
代码复制
  1. 0 construction
  2. 0 destruction

可以看到,编译器应用了“复制消除”(copy elision)优化。整个过程中只有一个对象。

所以,与其想那么多,为什么不放心地交给编译器呢: )
←你看到一只经常潜水的萌新。

Lv3.寻梦者

梦石
0
星屑
2512
在线时间
215 小时
注册时间
2017-9-27
帖子
613
2
发表于 2018-3-4 22:19:14 | 只看该作者
大佬大佬
回复 支持 反对

使用道具 举报

Lv4.逐梦者 (版主)

梦石
0
星屑
6901
在线时间
7028 小时
注册时间
2013-11-2
帖子
1344

开拓者剧作品鉴家

3
发表于 2018-3-4 22:26:52 | 只看该作者
现在都2018年了,C++17都出了,而且也会不停地进化(下一个貌似是C++20?)…
还是交给编译器吧。谁知道你现在想到的优化会不会下一更新就加到编译器里了呢。

点评

京紫元年  发表于 2018-3-5 09:22
不是8102年么  发表于 2018-3-5 09:18
回复 支持 反对

使用道具 举报

Lv3.寻梦者

孤独守望

梦石
0
星屑
3132
在线时间
1535 小时
注册时间
2006-10-16
帖子
4321

开拓者贵宾

4
发表于 2018-3-4 23:09:33 | 只看该作者
但是我看到Rust的借-还机制第一反应还是溜了溜了
菩提本非树,明镜本非台。回头自望路漫漫。不求姻缘,但求再见。
本来无一物,何处惹尘埃。风打浪吹雨不来。荒庭遍野,扶摇难接。
不知道多久更新一次的博客
回复 支持 反对

使用道具 举报

Lv5.捕梦者 (版主)

遠航の猫咪

梦石
3
星屑
23186
在线时间
2387 小时
注册时间
2005-10-15
帖子
1166

开拓者

5
发表于 2018-3-5 02:35:20 | 只看该作者
可是在RGSS里面x**2是比x*x慢,x*2也确实是比x<<1慢,x<<1又比x+x慢……
更不要提x/2比x>>1慢的不是一点儿半点儿的问题

这不是胡扯,这是实际测试的结果

点评

因为ruby是解释不是编译啊 编译器的优化是真的让人放心  发表于 2018-3-5 10:14
SailCat (小猫子·要开心一点) 共上站 24 次,发表过 11 篇文章 上 次 在: [2006年01月28日11:41:18 星期六] 从 [162.105.120.91] 到本站一游。
回复 支持 反对

使用道具 举报

Lv5.捕梦者 (管理员)

老黄鸡

梦石
0
星屑
42387
在线时间
7601 小时
注册时间
2009-7-6
帖子
13506

开拓者贵宾

6
发表于 2018-3-5 07:03:28 | 只看该作者
单从乘法和位移两种方法来看是位移要快不少,不过现在的编译器都会自动根据情况优化成
更快的一种,除非你是写内嵌asm否则基本不用考虑这个。
RGDirect - DirectX驱动的RGSS,点我了解.
RM全系列成套系统定制请联系QQ1213237796
不接受对其他插件维护的委托
回复 支持 反对

使用道具 举报

头像被屏蔽

Lv2.观梦者 (禁止发言)

梦石
0
星屑
653
在线时间
3774 小时
注册时间
2011-2-26
帖子
1839

开拓者

7
发表于 2018-3-5 10:16:37 | 只看该作者
提示: 作者被禁止或删除 内容自动屏蔽
签名被屏蔽
回复 支持 反对

使用道具 举报

Lv1.梦旅人

梦石
0
星屑
169
在线时间
53 小时
注册时间
2015-8-24
帖子
12

开拓者

8
发表于 2018-3-5 20:39:24 | 只看该作者
反正信息学竞赛的选手们还是会研究各种常数优化法
“位移比四则运算快”“++i比i++快”之类
不要跟我解释我不听qwq
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册会员

本版积分规则

拿上你的纸笔,建造一个属于你的梦想世界,加入吧。
 注册会员
找回密码

站长信箱:[email protected]|手机版|小黑屋|无图版|Project1游戏制作

GMT+8, 2024-11-21 21:48

Powered by Discuz! X3.1

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表