from Hacker News

Never create Ruby strings longer than 23 characters

by ctaglia on 12/11/13, 2:39 PM with 59 comments

  • by nly on 12/11/13, 3:39 PM

    This is known as the "small string optimisation" in C++, so you can see a similar implementation in Clangs libc++[1].

    One interesting corollary is that moving short strings in an implementation that does this could actually be ever so slightly (negligibly) slower than moving long ones (since byte copies are slower than word copies). But generally, this is a free lunch optimisation and can save you hundreds of megs of memory when writing programs dealing with millions of short strings.

    [1] http://llvm.org/svn/llvm-project/libcxx/trunk/include/string - search for "union"

  • by Someone on 12/11/13, 3:15 PM

    http://www.slideshare.net/nirusuma/what-lies-beneath-the-bea... (from march 2012) also discusses this.

    Also (pedantic):

       #define RSTRING_EMBED_LEN_MAX ((int)((sizeof(VALUE)*3)/sizeof(char)-1))
    
    sizeof(char) is always 1, so that division is superfluous.
  • by danielweber on 12/11/13, 3:21 PM

    More like "ruby optimizes for short strings, and chose 23 at the cut-off point for Reasons."
  • by ben0x539 on 12/11/13, 3:21 PM

    There's some discussion at https://news.ycombinator.com/item?id=3425164 , including some interesting technical/benchmarky comments.
  • by ra88it on 12/11/13, 4:24 PM

    Title: "Never create Ruby strings longer than 23 characters"

    Conclusion: "Don’t worry! I don’t think you should refactor all your code to be sure you have strings of length 23 or less."

  • by spoiler on 12/11/13, 3:40 PM

    This is MRI (C Ruby) behaviour and not Ruby - specific , though. However, this is still interesting information.
  • by anon4 on 12/11/13, 5:12 PM

    Wouldn't it be better to use this declaration though:

        struct RString {
    
          struct RBasic basic;
    
          union {
            struct {
              long len;
              char *ptr;
              union {
                long capa;
                VALUE shared;
              } aux;
            } heap;
        
            char ary[];
          } as;
        };
    
        /* apologies if I messed up the syntax here */
        #define RSTRING_EMBED_LEN_MAX (sizeof(((RString*)(0))->as) - 1)
    
    Then you can even use the padding the compiler added, if any, plus you can add more things to heap and the embed length will grow automatically.
  • by markburns on 12/11/13, 3:21 PM

    For anyone interested, he points to an older translation of the Ruby Hacking Guide, there is a pretty much complete translation at

    http://ruby-hacking-guide.github.com

  • by gaius on 12/11/13, 3:47 PM

    I suppose the thing to do is analyse your app for the average string length, and just recompile your Ruby with that. Would be even better of it was a command line parameter.
  • by pedrocr on 12/11/13, 3:15 PM

    Why does "str2 = str" actually allocate a new RString instead of just pointing both str and str2 to the same RString?
  • by grosbisou on 12/11/13, 3:28 PM

    Extremely interesting. But I cannot quite understand why RSTRING_EMBED_LEN_MAX is calculated that way.

    VALUE seems to be unsigned int defined via "typedef uintptr_t VALUE;" and "typedef unsigned __int64 uintptr_t;"

    But why is it calculated like that I don't get. Anyone can explain?

  • by gesman on 12/11/13, 4:11 PM

    I wonder why they didn't make cut-off optimization points at 33?

    When programmers don't know in advance how long name/email/input/whatever field is going to be - they just use the magic "power of two" length :)

    So 32 (or 33) in this case would be more reasonable.

  • by badman_ting on 12/11/13, 3:18 PM

    Reminds me of this Mr Show sketch :) https://www.youtube.com/watch?v=RkP_OGDCLY0
  • by throwaway0094 on 12/11/13, 3:48 PM

    Is Ruby's internal encoding UTF-8, then?
  • by jokoon on 12/11/13, 3:48 PM

    "never use ruby" works well for me
  • by drakaal on 12/11/13, 4:28 PM

    Who needs more than 23?
  • by corresation on 12/11/13, 4:11 PM

    This all sounds rather terrible for Ruby, doesn't it? It isn't so much that the short string is faster (though I'm left unclear whether it itself is on the stack/heap, though given the GC nature of Ruby and practical considerations of the language, it must be the heap), but rather that the cost of the short string is also added to the long string in the heap (assumed) allocation of the RString (which becomes larger and thus more difficult to malloc).

    If this is intended to sit on the stack, which I find highly unlikely (especially given the timings that seem to be the delta between one malloc and two, and would be much more significant if it were a stack allocation versus a heap allocation. This is not comparable to small string optimizations for the stack in C++), maybe. But otherwise it seems like a poorly considered hack.

    The string type could as easily have been dynamically allocated based upon the length of the string, where the ptr by default points inside that same allocated block. If the string is expanded it can then be realloced and the string alloced somewhere else. No waste, a single allocation, etc.