[MacRuby-devel] NSNumber Numeric Patch

Jonathan deWerd jjoonathan at gmail.com
Sat Nov 29 08:41:18 PST 2008


> Hi Jonathan,
>
> On Nov 28, 2008, at 7:08 PM, Jonathan deWerd wrote:
>
>> Hi!
>> I was tracking down a bug in my app that caused a crash, and I
>> finally narrowed it down to the conversion between NSNumber and the
>> Numeric types. I don't think that was the direct cause of the crash
>> (the immediate cause was a longjmp to a nonexistant "thread"), but
>> manually converting it via to_i made it work just fine. In the
>> spirit of "if it's worth doing once, it's worth automating" I wrote
>> some code that patches NSNumber itself to act more like a a Numeric.
>> You can now mix it in just about anywhere, with bignums and floats
>> and ints and it should all Just Work.
>>
>> This patch doesn't replace any ruby Numeric machinery, it just adds
>> a bit of sugar to the bridge.
>> <numeric.c.patch>
>>
>> I didn't write any formal test cases for it since I was hoping a
>> greater ruby mind than I could figure out an easy way to subvert the
>> Numeric test cases to my purposes :)
>> I would love for anyone experienced with boundary conditions to take
>> a look at it. I think I got just about everything, but an awful lot
>> of hackery goes on with ruby's numeric types, so a second opinion
>> would be most welcome.
>
> This is very interesting! In fact, I was planning to implement
> something very similar in order to fix http://www.macruby.org/trac/ticket/112
>
> My idea is to move the Numeric methods to NSNumber directly (very
> similarly to what we currently do for NSString, NSArray and
> NSDictionary), so that pure NSNumber objects can respond to #+, #-
> etc...
>
> So your patch seems to already implement parts of this idea. The next
> step would be to make sure these methods are created on NSNumber and
> that this still works with Fixnums, which are as you might know
> special types in the core. They are not real objects but masked
> pointers, unless they got converted to a RFixnum structure in order to
> be passed to Objective-C. (Eventually I would like to get rid of the
> masked pointers and use RFixnum structures for every Fixnum, which
> shouldn't turn that bad performance wise once we will JIT compile the
> arithmetic code paths. I would investigate that later.)
>
> Would you be willing to adapt your patch to implement this idea?
Sure, but I think we might want to make sure that's the best option  
before we go ahead and do it.
Currently (as in after the patch) NSNumber interoperates almost  
perfectly with the existing ruby infrastructure:

 >> n = (NSNumber.numberWithInt(3)**NSNumber.numberWithInt(100000))
=> Some amusingly large number
 >> n==(NSNumber.numberWithInt(3)**NSNumber.numberWithInt(100000))
=> true
 >> n==(3**NSNumber.numberWithInt(100000))
=> true
 >> n==(3**100000)
=> true
 >> n.to_s(10).length
=> 47713
 >> 10[NSNumber.numberWithInt(3)]
=> 1
 >> NSNumber.numberWithFloat(10.1)+NSNumber.numberWithInt(2)+1
=> 13.1000003814697
 >> NSNumber.numberWithFloat(10)+NSNumber.numberWithInt(2)-0.1
=> 11.9
 >> 1+NSNumber.numberWithInt(10)+NSNumber.numberWithInt(2)
=> 13

Basically, unless there is a very simple operation being done (with no  
sharp edge cases that I was uncertain about), it converts itself into  
a ruby object or pseudo-object and passes itself directly to the C  
implementations of the appropriate ruby methods. This probably  
performs better than keeping it as a CFNumber, since a CFNumber's  
value would need to be fetched and a new CFNumber would need to be  
reallocated for every operation (and I believe the ruby equivalents  
are quite a bit cheaper).

Current ("ruby") solution:
1 CFNumber value fetch (2 C calls) for each OC->RB crossing (no double- 
dispatch penalty, it sends the new object right to the C functions)
1 RFixnum allocation for each RB->OC crossing
Cheap numeric operations (no allocations in the good case, at worst  
it's probably the same as objc)

Complete NSNumber conversion ("objc" solution):
1 CFNumber value fetch for each numeric operation
1 CFNumber allocation for each numeric operation

Using FIXABLE() numbers, both solutions should approximately tie for 1- 
step arithmetic. But in every other case, the ruby solution should be  
faster, and it has the advantage of already being implemented :-)

It just occurred to me that I left out method dispatch overhead.  
You're the expert on that, so you tell me how it weighs in here :)
I would love to see something like FScript's operator methods ('+'  
maps to 'add:', and the like). Would using objc_msgSend make it worth  
the allocation overhead?

For compatibility reasons, a complete overhaul of the FIXNUM system  
probably wouldn't be very practical: any "regular" ruby C API code  
would still use the FIXNUM interface, so you would have to keep it  
around even if you replaced it with something more elegant under the  
hood. Unless there are secret plans I don't know about :)

>
>
>> Thanks to everyone here working on macruby, you've done an amazing
>> job. It's a very worthy successor to rubycocoa :)
>
> I'm very glad you like it, and thanks for taking the time to hack on a
> patch :-)
>
>> PS
>> While you're applying patches, I've got another quick one in a
>> ticket that deals with macruby_main and absolute paths: http://www.macruby.org/trac/ticket/163
>
> I will look at this one, thanks for reminding me!
Thanks for applying it :)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/macruby-devel/attachments/20081129/a431ca96/attachment.html>


More information about the MacRuby-devel mailing list