编程中用移位运算替代乘除法会不会有问题?

2018-07-10 14:40:33 +08:00
 jssyxzy

因为移位运算实际上有算术移位,逻辑移位,循环移位;
又有有符号数,无符号数;
不同编程语言有可能实现不同。

请问这里面有没有什么坑?

10465 次点击
所在节点    程序员
80 条回复
shijingshijing
2018-07-11 10:05:03 +08:00
@shijingshijing 爪机打字错误百出,修订:
输入入门级 -> 属于入门级
见过很 PIC -> 见过在 PIC

补充,1,PIC 这种单片机用位移做乘法也是有编译器或者库可以用的,真没必要手写。
2,FMA 指令不仅能做乘法,其实除法也是用 FMA 来做的,A/B 实际上是把被除数 B 先取倒数,然后再与 A 乘。

多了解一下现代处理器的 ISA 吧
miaomiaoweiwei
2018-07-11 12:03:28 +08:00
强行装逼不可取 哈哈
billwsy
2018-07-11 12:22:48 +08:00
@shijingshijing fma 一个周期取结果真的么…
billwsy
2018-07-11 12:28:26 +08:00
@tempdban 固定值的变量的话会被 CSE common subedpression elimination 优化掉…

除非是从 caller 传进来或者是全局变量…那真的不怕万一被改了?怕被改了就加个 if 判断,然后 CSE 继续给优化了…

当然得承认有些技巧还是有用的…
shijingshijing
2018-07-11 12:43:07 +08:00
@billwsy 嗯,这个是我的疏忽,我的意思你懂的,一条指令搞定的基本上都是硬件级的,远比自己软件折腾来的快得多和靠谱的多。

用位移替代乘除,我能想到的就两种情形:
1,片上资源紧张,又需要用到乘除而且没精度要求;

2,大数的乘除。
shijingshijing
2018-07-11 12:46:57 +08:00
@imn1 你这个其实一条按位与操作就行了:

$num & 10000000b

返回结果 true or false
xuboying
2018-07-11 12:55:27 +08:00
楼主非要强行装就很无聊了,你说是科学问题,那么就把位移和乘法除法等效的过程证明一遍就好了,然后你用不同的语言实现你的证明过程就行了,然后就可以结贴了。
自己有又想编译器优化,那么就看汇编怎么实现乘法除法就好了
jssyxzy
2018-07-11 14:40:30 +08:00
题目描述有歧义,把标题 "编程中" 换成 “理论上”。
Cu635
2018-07-11 15:00:57 +08:00
@redsonic
不懂不要乱说。
“至于动态语言里面就变装逼”谁都没说,说的理由其实就是 C 语言编译器的优化,至少 gcc 是会做优化的,乘除法能优化的就优化到位运算。

@jssyxzy
你再看看自己发的帖子是啥,是怎么描述的。
就是因为“这个确实很多方面要用到,比如编译器优化”,既然编译器已经帮助你优化了,自然的你在编程过程中就不需要费事了。
单片机、嵌入式这种当然还是需要的。

@tempdban
“都没考虑 第一个操作数 是变量 第二个操作数是个只要程序运行就能确定下来的变量
也就是说第二个变量初始化后就不会变。 ”
这种情况下,编译器同样能够优化,只不过可能不是默认行为,而是要由你来配置编译参数,或者不是编译器所有版本都支持,需要你有意识的来选择一个版本比较新的。

“像我们这种一个任务需要执行几个指令周期都要细扣的领域,编译器的优化甚至有可能产生副作用。 ”
这是搞单片机还是嵌入式的?

@chinvo
“或者大兄弟你还在用“先进的” VC++ 6.0 ?”
呃,现在 VS 花里胡哨的太多了,VC++6.0 毕竟功能简单一些,作为入门比较合适……

@imn1
“例如: $num >> 7 & 1
我目的是要知道$num 第八位是 1 还是 0,实际意义就是第八个权限:[ 有 | 无 ]”
这个时候难道不是用掩码?

@shijingshijing
问题就是,tempdban 老兄的开发环境不一定是新版本编译器啊,那种单片机嵌入式的开发环境很多版本都是非常非常老的……
mooncakejs
2018-07-11 15:04:02 +08:00
@rabbbit 据我所知,c 系语音,32 位整数都是这个结果。
CoderGeek
2018-07-11 15:18:36 +08:00
用来做标志位的时候会用到 比如一些开关
0 1 2 4 8 16 32 64 代表每个打开状态
CoderGeek
2018-07-11 15:20:26 +08:00
int[] code = { 0, 1 << 0, 1 << 1, 1 << 2, 1 << 3, 1 << 4 }
后期做&判断啥的 = =
imn1
2018-07-11 15:51:27 +08:00
@shijingshijing
@Cu635
我知道,但这样直观
序号 19 就 >> 19-1,不用去算掩码
firebroo
2018-07-11 19:19:44 +08:00
我觉得看啥场景吧。。
当然现在的编译器真心优化的挺牛逼的。。之前看过一点 https://www.firebroo.com/categorys/3cb6da3228424ff7c02df7ad7f8cfd3acc9265ee12f515ee875f075f03edbfbc/articles/7bd1dee60330ecaddf114cfb93528054a65ed1e4c8250ce37bf9d1d05a1f92ad
就好比大家都觉得写汇编会比写 c 效率高,但是如果不是精通汇编,我觉得优化不过编译器。。
tempdban
2018-07-11 21:34:24 +08:00
@Cu635 这个值不是每次上电初始化都不一样,但是一旦初始化了在整个运行时都不变。这样编译器也会优化么?
我只是对最大可能的值做了一个单独的分支 进来这个分支就死循环住,因为能进这个分支就说明这个值是确定的,这个值一确定,就有很多东西能优化成常量位移操作,进而编译器优化掉。
是做数据面转发的
yanaraika
2018-07-11 21:37:45 +08:00
除非你知道编译器不知道的信息 /能做编译器不能做的语义修改 /特殊的向量指令,否则能打得过 clang 6.0 -O3 -flto + PGO + ipa-pta 算我输
tempdban
2018-07-11 22:00:59 +08:00
@shijingshijing

@billwsy
首先还是给点了个感谢

比如 a % b
这个 b 是在程序初始化中经过统计得来的,但是一旦 b == 8 呢
我做的只是
If b == 8
while (1) a & 7
else
while (1) a % b

这种操作一个两个还好,可是如果有大量的呢
我是反汇编加 vtune 性能采集对过的,这么写对我来讲效果很好,而且实际情况选比我们在这讨论来的复杂 比如说我们只能开 O2 O3 上来就会段错误。
我们的编译器确实已经很老了,连内核都是 2.6.x 的。
这东西谁敢说换,一旦换了是不是要投入很多人?

我们调了半年的代码,就为了让流水线打满,可别让我再了解 ISA 了,了解不动了兄弟。
billwsy
2018-07-11 22:40:58 +08:00
@tempdban 感谢!这倒是一个很不错的例子,看看做编译器的人能不能挖一挖,说不定是篇很好的 paper 呢
Raymon111111
2018-07-11 22:57:07 +08:00
带来不了足够的性能优势

写这种代码, 可读性第一

如果你认为能显著带来性能上优化, 给出报告再说吧
yanaraika
2018-07-11 23:00:15 +08:00
@tempdban O3 段错误的代码是怎么敢上线的……

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/469591

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX