Optimize String#to_s - yjit

According to @casperisfine, much of the time on some SFR requests is spent in String#to_s, which just returns self.

I was thinking that this should be pretty easy to optimize. In some cases we might already know the type of self to be a string, in which case it's basically just an identity op. For the other cases we could guard that the type of self is a T_STRING which should also be quicker than calling into a C function I think. For the other cases we could call into the original C function?

@jhawthorn Is there complexity I'm not seeing and would you have time to take this one on? :)

Asked Sep 29 '21 04:09
avatar maximecb

3 Answer:

about 15% of the time on some SFR requests is spent in String#to_s

To clarify a bit, it might have been a one off, and I'm always a bit doubtful of the StackProf results, but yes it reported 5ms in String#to_s, mostly coming from a tracing library. But more generally the pattern is:

def initialize(arg)
  @arg = arg.to_s

But I suppose it's tricky, in term of ISeq to_s is

 opt_send_without_block                 <calldata!mid:to_s, argc:0, ARGS_SIMPLE>

So you'd need to check:

  • String#to_s wasn't redefined or refined
  • arg is T_STRING AND its class is rb_cString.

But yeah, being able to compile out these useless casting calls could be interesting.

Answered Sep 23 '21 at 15:20
avatar  of casperisfine

I believe we can do this relatively easily by adding it to our method codegen table, which should cover both checking for to_s redefinition and will check the class.

Answered Sep 23 '21 at 16:47
avatar  of jhawthorn

I did some profiling on this with perf and it does look like we spend a sizable amount of cycles for the simple case even in railsbench. On my profile it's about 1% of he cycles out of all cycles spent in generated code. I sent a PR for this.

Answered Sep 24 '21 at 21:07
avatar  of XrXr