RCR 296: Destructive methods return self

I know that it's not standard policy to announce RCRs on ruby-talk,
but since this has been a topic of conversation (debate, polemic,
death threats?) lately on this list, I thought it relevant.

RCR 296: Destructive methods return self

http://www.rcrchive.net/rcr/show/296

Let the flame war begin!

Dan

Hi --

I know that it's not standard policy to announce RCRs on ruby-talk,
but since this has been a topic of conversation (debate, polemic,
death threats?) lately on this list, I thought it relevant.

RCR 296: Destructive methods return self

RCR 296: Destructive methods return self

Let the flame war begin!

No war on RCRchive, please.... The 150 or so discussions of this
topic I've been witness to have all been very civil :slight_smile:

By the way, the ruby-talk.org/###### URL syntax doesn't work any more.
You have to do:
http://www.ruby-talk.org/cgi-bin/scat.rb/ruby/ruby-talk/######

I'll try to fix them directly in the database.

David

···

On Sun, 20 Mar 2005, Daniel Amelang wrote:

--
David A. Black
dblack@wobblini.net

In data 3/19/2005, "Daniel Amelang" <daniel.amelang@gmail.com> ha
scritto:

I know that it's not standard policy to announce RCRs on ruby-talk,
but since this has been a topic of conversation (debate, polemic,
death threats?) lately on this list, I thought it relevant.

Wonder what's the difference between
"I'll kill you" and "I'll kill you!"

RCR 296: Destructive methods return self

RCR 296: Destructive methods return self

Let the flame war begin!

Dan

E

For those interested in an alternative, I just put this up on the RCR:

Perhaps a more interesting solution would be to get rid of all
destructive methods and work on optimizing ther existing
non-destructive counterparts. Matz seems to prefer this solution.

http://ruby-talk.org/cgi-bin/scat.rb/ruby/ruby-talk/17764

Unfortunately, the non-destructive versions are still quite noticeably slower.

If interest towards this approach materializes, I would be happy to
supercede this RCR.

Dan

Actually you can do this as a minor edit, as the RCRs author. (Silly
me, I forgot how convenient I made it :slight_smile:

David

···

On Sun, 20 Mar 2005, David A. Black wrote:

I'll try to fix them directly in the database.

--
David A. Black
dblack@wobblini.net

* Daniel Amelang (Mar 19, 2005 23:30):

Perhaps a more interesting solution would be to get rid of all
destructive methods and work on optimizing ther existing
non-destructive counterparts. Matz seems to prefer this solution.

http://ruby-talk.org/cgi-bin/scat.rb/ruby/ruby-talk/17764

Unfortunately, the non-destructive versions are still quite noticeably
slower.

No they're not. Where are you getting these assertions from?,
        nikolai

···

--
::: name: Nikolai Weibull :: aliases: pcp / lone-star / aka :::
::: born: Chicago, IL USA :: loc atm: Gothenburg, Sweden :::
::: page: minimalistic.org :: fun atm: gf,lps,ruby,lisp,war3 :::
main(){printf(&linux["\021%six\012\0"],(linux)["have"]+"fun"-97);}

David A. Black wrote:

By the way, the ruby-talk.org/###### URL syntax doesn't work any more.
You have to do:
http://www.ruby-talk.org/cgi-bin/scat.rb/ruby/ruby-talk/######

That's disappointing. I found that feature very convenient.

Who administers the app? Can that function be restored?

Hal

Hi,

Perhaps a more interesting solution would be to get rid of all
destructive methods and work on optimizing ther existing
non-destructive counterparts. Matz seems to prefer this solution.

I don't like this proposal, since it loose some feature (modify
detection) and there's no way to recover without serious performance
penalty, which makes no sense of bang methods usage.

Alternative plans:

  (1) remove bang methods altogether.

  (2) current bang methods are bad because they work most of the
      cases, and fail if no change has made. thus let us make bang
      methods return something other than self, boolean for example,
      to fail always. this forces no chaining of bang methods.

  (3) introduce "real" multiple return values ala common lisp, and let
      the first returned value as the receiver and the second return
      value is boolean value to denote success/failure.

  (4) add some kind of reference counting, and if the receiver is
      referenced from only one place, modify the receiver in place, to
      gain performance.

              matz.

···

In message "Re: RCR 296: Destructive methods return self" on Sun, 20 Mar 2005 07:27:33 +0900, Daniel Amelang <daniel.amelang@gmail.com> writes:

