Consider the following Ruby code: i = 0 while (i < 1000) i = i + 1 end When run using IronRuby, the (i < 1000) comparison results in method ConvertToInt32 being invoked to convert local i into an integer, however, the i +1 operation simply casts i to be an integer. (IronPython performs a cast in both cases to avoid the method call to ConvertToInt32) It appears that the difference in behaviour of these two method calls in IronRuby is due to the fact that the first argument for the LessThan method is not overloaded: public static bool LessThan(int self, int other) public static bool LessThan(CodeContext/*!*/ context, int self, object other) but the first argument for the Add method is overloaded: public static object Add(int self, int other) public static double Add(int self, double other) public static object Add(CodeContext/*!*/ context, object self, object other) Note, this only affects performance, not correctness. Eliminating the call to ConvertToInt32 makes the program execute about 10% faster. As an aside, is there really a need for an overloaded Add method with a self of type object rather than int? Ironically, making the types stronger in this way would make the performance worse with the current implementation. Or am I completely mistaken? Cheers, Wayne. -------------- next part -------------- An HTML attachment was scrubbed... URL: http://rubyforge.org/pipermail/ironruby-core/attachments/20080205/6ba1fa7e/attachment.html
Regarding the aside, you can pass Float, Bignum, Complex or any other class you choose to define to the Fixnum#+ operator so you do need an overload with object. I seem to remember it then coerces the fixnum to whatever the other type is and then calls + on the coerced object. Without looking at the code I can''t comment on the main thrust of the mail, but shouldn''t the local variable i be a Fixnum from the word go and therefore call the LessThan(int, int) method anyway? Pete From: ironruby-core-bounces at rubyforge.org [mailto:ironruby-core-bounces at rubyforge.org] On Behalf Of Wayne Kelly Sent: Tuesday,05 February 05, 2008 03:36 To: ironruby-core at rubyforge.org Subject: [Ironruby-core] ConvertTo vs Cast Consider the following Ruby code: i = 0 while (i < 1000) i = i + 1 end When run using IronRuby, the (i < 1000) comparison results in method ConvertToInt32 being invoked to convert local i into an integer, however, the i +1 operation simply casts i to be an integer. (IronPython performs a cast in both cases to avoid the method call to ConvertToInt32) It appears that the difference in behaviour of these two method calls in IronRuby is due to the fact that the first argument for the LessThan method is not overloaded: public static bool LessThan(int self, int other) public static bool LessThan(CodeContext/*!*/ context, int self, object other) but the first argument for the Add method is overloaded: public static object Add(int self, int other) public static double Add(int self, double other) public static object Add(CodeContext/*!*/ context, object self, object other) Note, this only affects performance, not correctness. Eliminating the call to ConvertToInt32 makes the program execute about 10% faster. As an aside, is there really a need for an overloaded Add method with a self of type object rather than int? Ironically, making the types stronger in this way would make the performance worse with the current implementation. Or am I completely mistaken? Cheers, Wayne. -------------- next part -------------- An HTML attachment was scrubbed... URL: http://rubyforge.org/pipermail/ironruby-core/attachments/20080205/2ad65866/attachment.html
From: ironruby-core-bounces at rubyforge.org [ironruby-core-bounces at rubyforge.org] On Behalf Of Peter Bacon Darwin [bacondarwin at googlemail.com] Sent: Tuesday, 5 February 2008 6:37 PM To: ironruby-core at rubyforge.org Subject: Re: [Ironruby-core] ConvertTo vs Cast Regarding the aside, you can pass Float, Bignum, Complex or any other class you choose to define to the Fixnum#+ operator so you do need an overload with object. I seem to remember it then coerces the fixnum to whatever the other type is and then calls + on the coerced object. In Ruby 1.8.6: Fixnum.instance_method("+'').bind(3.0) produces: ''bind'': bind argument must be an instance of Fixnum (TypeError) Without looking at the code I can?t comment on the main thrust of the mail, but shouldn?t the local variable i be a Fixnum from the word go and therefore call the LessThan(int, int) method anyway? Yes, LessThan(int, int) is called each time, but an unnecessary call is also made to method ConvertToInt32 every time around the loop to convert what is already an integer into an integer. Cheers, Wayne. -------------- next part -------------- An HTML attachment was scrubbed... URL: http://rubyforge.org/pipermail/ironruby-core/attachments/20080205/d04fba0f/attachment-0001.html
What about this though? class D def +(other) 45 end def coerce(other) [self,self] end end 3 + D.new => 45 In Ruby 1.8.6: Fixnum.instance_method("+'').bind(3.0) produces: ''bind'': bind argument must be an instance of Fixnum (TypeError) -------------- next part -------------- An HTML attachment was scrubbed... URL: http://rubyforge.org/pipermail/ironruby-core/attachments/20080205/cfd00e45/attachment.html
OK, so I think that this is a problem with the AST to code generation, though I really don''t understand it deeply enough to say exactly where. If you dump the ASTs from the ruby code the body of the loop looks like this: { (.bound i) = (Object)0; .return .comma { .try { { (.bound #rfc).InLoop = (Boolean)True; { (.bound #skip-condition) = (Boolean)False; .for (; ; ) { .try { { .if ((.bound #skip-condition) ) {(Void)(.bound #skip-condition) = (Boolean)False; } .else {.if (.action (Boolean) ConvertTo System.Boolean( // ConvertTo to System.Boolean .action (Object) InvokeMember <( // InvokeMember <(Instance, Simple) ReturnNonCallable, IsCallWithThis (.bound i) 1000 ) ) ) {/*empty*/; } .else {.break; }} { (.bound i) = (Object).action (Object) InvokeMember +( // InvokeMember +(Instance, Simple) ReturnNonCallable, IsCallWithThis (.bound i) 1 ); } } } .catch ( BlockUnwinder #unwinder) { (.bound #skip-condition) = (.bound #unwinder).IsRedo; } .catch ( EvalUnwinder #unwinder) { .if (((.bound #unwinder).Reason =(BlockReturnReason)Break) ) {{ (.bound #loop-result) = (.bound #unwinder).ReturnValue; .break; } } .else { .throw () }} } } } } .finally { (.bound #rfc).InLoop = (Boolean)False; } (.bound #loop-result) } ; } No mention of ConvertToInt32 there although it is noticeable that the (.bound i) value is an object and not a Fixnum. If you dump the generated code you get the following two methods for invoking the Fixnum methods: public static object $Ruby.Builtins.FixnumOps.LessThan(object[] objArray1, DynamicSite<object, int, object> site1, CodeContext context1, object obj1, int num1) { if (((obj1 != null) && (obj1.GetType() == typeof(int))) && (((RubyClass) objArray1[0]).get_Version() == 0x3b3e)) { bool flag; bool flag1 = flag FixnumOps.LessThan(Converter.ConvertToInt32(obj1), num1); return RuntimeHelpers.BooleanToObject(flag); } return site1.UpdateBindingAndInvoke(context1, obj1, num1); } public static object $Ruby.Builtins.FixnumOps.Add(object[] objArray1, DynamicSite<object, int, object> site1, CodeContext context1, object obj1, int num1) { if (((obj1 != null) && (obj1.GetType() == typeof(int))) && (((RubyClass) objArray1[0]).get_Version() == 0x3b3e)) { return FixnumOps.Add((int) obj1, num1); } return site1.UpdateBindingAndInvoke(context1, obj1, num1); } Here as Wayne points out the code generator has added in the ConvertToInt32 in the LessThan invocation, whereas in the Add invocation it has used a direct cast. Somewhere in the innards of Ruby.dll this decision is being made and I haven''t tracked down where. Clearly it is not necessary to use the conversion method: for a start there is a clause around the call to the method that tests whether obj1 (self) is an int. As it happens I am generally doubtful about the inclusion of automated Conversion in IronRuby at all, apart from perhaps for .NET classes that don''t map directly to Ruby ones, such as byte and float. The design of the Ruby builtin classes manages the conversions directly, using such things as to_s, to_str, to_f, to_int and to_i. Getting the DLR to do hidden conversions seems to be overkill and can upset the semantics of the library classes. Pete Without looking at the code I can''t comment on the main thrust of the mail, but shouldn''t the local variable i be a Fixnum from the word go and therefore call the LessThan(int, int) method anyway? Yes, LessThan(int, int) is called each time, but an unnecessary call is also made to method ConvertToInt32 every time around the loop to convert what is already an integer into an integer. Cheers, Wayne. -------------- next part -------------- An HTML attachment was scrubbed... URL: http://rubyforge.org/pipermail/ironruby-core/attachments/20080205/76a2a3b6/attachment-0001.html
Peter Bacon Darwin:> OK, so I think that this is a problem with the AST to code generation, > though I really don''t understand it deeply enough to say exactly where. > > If you dump the ASTs from the ruby code the body of the loop looks > like > this: > (scary Martin AST dump elided)Yep. Looks like a bug. Can you open one on this for us? Thanks, -John
Stupid question, but how do you dump the AST? I''ve wanted to do that a couple times (most recently while hacking my own temporary "eval"). On Feb 5, 2008 1:42 PM, Peter Bacon Darwin <bacondarwin at googlemail.com> wrote:> OK, so I think that this is a problem with the AST to code generation, > though I really don''t understand it deeply enough to say exactly where. > > > > If you dump the ASTs from the ruby code the body of the loop looks like > this: > > >-------------- next part -------------- An HTML attachment was scrubbed... URL: http://rubyforge.org/pipermail/ironruby-core/attachments/20080205/b46fb12b/attachment.html
rbx /? is your friend :) the quick answer is rbx -X:ShowASTs For reference: IronRuby command line: Usage: [options] [file|- [arguments]] Options: -opt dummy -c cmd Program passed in as string (terminates option list) -h Display usage -i Inspect interactively after running script -V Print the version number and exit -O Enable optimizations -D EngineDebug mode -OO Remove doc-strings in addition to the -O optimizations -X:AutoIndent -X:AssembliesDir Set the directory for saving generated assemblies -X:ColorfulConsole Enable ColorfulConsole -X:DumpASTs Dump all ASTs generated to a file -X:ExceptionDetail Enable ExceptionDetail mode -X:Interpret Enable interpreted mode -X:Frames Generate custom frames -X:GenerateAsSnippets Generate code to run in snippet mode -X:ILDebug Output generated IL code to a text file for debugging -X:MaxRecursion Set the maximum recursion level -X:NoOptimize Disable JIT optimization in generated code -X:NoTraceback Do not emit traceback code -X:PassExceptions Do not catch exceptions that are unhandled by script code -X:PrivateBinding Enable binding to private members -X:SaveAssemblies Save generated assemblies -X:ShowASTs Print all ASTs to the console -X:ShowClrExceptions Display CLS Exception information -X:ShowRules Show the AST for rules generated -X:SlowOps Enable fast ops -X:StaticMethods Generate static methods only -X:TabCompletion Enable TabCompletion mode -X:TrackPerformance Track performance sensitive areas -X:CachePointersInApartment Cache COM pointers per apartment (prototype) On Feb 5, 2008 3:59 PM, Eric Nicholson <enicholson at gmail.com> wrote:> Stupid question, but how do you dump the AST? I''ve wanted to do that a > couple times (most recently while hacking my own temporary "eval"). > > > > On Feb 5, 2008 1:42 PM, Peter Bacon Darwin <bacondarwin at googlemail.com> > wrote: > > > > > > > > > > > > > OK, so I think that this is a problem with the AST to code generation, > though I really don''t understand it deeply enough to say exactly where. > > > > > > > > If you dump the ASTs from the ruby code the body of the loop looks like > this: > > > > > > > _______________________________________________ > Ironruby-core mailing list > Ironruby-core at rubyforge.org > http://rubyforge.org/mailman/listinfo/ironruby-core > >-- Michael Letterle [Polymath Programmer] http://michaeldotnet.blogspot.com
Hi Peter, Yes, you are right, sorry - somehow I missed reading the very last case in our fix_plus implementation :( ________________________________ From: ironruby-core-bounces at rubyforge.org [mailto:ironruby-core-bounces at rubyforge.org] On Behalf Of Peter Bacon Darwin Sent: Wednesday, 6 February 2008 12:16 AM To: ironruby-core at rubyforge.org Subject: Re: [Ironruby-core] ConvertTo vs Cast What about this though? class D def +(other) 45 end def coerce(other) [self,self] end end 3 + D.new => 45 In Ruby 1.8.6: Fixnum.instance_method("+'').bind(3.0) produces: ''bind'': bind argument must be an instance of Fixnum (TypeError) -------------- next part -------------- An HTML attachment was scrubbed... URL: http://rubyforge.org/pipermail/ironruby-core/attachments/20080206/3636870d/attachment.html