Hi, I''m working on a native extension by writing an IronRuby library in C#. I hit the wall with the CallSiteStorage parameters that can optionally come as the first 0 or more parameters for a ruby method. What are they and what''s their use? I tried to understand that from their current uses accross the code by I couldn''t understand how IronRuby knows what to set there. For example, this is from ArrayOps: [RubyMethod("sort")] public static object Sort( BinaryOpStorage/*!*/ comparisonStorage, BinaryOpStorage/*!*/ lessThanStorage, BinaryOpStorage/*!*/ greaterThanStorage, BlockParam block, RubyArray/*!*/ self) { ... ... } How does IronRuby knows that the first should be a comparison storage, the second a less-than storage and the third a greater-than storage? is it because of the name of the parameter? Thanks! Shay. -- -------------------------------------------------- Shay Friedman Author of IronRuby Unleashed http://www.IronShay.com Follow me: http://twitter.com/ironshay -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/ironruby-core/attachments/20091018/72e5f39a/attachment.html>
A dynamic call site is an object that takes a certain number of arguments and a kind of description of how to combine those arguments into a result. The "description" (also known as the call site binder) generates code that is stored in the call site and keyed on some property of the arguments -- usually the type. So a call site that''s used to perform a "less than" operation must be attached to code that knows how to look at two arguments and generate IL which will determine whether the first arg is less than the second one (or, if the comparison is not valid, IL which will throw an exception). Now let''s say you have a bit of Ruby that says "c = a < b". When the IronRuby compiler generates code for this, it doesn''t know what the types of "a" and "b" are, so it can''t emit the actual comparison. Instead, it creates a call site that takes two arguments and returns a result, and it attaches this call site to a binder that says "call the < function that''s defined on the first object". The first time the code is executed, a = 1 and b = 3. The binder looks at the type of a and determines that it should call Fixnum.<, so it creates code in the form of a delegate which will do that (let''s name this "CALL1"), and will store that delegate in the call site along with a test ("TEST1") which indicates when that delegate is valid. The next time the code is executed, a = 3 and b = 7. The call site executes TEST1, which returns a positive result so it uses CALL1 to execute the comparison. The binder does not get involved. The third time the code is executed, a = "hello" and b = "world". TEST1 fails, so the call site asks the binder to analyze the new situation and come up with a TEST2 and CALL2 that will call String.< for an argument of type string. All of this is basically DLR 101, so I''m sorry if it''s stuff you already know :). Now it should be pretty obvious that a call site''s performance degrades as it sees a greater variety of types. The site has more and more TEST methods to run before it gets a positive result or knows that it has to fall back to the binder to generate new code. Consider a method like Array.sort. This method needs to perform comparisons on individual array elements of arbitrary type. That means that a dynamic call site needs to be involved. In earlier versions of IronRuby, we only had two choices -- use a single call site that''s shared between all calls to Array.sort, or create a new site each time sort is called. The former approach suffers from the problem of "going megamorphic", that is, having too many tests to be called for each comparison. But with the latter, we lose out on some efficiency because we need to create a new call site and run the binder code at least once for every call to sort. The CallSiteStorage mechanism adds a third option. It allows the library author to push the location of the call site cache up to the place where Array.sort is actually called. This then creates a situation that is much more like the one for "c = a < b"; there''s a one-for-one relationship between the call site in user code and the call site cache. This is a good level for caching, because any given array in user code that calls "sort" is very likely to contain elements of only a single type or small number of types. So to answer your questions more specifically, 1. The IronRuby compiler automatically creates three BinaryOpStorage objects for each call to Array.sort -- one for each of the three parameters which have that type -- and emits code to insert those parameter onto the call stack before calling ArrayOps.Sort. 2. The BinaryOpStorage object is not tied to a specific operation at creation; it''s just a cache. It''s how the BinaryOpStorage is used that determines the kind of code stored in the cache. 3. Specifically, if you trace the code path into Protocols.Compare and Protocols.ConvertCompareResult, you''ll see that these storages are initialized with calls to "<=>", "<" and ">", respectively. 4. The names of the parameters are irrelevant. :) There''s a general lesson here -- the DLR is like porridge; it''s best when it''s neither too hot nor too cold. From: ironruby-core-bounces at rubyforge.org [mailto:ironruby-core-bounces at rubyforge.org] On Behalf Of Shay Friedman Sent: Saturday, October 17, 2009 11:14 PM To: ironruby-core at rubyforge.org Subject: [Ironruby-core] CallSiteStorage for [RubyMethod] Hi, I''m working on a native extension by writing an IronRuby library in C#. I hit the wall with the CallSiteStorage parameters that can optionally come as the first 0 or more parameters for a ruby method. What are they and what''s their use? I tried to understand that from their current uses accross the code by I couldn''t understand how IronRuby knows what to set there. For example, this is from ArrayOps: [RubyMethod("sort")] public static object Sort( BinaryOpStorage/*!*/ comparisonStorage, BinaryOpStorage/*!*/ lessThanStorage, BinaryOpStorage/*!*/ greaterThanStorage, BlockParam block, RubyArray/*!*/ self) { ... ... } How does IronRuby knows that the first should be a comparison storage, the second a less-than storage and the third a greater-than storage? is it because of the name of the parameter? Thanks! Shay. -- -------------------------------------------------- Shay Friedman Author of IronRuby Unleashed http://www.IronShay.com Follow me: http://twitter.com/ironshay -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/ironruby-core/attachments/20091018/de767b0a/attachment.html>
Thanks so much Curt for the detailed reply! Shay. On Sun, Oct 18, 2009 at 3:52 PM, Curt Hagenlocher <curth at microsoft.com>wrote:> A dynamic call site is an object that takes a certain number of arguments > and a kind of description of how to combine those arguments into a result. > The "description" (also known as the call site binder) generates code that > is stored in the call site and keyed on some property of the arguments -- > usually the type. So a call site that''s used to perform a "less than" > operation must be attached to code that knows how to look at two arguments > and generate IL which will determine whether the first arg is less than the > second one (or, if the comparison is not valid, IL which will throw an > exception). > > > > Now let''s say you have a bit of Ruby that says "c = a < b". When the > IronRuby compiler generates code for this, it doesn''t know what the types of > "a" and "b" are, so it can''t emit the actual comparison. Instead, it creates > a call site that takes two arguments and returns a result, and it attaches > this call site to a binder that says "call the < function that''s defined on > the first object". The first time the code is executed, a = 1 and b = 3. The > binder looks at the type of a and determines that it should call Fixnum.<, > so it creates code in the form of a delegate which will do that (let''s name > this "CALL1"), and will store that delegate in the call site along with a > test ("TEST1") which indicates when that delegate is valid. > > > > The next time the code is executed, a = 3 and b = 7. The call site executes > TEST1, which returns a positive result so it uses CALL1 to execute the > comparison. The binder does not get involved. > > > > The third time the code is executed, a = "hello" and b = "world". TEST1 > fails, so the call site asks the binder to analyze the new situation and > come up with a TEST2 and CALL2 that will call String.< for an argument of > type string. > > > > All of this is basically DLR 101, so I''m sorry if it''s stuff you already > know :). > > > > Now it should be pretty obvious that a call site''s performance degrades as > it sees a greater variety of types. The site has more and more TEST methods > to run before it gets a positive result or knows that it has to fall back to > the binder to generate new code. > > > > Consider a method like Array.sort. This method needs to perform comparisons > on individual array elements of arbitrary type. That means that a dynamic > call site needs to be involved. In earlier versions of IronRuby, we only had > two choices -- use a single call site that''s shared between all calls to > Array.sort, or create a new site each time sort is called. The former > approach suffers from the problem of "going megamorphic", that is, having > too many tests to be called for each comparison. But with the latter, we > lose out on some efficiency because we need to create a new call site and > run the binder code at least once for every call to sort. > > > > The CallSiteStorage mechanism adds a third option. It allows the library > author to push the location of the call site cache up to the place where > Array.sort is actually called. This then creates a situation that is much > more like the one for "c = a < b"; there''s a one-for-one relationship > between the call site in user code and the call site cache. This is a good > level for caching, because any given array in user code that calls "sort" is > very likely to contain elements of only a single type or small number of > types. > > > > > > So to answer your questions more specifically, > > 1. The IronRuby compiler automatically creates three BinaryOpStorage > objects for each call to Array.sort -- one for each of the three parameters > which have that type -- and emits code to insert those parameter onto the > call stack before calling ArrayOps.Sort. > > 2. The BinaryOpStorage object is not tied to a specific operation at > creation; it''s just a cache. It''s how the BinaryOpStorage is used that > determines the kind of code stored in the cache. > > 3. Specifically, if you trace the code path into Protocols.Compare and > Protocols.ConvertCompareResult, you''ll see that these storages are > initialized with calls to "<=>", "<" and ">", respectively. > > 4. The names of the parameters are irrelevant. :) > > > > > > There''s a general lesson here -- the DLR is like porridge; it''s best when > it''s neither too hot nor too cold. > > > > > > *From:* ironruby-core-bounces at rubyforge.org [mailto: > ironruby-core-bounces at rubyforge.org] *On Behalf Of *Shay Friedman > *Sent:* Saturday, October 17, 2009 11:14 PM > *To:* ironruby-core at rubyforge.org > *Subject:* [Ironruby-core] CallSiteStorage for [RubyMethod] > > > > Hi, > > I''m working on a native extension by writing an IronRuby library in C#. I > hit the wall with the CallSiteStorage parameters that can optionally come as > the first 0 or more parameters for a ruby method. > > What are they and what''s their use? I tried to understand that from their > current uses accross the code by I couldn''t understand how IronRuby knows > what to set there. > > For example, this is from ArrayOps: > [RubyMethod("sort")] > public static object Sort( > BinaryOpStorage/*!*/ comparisonStorage, > BinaryOpStorage/*!*/ lessThanStorage, > BinaryOpStorage/*!*/ greaterThanStorage, > BlockParam block, RubyArray/*!*/ self) { > ... > ... > } > > How does IronRuby knows that the first should be a comparison storage, the > second a less-than storage and the third a greater-than storage? is it > because of the name of the parameter? > > Thanks! > Shay. > > -- > -------------------------------------------------- > Shay Friedman > Author of IronRuby Unleashed > http://www.IronShay.com > Follow me: http://twitter.com/ironshay > > _______________________________________________ > Ironruby-core mailing list > Ironruby-core at rubyforge.org > http://rubyforge.org/mailman/listinfo/ironruby-core > >-- -------------------------------------------------- Shay Friedman Author of IronRuby Unleashed http://www.IronShay.com Follow me: http://twitter.com/ironshay -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/ironruby-core/attachments/20091019/71e9c221/attachment.html>
Glad you enjoyed it! One small correction: ignore the specific usage of the word "delegate" in the second paragraph. The gist is correct, but the technical details are different. "Delegate" was left over from an earlier revision of the text. From: ironruby-core-bounces at rubyforge.org [mailto:ironruby-core-bounces at rubyforge.org] On Behalf Of Shay Friedman Sent: Monday, October 19, 2009 2:30 AM To: ironruby-core at rubyforge.org Subject: Re: [Ironruby-core] CallSiteStorage for [RubyMethod] Thanks so much Curt for the detailed reply! Shay. On Sun, Oct 18, 2009 at 3:52 PM, Curt Hagenlocher <curth at microsoft.com<mailto:curth at microsoft.com>> wrote: A dynamic call site is an object that takes a certain number of arguments and a kind of description of how to combine those arguments into a result. The "description" (also known as the call site binder) generates code that is stored in the call site and keyed on some property of the arguments -- usually the type. So a call site that''s used to perform a "less than" operation must be attached to code that knows how to look at two arguments and generate IL which will determine whether the first arg is less than the second one (or, if the comparison is not valid, IL which will throw an exception). Now let''s say you have a bit of Ruby that says "c = a < b". When the IronRuby compiler generates code for this, it doesn''t know what the types of "a" and "b" are, so it can''t emit the actual comparison. Instead, it creates a call site that takes two arguments and returns a result, and it attaches this call site to a binder that says "call the < function that''s defined on the first object". The first time the code is executed, a = 1 and b = 3. The binder looks at the type of a and determines that it should call Fixnum.<, so it creates code in the form of a delegate which will do that (let''s name this "CALL1"), and will store that delegate in the call site along with a test ("TEST1") which indicates when that delegate is valid. The next time the code is executed, a = 3 and b = 7. The call site executes TEST1, which returns a positive result so it uses CALL1 to execute the comparison. The binder does not get involved. The third time the code is executed, a = "hello" and b = "world". TEST1 fails, so the call site asks the binder to analyze the new situation and come up with a TEST2 and CALL2 that will call String.< for an argument of type string. All of this is basically DLR 101, so I''m sorry if it''s stuff you already know :). Now it should be pretty obvious that a call site''s performance degrades as it sees a greater variety of types. The site has more and more TEST methods to run before it gets a positive result or knows that it has to fall back to the binder to generate new code. Consider a method like Array.sort. This method needs to perform comparisons on individual array elements of arbitrary type. That means that a dynamic call site needs to be involved. In earlier versions of IronRuby, we only had two choices -- use a single call site that''s shared between all calls to Array.sort, or create a new site each time sort is called. The former approach suffers from the problem of "going megamorphic", that is, having too many tests to be called for each comparison. But with the latter, we lose out on some efficiency because we need to create a new call site and run the binder code at least once for every call to sort. The CallSiteStorage mechanism adds a third option. It allows the library author to push the location of the call site cache up to the place where Array.sort is actually called. This then creates a situation that is much more like the one for "c = a < b"; there''s a one-for-one relationship between the call site in user code and the call site cache. This is a good level for caching, because any given array in user code that calls "sort" is very likely to contain elements of only a single type or small number of types. So to answer your questions more specifically, 1. The IronRuby compiler automatically creates three BinaryOpStorage objects for each call to Array.sort -- one for each of the three parameters which have that type -- and emits code to insert those parameter onto the call stack before calling ArrayOps.Sort. 2. The BinaryOpStorage object is not tied to a specific operation at creation; it''s just a cache. It''s how the BinaryOpStorage is used that determines the kind of code stored in the cache. 3. Specifically, if you trace the code path into Protocols.Compare and Protocols.ConvertCompareResult, you''ll see that these storages are initialized with calls to "<=>", "<" and ">", respectively. 4. The names of the parameters are irrelevant. :) There''s a general lesson here -- the DLR is like porridge; it''s best when it''s neither too hot nor too cold. From: ironruby-core-bounces at rubyforge.org<mailto:ironruby-core-bounces at rubyforge.org> [mailto:ironruby-core-bounces at rubyforge.org<mailto:ironruby-core-bounces at rubyforge.org>] On Behalf Of Shay Friedman Sent: Saturday, October 17, 2009 11:14 PM To: ironruby-core at rubyforge.org<mailto:ironruby-core at rubyforge.org> Subject: [Ironruby-core] CallSiteStorage for [RubyMethod] Hi, I''m working on a native extension by writing an IronRuby library in C#. I hit the wall with the CallSiteStorage parameters that can optionally come as the first 0 or more parameters for a ruby method. What are they and what''s their use? I tried to understand that from their current uses accross the code by I couldn''t understand how IronRuby knows what to set there. For example, this is from ArrayOps: [RubyMethod("sort")] public static object Sort( BinaryOpStorage/*!*/ comparisonStorage, BinaryOpStorage/*!*/ lessThanStorage, BinaryOpStorage/*!*/ greaterThanStorage, BlockParam block, RubyArray/*!*/ self) { ... ... } How does IronRuby knows that the first should be a comparison storage, the second a less-than storage and the third a greater-than storage? is it because of the name of the parameter? Thanks! Shay. -- -------------------------------------------------- Shay Friedman Author of IronRuby Unleashed http://www.IronShay.com Follow me: http://twitter.com/ironshay _______________________________________________ Ironruby-core mailing list Ironruby-core at rubyforge.org<mailto:Ironruby-core at rubyforge.org> http://rubyforge.org/mailman/listinfo/ironruby-core -- -------------------------------------------------- Shay Friedman Author of IronRuby Unleashed http://www.IronShay.com Follow me: http://twitter.com/ironshay -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/ironruby-core/attachments/20091019/36f1df96/attachment-0001.html>