Update: the RCR is now 297. Sorry, slip of the hand. I really meant
'minor edit', I swear!

···

On Sun, 20 Mar 2005 06:48:56 +0900, David A. Black <dblack@wobblini.net> wrote:

On Sun, 20 Mar 2005, David A. Black wrote:

> I'll try to fix them directly in the database.

Actually you can do this as a minor edit, as the RCRs author. (Silly
me, I forgot how convenient I made it :slight_smile:

David

--
David A. Black
dblack@wobblini.net

No they're not. Where are you getting these assertions from?,

I use ruby in the bioinformatics field. We deal with very long
biological sequence data and protein structures. I'll have to go
through my code and post some examples with corresponding bio data,
but I've already spent enough time today on the RCR. I may have some
examples from my computer graphics class years ago that used sort!
also. You're right for asking for benchmarks, I just have to move on
to other things right now.

So, anyone else want to brew up some examples? I'd be glad to be proved wrong.

And don't forget, unless bang(!) methods are eliminated entirely,
there is still the problem with the confusion (of experienced ruby
programmers).

Dan

Nikolai Weibull wrote:

* Daniel Amelang (Mar 19, 2005 23:30):
> Perhaps a more interesting solution would be to get rid of all
> destructive methods and work on optimizing ther existing
> non-destructive counterparts. Matz seems to prefer this solution.

> http://ruby-talk.org/cgi-bin/scat.rb/ruby/ruby-talk/17764

> Unfortunately, the non-destructive versions are still quite

noticeably

> slower.

No they're not. Where are you getting these assertions from?,
        nikolai

Oh, I dunno. Maybe this benchmark?

The only exception was slice vs. slice!. No idea what happened there.

Here are the results of my benchmark running on a 1.8 GHz P4, 512MB
RAM, on Windows XP, SP2. The actual benchmark code I used it further
below.

C:\eclipse\workspace\ruby-foo\benchmarks>ruby bang.rb
      user system total real
capitalize 1.862000 0.000000 1.862000 ( 1.923000)
capitalize! 0.591000 0.000000 0.591000 ( 0.600000)
chomp 1.663000 0.010000 1.673000 ( 1.853000)
chomp! 0.510000 0.000000 0.510000 ( 0.511000)
delete 5.739000 0.030000 5.769000 ( 6.449000)
delete! 4.396000 0.000000 4.396000 ( 4.606000)
downcase 1.712000 0.010000 1.722000 ( 2.294000)
downcase! 0.581000 0.000000 0.581000 ( 0.580000)
gsub 4.296000 0.030000 4.326000 ( 5.188000)
gsub! 0.972000 0.000000 0.972000 ( 1.161000)
lstrip 1.762000 0.020000 1.782000 ( 2.123000)
lstrip! 0.431000 0.000000 0.431000 ( 0.431000)
next 1.813000 0.000000 1.813000 ( 1.933000)
next! 1.331000 0.010000 1.341000 ( 1.472000)
reverse 1.633000 0.010000 1.643000 ( 1.742000)
reverse! 0.380000 0.000000 0.380000 ( 0.501000)
rstrip 1.773000 0.000000 1.773000 ( 1.882000)
rstrip! 0.420000 0.000000 0.420000 ( 0.431000)
slice 1.883000 0.020000 1.903000 ( 2.454000)
slice! 3.415000 0.010000 3.425000 ( 3.765000)
strip 1.793000 0.000000 1.793000 ( 1.812000)
strip! 0.370000 0.000000 0.370000 ( 0.411000)
sub 1.302000 0.000000 1.302000 ( 1.382000)
sub! 0.901000 0.000000 0.901000 ( 1.202000)
swapcase 0.862000 0.000000 0.862000 ( 0.961000)
swapcase! 0.420000 0.000000 0.420000 ( 0.451000)
tr 3.515000 0.000000 3.515000 ( 3.925000)
tr 2.284000 0.000000 2.284000 ( 2.484000)
tr_s 3.545000 0.010000 3.555000 ( 3.845000)
tr_s! 2.193000 0.000000 2.193000 ( 2.303000)
upcase 2.023000 0.000000 2.023000 ( 2.103000)
upcase! 0.631000 0.000000 0.631000 ( 0.651000)

require "benchmark"
include Benchmark

s1 = " hello "
s2 = " hello "
s3 = " hello "
max = 500000

bm do |bench|
   bench.report("capitalize"){
      max.times{ s1.capitalize }
   }

   bench.report("capitalize!"){
      max.times{ s1.capitalize! }
   }

   bench.report("chomp"){
      max.times{ s1.chomp }
   }

   bench.report("chomp!"){
      max.times{ s1.chomp! }
   }

   bench.report("delete"){
      max.times{ s1.delete("o") }
   }

   bench.report("delete!"){
      max.times{ s1.delete!("o") }
   }

   bench.report("downcase"){
      max.times{ s1.downcase }
   }

   bench.report("downcase!"){
      max.times{ s1.downcase! }
   }

   bench.report("gsub"){
      max.times{ s1.gsub(/e/,"i") }
   }

   bench.report("gsub!"){
      max.times{ s1.gsub!(/e/,"i") }
   }

   bench.report("lstrip"){
      max.times{ s1.lstrip }
   }

   bench.report("lstrip!"){
      max.times{ s1.lstrip! }
   }

   bench.report("next"){
      max.times{ s1.next }
   }

   bench.report("next!"){
      max.times{ s1.next! }
   }

   bench.report("reverse"){
      max.times{ s1.reverse }
   }

   bench.report("reverse!"){
      max.times{ s1.reverse! }
   }

   bench.report("rstrip"){
      max.times{ s1.rstrip }
   }

   bench.report("rstrip!"){
      max.times{ s1.rstrip! }
   }

   bench.report("slice"){
      max.times{ s2.slice(1..4) }
   }

   bench.report("slice!"){
      max.times{ s2.slice!(1..4) }
   }

   bench.report("strip"){
      max.times{ s2.strip }
   }

   bench.report("strip!"){
      max.times{ s2.strip! }
   }

   bench.report("sub"){
      max.times{ s2.sub(/e/,"i") }
   }

   bench.report("sub!"){
      max.times{ s2.sub!(/e/,"i") }
   }

   bench.report("swapcase"){
      max.times{ s2.swapcase }
   }

   bench.report("swapcase!"){
      max.times{ s2.swapcase! }
   }

   bench.report("tr"){
      max.times{ s3.tr("e","a") }
   }

   bench.report("tr"){
      max.times{ s3.tr!("e","a") }
   }

   bench.report("tr_s"){
      max.times{ s3.tr_s("a","e") }
   }

   bench.report("tr_s!"){
      max.times{ s3.tr_s!("a","e") }
   }

   bench.report("upcase"){
      max.times{ s3.upcase }
   }

   bench.report("upcase!"){
      max.times{ s3.upcase! }
   }
end

Regards,

Dan

Yukihiro Matsumoto wrote:

Alternative plans:

  (1) remove bang methods altogether.

Backwards compatibility is lost.

  (2) current bang methods are bad because they work most of the
      cases, and fail if no change has made. thus let us make bang
      methods return something other than self, boolean for example,
      to fail always. this forces no chaining of bang methods.

Tolerable, but only because it removes the element of surprise.

  (3) introduce "real" multiple return values ala common lisp, and let
      the first returned value as the receiver and the second return
      value is boolean value to denote success/failure.

Ick. Ever wonder why other languages don't do this?

  (4) add some kind of reference counting, and if the receiver is
      referenced from only one place, modify the receiver in place, to
      gain performance.

Oooo, pretty! Seems like it might defeat the goal of optimizing things, unless the reference counting could reduce the overall impact of garbage collection.

When I'm playing with simple algorithms, disabling garbage collection seems to yield a guaranteed 20% speedup, even for relatively short tasks (< 30 sec.).

Another option:

    (5) add alternative destructive methods with different names that
        always return self.

This is the change with the least impact, but I realize it is ugly.

···

--
Glenn Parker | glenn.parker-AT-comcast.net | <http://www.tetrafoil.com/&gt;

Yes, I am liking the proposal less and less as time goes on. And I'm
the one who wrote it!

I'm *am* liking the (1) remove bang methods altogether idea more and
more. It has the positive (minor) side effect of reducing the number
of instance methods for String and Array, which is nice. And of
course, it eliminates the chaining confusion, since bang methods are
no longer an option. Losing the bang methods has also proven to be
only a minor loss in the efficiency category (we could live without
them).

But, this alone doesn't solve the problem with the modification
detection. Is it _that_ common? I've heard only one person complain so
far. Yet, you (matz) seem to be concerned about losing that
functionality, so I'll be concerned too. :slight_smile:

Will (2) really solve the problem, or just mitigate it slightly?

(3) is an interesting solution to the detection problem. Perhaps
having real multiple return values will solve some other issues in
Rubyland also (anyone?). Using a modified version of the 'strip'
method, let me illustrate a minor modification in the language that
allows for easier use of multiple return values:

class String
  # The new strip returns both the string result and
  # a flag that some change occured.
  def strip
    ...
    return result, changed
  end
end

# This works now:
str, changed = "hello ".strip

# But currently, when you only want the string result, you have to do this:
str, _ = "hello ".strip

# Because this:
str = "hello ".strip

# gives you an array :frowning:

# Why not make the minor change such that this:
*str = "hello ".strip

# gives you the array and this:

str = "hello ".strip

only gives you the _first_ of the multiple return values. That way we can return
multiple return values without requiring the receivers to use _ all the time
to throw away the rest.

I'm done. Thanks for taking the time to consider my proposal.

Dan

Obviously I'm only expressing my opinion here, which often
is nearly worthless:

Yukihiro Matsumoto wrote:

Alternative plans:

  (1) remove bang methods altogether.

"The cradle is too short. Let's cut off the baby's feet."

  (2) current bang methods are bad because they work most of the
      cases, and fail if no change has made. thus let us make bang
      methods return something other than self, boolean for example,
      to fail always. this forces no chaining of bang methods.

Again, please no. I like chaining.

  (3) introduce "real" multiple return values ala common lisp, and let
      the first returned value as the receiver and the second return
      value is boolean value to denote success/failure.

Interesting, but only if "the common case is the prettier one" and
back compatibility is maintained. I don't want to go sprinkling
commas and asterisks through old code.

  (4) add some kind of reference counting, and if the receiver is
      referenced from only one place, modify the receiver in place, to
      gain performance.

Hmm, does this work? Would there be times it would not be obvious whether we
were changing the original object or not? If so, unacceptable.

I would almost suggest what I once suggested as a joke: Combine the ! and ?
suffixes.

    gsub! returns self
    gsub!? returns self or nil (yes, it looks silly)

Or alternatively:

    gsub! returns self or nil as now
    gsub_! returns self (yes, it's ugly)

Or perhaps:

    Give the objects a "changed?" flag. Every bang method is expected to
    set it...

      obj.gsub!(...) # returns self and sets flag
      if obj.changed? then... # not thread-friendly, but I could live with it

    Is that really an expensive solution?

Hal

Yukihiro Matsumoto wrote:

  (2) current bang methods are bad because they work most of the
      cases, and fail if no change has made. thus let us make bang
      methods return something other than self, boolean for example,
      to fail always. this forces no chaining of bang methods.

I think this one is the most consistent way of doing things. In a way, using chaining with receiver-modifying methods doesn't make sense. You really don't want to modify the return value of the method, you want to modify the original object. I think the main reaon people want to chain bang methods is that they don't like having to type the variable a lot of times.

the_config_string_for_foo = $stdin.gets
the_config_string_for_foo.strip!.downcase!

is easier to type than

the_config_string_for_foo = $stdin.gets
the_config_string_for_foo.strip!
the_config_string_for_foo.downcase!

But it seems to me that what people are really looking for is a way to apply multiple methods to an object without having to retype it's name:

the_config_string_for_foo = $stdin.gets
the_config_string_for_foo.apply { strip!; downcase! }

This has been proposed a few times, I think. I can't remember how you (Matz) felt about it. Is the reason Ruby doesn't have a way to do it because you don't like the idea, or you were hoping for consensus on the exact method of doing it?

  (4) add some kind of reference counting, and if the receiver is
      referenced from only one place, modify the receiver in place, to
      gain performance.

This sounds like "more work for Matz", but also a really good solution. Most of the time people only want bang methods for efficiency. If that weren't an issue then it would be even easier to have the bang-methods return boolean (modified / not modified) and then people could choose the method based on what they were after (chaining vs. "was something changed") and not based on efficiency.

Ben

Hi --

···

On Sun, 20 Mar 2005, Daniel Amelang wrote:

Update: the RCR is now 297. Sorry, slip of the hand. I really meant
'minor edit', I swear!

I manually fixed it -- it's 296 again :slight_smile: (with the comments and
votes from 297).

David

--
David A. Black
dblack@wobblini.net

* Daniel Berger (Mar 20, 2005 01:10):

> > Unfortunately, the non-destructive versions are still quite
> > noticeably slower.

> No they're not. Where are you getting these assertions from?,

Oh, I dunno. Maybe this benchmark?

The only exception was slice vs. slice!. No idea what happened there.

Well, with slice!, you have to do more work than doing a slice. The
reason is that you have to paste together the parts that are still a
part of the string. With slice you simply return the substring.

For me, next! is also slower than next.

A comment on your benchmark: doesn't it make more sense to do the tests
on new strings each time? The benchmark should surely be try to
evaluate the merit of returning a new string versus replacing its
contents, right? With a benchmark that preallocates 'max' copies of
's1' and fetches them from an array for each of the calls to the
respective functions, the times are much more evenly matched:

            user system total real
      capitalize 0.660000 0.000000 0.660000 ( 0.675759)
      capitalize! 0.480000 0.010000 0.490000 ( 0.494108)
      chomp 0.660000 0.000000 0.660000 ( 0.680509)
      chomp! 0.370000 0.000000 0.370000 ( 0.370647)
      delete 1.300000 0.000000 1.300000 ( 1.296738)
      delete! 1.070000 0.000000 1.070000 ( 1.068162)
      downcase 0.630000 0.000000 0.630000 ( 0.639248)
      downcase! 0.400000 0.000000 0.400000 ( 0.399627)
      gsub 2.710000 0.020000 2.730000 ( 2.844503)
      gsub! 2.250000 0.030000 2.280000 ( 2.331406)
      lstrip 0.700000 0.000000 0.700000 ( 0.705452)
      lstrip! 0.430000 0.000000 0.430000 ( 0.424257)
      next 0.570000 0.000000 0.570000 ( 0.570100)
      next! 0.720000 0.000000 0.720000 ( 0.725646)
      slice 0.850000 0.000000 0.850000 ( 0.844139)
      slice! 1.210000 0.000000 1.210000 ( 1.217413)
      reverse 0.510000 0.000000 0.510000 ( 0.514665)
      reverse! 0.350000 0.000000 0.350000 ( 0.349400)
      rstrip 0.660000 0.000000 0.660000 ( 0.664235)
      rstrip! 0.370000 0.000000 0.370000 ( 0.371327)
      strip 0.550000 0.000000 0.550000 ( 0.547162)
      strip! 0.390000 0.000000 0.390000 ( 0.390382)
      sub 0.940000 0.000000 0.940000 ( 0.938963)
      sub! 0.690000 0.000000 0.690000 ( 0.690106)
      swapcase 0.630000 0.000000 0.630000 ( 0.630045)
      swapcase! 0.390000 0.000000 0.390000 ( 0.392977)
      tr 0.850000 0.000000 0.850000 ( 0.852794)
      tr 0.750000 0.000000 0.750000 ( 0.751016)
      tr_s 0.850000 0.000000 0.850000 ( 0.845268)
      tr_s! 0.690000 0.000000 0.690000 ( 0.690721)
      upcase 0.600000 0.000000 0.600000 ( 0.606707)
      upcase! 0.390000 0.000000 0.390000 ( 0.413688)

Anyway, if you don't like this test, here are the times on my machine
(it seems Ruby on a Linux system runs a lot faster than on a Windows
system, and much more evenly between the two versions of each method):

            user system total real
      capitalize 0.490000 0.000000 0.490000 ( 0.488138)
      capitalize! 0.290000 0.000000 0.290000 ( 0.286009)
      chomp 0.460000 0.000000 0.460000 ( 0.466617)
      chomp! 0.220000 0.000000 0.220000 ( 0.219798)
      delete 1.150000 0.000000 1.150000 ( 1.153041)
      delete! 0.920000 0.000000 0.920000 ( 0.912380)
      downcase 0.490000 0.000000 0.490000 ( 0.489456)
      downcase! 0.240000 0.000000 0.240000 ( 0.240895)
      gsub 1.250000 0.000000 1.250000 ( 1.254223)
      gsub! 0.520000 0.000000 0.520000 ( 0.516004)
      lstrip 0.480000 0.000000 0.480000 ( 0.485036)
      lstrip! 0.200000 0.000000 0.200000 ( 0.198516)
      next 0.410000 0.000000 0.410000 ( 0.406751)
      next! 0.430000 0.000000 0.430000 ( 0.430365)
      reverse 0.380000 0.000000 0.380000 ( 0.384075)
      reverse! 0.190000 0.000000 0.190000 ( 0.193044)
      rstrip 0.470000 0.000000 0.470000 ( 0.471682)
      rstrip! 0.200000 0.000000 0.200000 ( 0.194479)
      slice 0.600000 0.000000 0.600000 ( 0.596786)
      slice! 0.960000 0.000000 0.960000 ( 0.961636)
      strip 0.470000 0.000000 0.470000 ( 0.469545)
      strip! 0.180000 0.000000 0.180000 ( 0.185437)
      sub 0.620000 0.000000 0.620000 ( 0.620524)
      sub! 0.470000 0.000000 0.470000 ( 0.464609)
      swapcase 0.380000 0.000000 0.380000 ( 0.380528)
      swapcase! 0.190000 0.000000 0.190000 ( 0.192872)
      tr 0.810000 0.000000 0.810000 ( 0.818261)
      tr 0.590000 0.000000 0.590000 ( 0.615229)
      tr_s 0.850000 0.000000 0.850000 ( 0.858405)
      tr_s! 0.590000 0.000000 0.590000 ( 0.597201)
      upcase 0.550000 0.000000 0.550000 ( 0.578785)
      upcase! 0.250000 0.000000 0.250000 ( 0.252732)

I'm not arguing with the fact that for most methods, the destructive
version will be faster. I'm just saying that the performance difference
between the two isn't that big,
        nikolai

···

--
::: name: Nikolai Weibull :: aliases: pcp / lone-star / aka :::
::: born: Chicago, IL USA :: loc atm: Gothenburg, Sweden :::
::: page: minimalistic.org :: fun atm: gf,lps,ruby,lisp,war3 :::
main(){printf(&linux["\021%six\012\0"],(linux)["have"]+"fun"-97);}

* Daniel Amelang (Mar 19, 2005 23:50):

I use ruby in the bioinformatics field. We deal with very long
biological sequence data and protein structures.

How long? Are you sure String is the class for you? Is it possible
that some domain-specific data structures would suite your tasks better?
I'm being curious, not provocative,
        nikolai

···

--
::: name: Nikolai Weibull :: aliases: pcp / lone-star / aka :::
::: born: Chicago, IL USA :: loc atm: Gothenburg, Sweden :::
::: page: minimalistic.org :: fun atm: gf,lps,ruby,lisp,war3 :::
main(){printf(&linux["\021%six\012\0"],(linux)["have"]+"fun"-97);}

Hi --

(3) is an interesting solution to the detection problem. Perhaps
having real multiple return values will solve some other issues in
Rubyland also (anyone?). Using a modified version of the 'strip'
method, let me illustrate a minor modification in the language that
allows for easier use of multiple return values:

class String
# The new strip returns both the string result and
# a flag that some change occured.
def strip
   ...
   return result, changed
end
end

# This works now:
str, changed = "hello ".strip

# But currently, when you only want the string result, you have to do this:
str, _ = "hello ".strip

# Because this:
str = "hello ".strip

# gives you an array :frowning:

Actually you can just do:

   str, = "hello ".strip

to assign the first array element to str. That's a nice construct,
but it would extremely annoying and feels kind of ad hoc to require it
for every strip operation (not to mention sub, gsub, reverse, etc.)

Maybe we need StripData, SubData, etc., like MatchData... (No, not
really :slight_smile:

David

···

On Tue, 22 Mar 2005, Daniel Amelang wrote:

--
David A. Black
dblack@wobblini.net

Interesting observation and benchmark! It might be neat to have
further details on how you are testing this. Or is this measurement
done on an actual app that you have deployed?

I guess it might make sense for a Ruby app to disable GC and invoke it
when really necessary in a speed-critical situation.

Cheers,
Navin.

···

Glenn Parker <glenn.parker@comcast.net> wrote:

Oooo, pretty! Seems like it might defeat the goal of optimizing things,
unless the reference counting could reduce the overall impact of garbage
collection.

When I'm playing with simple algorithms, disabling garbage collection
seems to yield a guaranteed 20% speedup, even for relatively short tasks
(< 30 sec.).