- 浏览: 3048143 次
- 性别:
- 来自: 海外
文章分类
- 全部博客 (430)
- Programming Languages (23)
- Compiler (20)
- Virtual Machine (57)
- Garbage Collection (4)
- HotSpot VM (26)
- Mono (2)
- SSCLI Rotor (1)
- Harmony (0)
- DLR (19)
- Ruby (28)
- C# (38)
- F# (3)
- Haskell (0)
- Scheme (1)
- Regular Expression (5)
- Python (4)
- ECMAScript (2)
- JavaScript (18)
- ActionScript (7)
- Squirrel (2)
- C (6)
- C++ (10)
- D (2)
- .NET (13)
- Java (86)
- Scala (1)
- Groovy (3)
- Optimization (6)
- Data Structure and Algorithm (3)
- Books (4)
- WPF (1)
- Game Engines (7)
- 吉里吉里 (12)
- UML (1)
- Reverse Engineering (11)
- NSIS (4)
- Utilities (3)
- Design Patterns (1)
- Visual Studio (9)
- Windows 7 (3)
- x86 Assembler (1)
- Android (2)
- School Assignment / Test (6)
- Anti-virus (1)
- REST (1)
- Profiling (1)
- misc (39)
- NetOA (12)
- rant (6)
- anime (5)
- Links (12)
- CLR (7)
- GC (1)
- OpenJDK (2)
- JVM (4)
- KVM (0)
- Rhino (1)
- LINQ (2)
- JScript (0)
- Nashorn (0)
- Dalvik (1)
- DTrace (0)
- LLVM (0)
- MSIL (0)
最新评论
-
mldxs:
虽然很多还是看不懂,写的很好!
虚拟机随谈(一):解释器,树遍历解释器,基于栈与基于寄存器,大杂烩 -
HanyuKing:
Java的多维数组 -
funnyone:
Java 8的default method与method resolution -
ljs_nogard:
Xamarin workbook - .Net Core 中不 ...
LINQ的恶搞…… -
txm119161336:
allocatestlye1 顺序为 // Fields o ...
最近做的两次Java/JVM分享的概要
嗯,在RPGCHINA读帖的时候看到一个有趣的主题,说Ruby的a = a + 1与a += 1的执行效率不一样。很明显这个认识有偏差,事实上Ruby的复合赋值运算符与其展开的简单赋值形式在经过解释器前端的解析后就一模一样了。连对应的抽象语法树都是一样的,执行效率能差多少呢?
更糟糕的是回帖中有错误的解释,将这个“差异”对应到汇编上:
这不是答非所问么……那帖的主题明明是在讨论a += 1与a = a + 1的差异;+=与++又不等价,况且Ruby里连++运算符都没有 =_=|||
诶,所以说不知道就不要乱说嗯。这MOV EBX, 1在运算支持立即数的指令集里明显是废的。
编译器能做的优化多的是。偏偏这a = a + 1却没什么优化可做。假如这是C代码生成为x86的目标代码,编译器选择ADD r32, #1还是INC r32与这个语句写成a = a + 1还是a += 1没什么关系……要说跟a++或者++a扯上关系那还靠谱点,不过如果是好的优化编译器都应该能生成一样的代码。
这复合赋值运算符最大的意义在于:1、让源代码更加简洁易读;2、减少重复的地址计算。之所以a = a + 1与a += 1无论如何也差不了多少是因为a本身就已经是一个变量了,能“直接”访问。假如这是一个需要昂贵的地址计算的表达式,那复合赋值运算符就显得很有意义了。例如这样:
这里要赋值的目标的地址无法直接得到,得经过复杂的计算。这种情况下如果编译器不优化、又或者计算地址有副作用,则取值和赋值分开两次来计算地址就比只计算一次要慢一些,因而有优化的必要。
原帖地址:http://www.rpgchina.net/read-htm-tid-32257.html
要讨论这种问题果然还是得研究一下MRI到底是如何实现这些东西的。
YARV的实现机制比较不同,而且1.9.0这个实验系列何时才会进化到稳定版的2.0.x还很难说,所以还是拿1.8.x系列为准来讨论了。
===========================================================================
下面内容是我在那帖14楼的回复:
这个……事情要具体问题具体分析对吧,张冠李戴就不太好了 ^ ^
+=之类的复合赋值运算符在许多语言都有,语义类似但是实现的方式并不总相同。
RGSS里的脚本语言是Ruby,RPG Maker VX里使用的Ruby是1.8.1版的MRI。Ruby源代码并没有被直接编译到机器码,而是被Ruby解释器所解释:先把源代码翻译成抽象语法树,然后解释抽象语法树。
在Ruby里,一切皆是对象。因此像是加号减号之类的运算,也被看作是对象上的方法。a += 1的语义是a = a.+(1)(语义是:调用a对象上的+()方法,把1作为参数传进去,然后将方法的返回值赋值给a。更准确的说,是对右手边的a对象发送了一个"+"消息,以1为参数;返回得到的值赋值给左手边的a)。
+=的语义不是单独定义,而是由+()方法所决定的;换句话说一个类定义了+()方法就自动具备了+=。假如有语句a = 1,那么a是一个Fixnum,+=当中调用的+()方法就是Fixnum#+()。
先看看"+="这个符号被解析器识别为什么了。Ruby的扫描器(词法分析器)里有这么一段:
parse.y
可以看到+=被识别为tOP_ASGN类型的token。
a += 1形式的语句对应的这条语法:
语法对应着解析器(语法分析器),而解析器会生成抽象语法树。如果等号前的是||则语法生成NODE_OP_ASGN_OR节点,如果是&&则生成NODE_OP_ASGN_AND节点,其它则调用call_op()函数生成NODE_CALL节点。
parse.y
由于节点的“值”(nd_value)被赋值为一个NODE_CALL节点,这里实质上完成了将a += 1变为a = a.+(1)的转换。
看看a = a + 1对应的语法和动作:
parse.y
结合下面node_assign()函数的实现,可以看到这里是把右手边的节点赋值给了左手边节点的“值”(nd_value)。并且,右手边的a + 1对应的语法与动作如下:
parse.y
也是调用call_op()生成NODE_CALL节点,跟前面a += 1时一样。
于是,a += 1与a = a + 1在被解析后所生成的语法树是一样的,后续执行中就都是等价的了。
parse.y
(nd_value是在node.h里定义的一个宏,展开为u2.node)
===========================================================================
这是Fixnum#+()对应的C函数:
numeric.c
该函数被注册到Ruby的类型系统中:
numeric.c
rb_cFixnum是Ruby的Fixnum的C的实现类,继承自rb_cInteger:
numeric.c
上面rb_define_method函数使得fix_plus与一个NODE_CFUNC关联在了一起。这个函数会调用rb_intern(name)来将方法名转换为ID,这里对运算符做了特殊处理:
parse.y
这个特殊处理可以保证运算符与内建函数的对应关系。
P.S. 以上代码来自Ruby 1.8.7的源码。
P.P.S Ruby Hacking Guide真是本好书
===================================================================
“不幸”的是,JRuby的行为也是一样。我们可以通过JRuby.parse方法查看一段代码被解析成了怎样的AST:
可见复合赋值表达式与分开的表达式被解析为相同的AST。
更糟糕的是回帖中有错误的解释,将这个“差异”对应到汇编上:
引用
引用第8楼nightaway于2008-03-12 21:33发表的 :
a+=1 编译后的汇编指令 add
a++ 编译后的汇编指令 inc
inc 的运算周期 小于 add 所以 a++ 比 a+=1 快
如果楼主有疑问可以学学汇编语言.. 这样你的编码水平会大增..
a+=1 编译后的汇编指令 add
a++ 编译后的汇编指令 inc
inc 的运算周期 小于 add 所以 a++ 比 a+=1 快
如果楼主有疑问可以学学汇编语言.. 这样你的编码水平会大增..
这不是答非所问么……那帖的主题明明是在讨论a += 1与a = a + 1的差异;+=与++又不等价,况且Ruby里连++运算符都没有 =_=|||
引用
引用第12楼jiangcq于2008-05-26 10:32发表的 :
回答正确,+= 比 = + 效率要高很多,特别是在早期的计算机上
####################
a=a+1
MOV EAX,A
MOV EBX,1
ADD EAX,EBX
MOV A,EAX
##################
a+=1
MOV EAX,A
INC EAX
MOV A,EAX
####################
如果没记错的话应该是这样计算的
回答正确,+= 比 = + 效率要高很多,特别是在早期的计算机上
####################
a=a+1
MOV EAX,A
MOV EBX,1
ADD EAX,EBX
MOV A,EAX
##################
a+=1
MOV EAX,A
INC EAX
MOV A,EAX
####################
如果没记错的话应该是这样计算的
诶,所以说不知道就不要乱说嗯。这MOV EBX, 1在运算支持立即数的指令集里明显是废的。
编译器能做的优化多的是。偏偏这a = a + 1却没什么优化可做。假如这是C代码生成为x86的目标代码,编译器选择ADD r32, #1还是INC r32与这个语句写成a = a + 1还是a += 1没什么关系……要说跟a++或者++a扯上关系那还靠谱点,不过如果是好的优化编译器都应该能生成一样的代码。
这复合赋值运算符最大的意义在于:1、让源代码更加简洁易读;2、减少重复的地址计算。之所以a = a + 1与a += 1无论如何也差不了多少是因为a本身就已经是一个变量了,能“直接”访问。假如这是一个需要昂贵的地址计算的表达式,那复合赋值运算符就显得很有意义了。例如这样:
a.prop[0][1] = a.prop[0][1] + 1;
这里要赋值的目标的地址无法直接得到,得经过复杂的计算。这种情况下如果编译器不优化、又或者计算地址有副作用,则取值和赋值分开两次来计算地址就比只计算一次要慢一些,因而有优化的必要。
原帖地址:http://www.rpgchina.net/read-htm-tid-32257.html
要讨论这种问题果然还是得研究一下MRI到底是如何实现这些东西的。
YARV的实现机制比较不同,而且1.9.0这个实验系列何时才会进化到稳定版的2.0.x还很难说,所以还是拿1.8.x系列为准来讨论了。
===========================================================================
下面内容是我在那帖14楼的回复:
这个……事情要具体问题具体分析对吧,张冠李戴就不太好了 ^ ^
+=之类的复合赋值运算符在许多语言都有,语义类似但是实现的方式并不总相同。
RGSS里的脚本语言是Ruby,RPG Maker VX里使用的Ruby是1.8.1版的MRI。Ruby源代码并没有被直接编译到机器码,而是被Ruby解释器所解释:先把源代码翻译成抽象语法树,然后解释抽象语法树。
在Ruby里,一切皆是对象。因此像是加号减号之类的运算,也被看作是对象上的方法。a += 1的语义是a = a.+(1)(语义是:调用a对象上的+()方法,把1作为参数传进去,然后将方法的返回值赋值给a。更准确的说,是对右手边的a对象发送了一个"+"消息,以1为参数;返回得到的值赋值给左手边的a)。
+=的语义不是单独定义,而是由+()方法所决定的;换句话说一个类定义了+()方法就自动具备了+=。假如有语句a = 1,那么a是一个Fixnum,+=当中调用的+()方法就是Fixnum#+()。
先看看"+="这个符号被解析器识别为什么了。Ruby的扫描器(词法分析器)里有这么一段:
parse.y
case '+': c = nextc(); if (lex_state == EXPR_FNAME || lex_state == EXPR_DOT) { lex_state = EXPR_ARG; if (c == '@') { return tUPLUS; } pushback(c); return '+'; } if (c == '=') { yylval.id = '+'; // 注意这里,id是'+' lex_state = EXPR_BEG; return tOP_ASGN; // 然后整体以tOP_ASGN返回 }
可以看到+=被识别为tOP_ASGN类型的token。
a += 1形式的语句对应的这条语法:
statement: //... | var_lhs tOP_ASGN command_call // ... | //... ;
语法对应着解析器(语法分析器),而解析器会生成抽象语法树。如果等号前的是||则语法生成NODE_OP_ASGN_OR节点,如果是&&则生成NODE_OP_ASGN_AND节点,其它则调用call_op()函数生成NODE_CALL节点。
parse.y
var_lhs tOP_ASGN command_call { value_expr($3); if ($1) { ID vid = $1->nd_vid; if ($2 == tOROP) { $1->nd_value = $3; $$ = NEW_OP_ASGN_OR(gettable(vid), $1); if (is_asgn_or_id(vid)) { $$->nd_aid = vid; } } else if ($2 == tANDOP) { $1->nd_value = $3; $$ = NEW_OP_ASGN_AND(gettable(vid), $1); } else { $$ = $1; // 获得var_lhs对应的节点 // call_op将返回一个NODE_CALL节点,并赋值给var_lhs对应节点的“值” $$->nd_value = call_op(gettable(vid),$2,1,$3); } } else { $$ = 0; } }
由于节点的“值”(nd_value)被赋值为一个NODE_CALL节点,这里实质上完成了将a += 1变为a = a.+(1)的转换。
看看a = a + 1对应的语法和动作:
parse.y
lhs '=' command_call { $$ = node_assign($1, $3); }
结合下面node_assign()函数的实现,可以看到这里是把右手边的节点赋值给了左手边节点的“值”(nd_value)。并且,右手边的a + 1对应的语法与动作如下:
parse.y
arg '+' arg { $$ = call_op($1, '+', 1, $3); }
也是调用call_op()生成NODE_CALL节点,跟前面a += 1时一样。
于是,a += 1与a = a + 1在被解析后所生成的语法树是一样的,后续执行中就都是等价的了。
parse.y
static NODE* node_assign(lhs, rhs) NODE *lhs, *rhs; { if (!lhs) return 0; value_expr(rhs); switch (nd_type(lhs)) { case NODE_GASGN: case NODE_IASGN: case NODE_LASGN: case NODE_DASGN: case NODE_DASGN_CURR: case NODE_MASGN: case NODE_CDECL: case NODE_CVDECL: case NODE_CVASGN: lhs->nd_value = rhs; // 注意这里 break; case NODE_ATTRASGN: case NODE_CALL: lhs->nd_args = arg_add(lhs->nd_args, rhs); break; default: /* should not happen */ break; } return lhs; }
(nd_value是在node.h里定义的一个宏,展开为u2.node)
===========================================================================
这是Fixnum#+()对应的C函数:
numeric.c
/* * call-seq: * fix + numeric => numeric_result * * Performs addition: the class of the resulting object depends on * the class of <code>numeric</code> and on the magnitude of the * result. */ static VALUE fix_plus(x, y) VALUE x, y; { if (FIXNUM_P(y)) { long a, b, c; VALUE r; a = FIX2LONG(x); b = FIX2LONG(y); c = a + b; r = LONG2NUM(c); return r; } if (TYPE(y) == T_FLOAT) { return rb_float_new((double)FIX2LONG(x) + RFLOAT(y)->value); } return rb_num_coerce_bin(x, y); }
该函数被注册到Ruby的类型系统中:
numeric.c
rb_define_method(rb_cFixnum, "+", fix_plus, 1);
rb_cFixnum是Ruby的Fixnum的C的实现类,继承自rb_cInteger:
numeric.c
rb_cFixnum = rb_define_class("Fixnum", rb_cInteger);
上面rb_define_method函数使得fix_plus与一个NODE_CFUNC关联在了一起。这个函数会调用rb_intern(name)来将方法名转换为ID,这里对运算符做了特殊处理:
parse.y
if (name[0] != '_' && ISASCII(name[0]) && !ISALNUM(name[0])) { /* operators */ int i; for (i=0; op_tbl[i].token; i++) { if (*op_tbl[i].name == *name && strcmp(op_tbl[i].name, name) == 0) { id = op_tbl[i].token; goto id_regist; } } }
这个特殊处理可以保证运算符与内建函数的对应关系。
P.S. 以上代码来自Ruby 1.8.7的源码。
P.P.S Ruby Hacking Guide真是本好书
===================================================================
“不幸”的是,JRuby的行为也是一样。我们可以通过JRuby.parse方法查看一段代码被解析成了怎样的AST:
irb(main):001:0> require 'java' => true irb(main):002:0> JRuby.parse 'a += 1' => RootNode NewlineNode LocalAsgnNode |a| &0 >0 CallOneArgFixnumNode |+| LocalVarNode |a| &0 >0 ArrayNode FixnumNode ==1 irb(main):003:0> JRuby.parse 'a = a + 1' => RootNode NewlineNode LocalAsgnNode |a| &0 >0 CallOneArgFixnumNode |+| LocalVarNode |a| &0 >0 ArrayNode FixnumNode ==1
可见复合赋值表达式与分开的表达式被解析为相同的AST。
发表评论
-
The Prehistory of Java, HotSpot and Train
2014-06-02 08:18 0http://cs.gmu.edu/cne/itcore/vi ... -
MSJVM and Sun 1.0.x/1.1.x
2014-05-20 18:50 0当年的survey paper: http://www.sym ... -
Sun JDK1.4.2_28有TieredCompilation
2014-05-12 08:48 0原来以前Sun的JDK 1.4.2 update 28就已经有 ... -
IBM JVM notes (2014 ver)
2014-05-11 07:16 0Sovereign JIT http://publib.bou ... -
class data sharing by Apple
2014-03-28 05:17 0class data sharing is implement ... -
HotSpot Server VM与Server Class Machine
2014-02-18 13:21 0HotSpot VM历来有Client VM与Server V ... -
Java 8的lambda表达式在OpenJDK8中的实现
2014-02-04 12:08 0三月份JDK8就要发布首发了,现在JDK8 release c ... -
GC stack map与deopt stack map的异同
2014-01-08 09:56 0两者之间不并存在包含关系。它们有交集,但也各自有特别的地方。 ... -
HotSpot Server Compiler与data-flow analysis
2014-01-07 17:41 0http://en.wikipedia.org/wiki/Da ... -
基于LLVM实现VM的JIT的一些痛点
2014-01-07 17:25 0同事Philip Reames Sanjoy Das http ... -
tailcall notes
2013-12-27 07:42 0http://blogs.msdn.com/b/clrcode ... -
《自制编程语言》的一些笔记
2013-11-24 00:20 0http://kmaebashi.com/programmer ... -
字符串的一般封装方式的内存布局 (1): 元数据与字符串内容,整体还是分离?
2013-11-07 17:44 22390(Disclaimer:未经许可请 ... -
字符串的一般封装方式的内存布局 (0): 拿在手上的是什么
2013-11-04 18:22 21489(Disclaimer:未经许可请 ... -
字符串的一般封装方式的内存布局
2013-11-01 12:55 0(Disclaimer:未经许可请 ... -
关于string,内存布局,C++ std::string,CoW
2013-10-30 20:45 0(Disclaimer:未经许可请 ... -
对C语义的for循环的基本代码生成模式
2013-10-19 23:12 21871之前有同学在做龙书(第二版)题目,做到8.4的练习,跟我对答案 ... -
Function.prototype.bind
2013-09-24 18:07 0polyfill http://stackoverflow. ... -
Java的instanceof是如何实现的
2013-09-22 16:57 0Java语言规范,Java SE 7版 http://docs ... -
struct做参数不能从寄存器传?
2013-08-28 23:33 0test test test struct Foo { i ...
相关推荐
ruby-irb-1.8.7.352-13.el6.x86_64.rpm ruby-irb-1.8.7.352-13.el6.x86_64.rpm
ruby-1.8.7.352-13.el6.x86_64.rpm ruby-1.8.7.352-13.el6.x86_64.rpm
ruby-libs-1.8.7.352-13.el6.x86_64.rpm ruby-libs-1.8.7.352-13.el6.x86_64.rpm
用于 Ruby 1.8.x 的轻量级 UTF8-aware String 类扫描代码是用 C 实现的(如果你们中有人想帮忙的话,也会喜欢 Java 版本)。 目前,此 gem 在 1.8.7、1.9.2、1.9.3 和 Rubinius 上进行了测试 - 它可能适用于其他人,...
SmartAdmin在Ruby on Rails中的实现可能包括了预处理过的CSS和JS文件,可能已经配置好了Webpacker或Sprockets来处理前端资源,以及Bootstrap和其他前端库的集成。此外,它可能还提供了示例布局、小部件、图表和其他...
ruby 1.8 API; 包括ruby的核心内容.
### jQuery Widget Factory 1.8.21 开发指南 #### 概述 jQuery Widget Factory 是 jQuery UI 提供的一个强大的工具包,它简化了自定义控件的开发过程。通过这个框架,开发者可以轻松地创建出功能丰富且高度可定制...
Ruby中的赋值运算符不仅包括基本的赋值运算符(`=`),还包括一系列复合赋值运算符,这些运算符将运算和赋值结合在一起,提高了代码的可读性和效率。 - **简单赋值运算符 (`=`)**:将右侧操作数的值赋给左侧操作数。 ...
Exercism_exercises_in_Ruby._ruby.zip Exercism_exercises_in_Ruby._ruby.zip Exercism_exercises_in_Ruby._ruby.zip Exercism_exercises_in_Ruby._ruby.zip Exercism_exercises_in_Ruby._ruby.zip Exercism_...
Ruby 1.8和1.9是Ruby语言的两个重要版本,它们在许多方面有所不同,同时也对Ruby的发展产生了深远的影响。 Ruby 1.8是Ruby的一个早期版本,发布于2004年,它引入了许多特性,如块语法的改进、元编程能力的增强以及...
在使用Ruby 1.8进行开发时,开发者可以利用MRI(Matz's Ruby Interpreter)作为默认解释器,也可以选择JRuby(基于Java平台的实现)或Rubinius(使用LLVM作为后端的实现)来获取不同的性能特性。Ruby 1.8版本虽然已...
此 Ruby SDK 适用于 Ruby 1.8.x, 1.9.x, jruby, rbx, ree 版本,基于 七牛云存储官方API 构建。使用此 SDK 构建您的网络应用程序,能让您以非常便捷地方式将数据安全地存储到七牛云存储上。无论您的网络应用是一个...
官方离线安装包,测试可用。使用rpm -ivh [rpm完整包名] 进行安装
官方离线安装包,测试可用。使用rpm -ivh [rpm完整包名] 进行安装
Ruby 1.8.5 是 Ruby 1.8 系列的一个稳定版本,相比于更早的版本,它引入了一些改进和修复,为开发者提供了更稳定的编程环境。 1. **对象导向**:Ruby 是一种纯面向对象的语言,每个值都是一个对象,包括基本类型如...
Ruby 1.8.x 系列是 Ruby 的一个长期支持版本,尽管已经过时,但在某些老项目或特定环境中仍然不可或缺。 在 "ruby-1.8.5.tar" 压缩包中,我们可以预期包含以下内容: 1. **源代码**:所有 Ruby 解释器的 C 语言源...
Ruby 1.8.7 是一个古老的 Ruby 语言版本,它是 Ruby 社区在 2011 年发布的最后一个 1.8.x 系列版本。这个版本在当时非常流行,尤其对于某些项目和框架来说是必要的依赖,比如 Redmine 就是一个典型例子。Redmine 是...
解压版的RUBY开发环境(SDK),在windows下运行时,默认的没有这些dll,把找到的现在打包到一块,解压后放在ruby安装目录的bin目录下即可。 主要有如下: readline.dll、zlib.dll、ssleay32.dll、iconv.dll、libeay...