Skip to content

Native (and fast) Time#xmlschema (iso8601)#8856

Merged
enebo merged 1 commit intojruby:masterfrom
PChambino:time-xmlschema-native
Jun 3, 2025
Merged

Native (and fast) Time#xmlschema (iso8601)#8856
enebo merged 1 commit intojruby:masterfrom
PChambino:time-xmlschema-native

Conversation

@PChambino
Copy link
Contributor

Solves: #8476
Related: ruby/ruby#11510

I was looking for something to learn (J)Ruby internals and this one looked like something within my ability, so here is my stab at it.

Benchmarks: ~5-10x faster than JRuby 10.0.0.1 + ~1-2x faster than CRuby 3.4.3.
time benchmark-driver benchmark/time_xmlschema.yml -e bin/ruby-3.4.3 -e 'bin/ruby-3.4.3 --jit' -e bin/jruby-10.0.0.1 -e bin/jruby-local                                          12:31:13
Warming up --------------------------------------
            time.xmlschema        10.319M i/s -     10.851M times in 1.051555s (96.91ns/i)
        utc_time.xmlschema        11.241M i/s -     11.297M times in 1.005024s (88.96ns/i)
         time.xmlschema(6)         7.711M i/s -      8.080M times in 1.047869s (129.69ns/i)
     utc_time.xmlschema(6)         7.999M i/s -      8.187M times in 1.023579s (125.02ns/i)
         time.xmlschema(9)         7.999M i/s -      8.224M times in 1.028214s (125.02ns/i)
     utc_time.xmlschema(9)         8.212M i/s -      8.571M times in 1.043714s (121.77ns/i)
fraction_sec.xmlschema(10)         2.846M i/s -      3.014M times in 1.058998s (351.38ns/i)
     future_time.xmlschema        11.051M i/s -     11.219M times in 1.015137s (90.49ns/i)
Calculating -------------------------------------
                           bin/ruby-3.4.3  bin/ruby-3.4.3 --jit  bin/jruby-10.0.0.1  bin/jruby-local
            time.xmlschema        12.178M               12.081M              1.721M          15.641M i/s -     30.958M times in 2.542229s 2.562450s 17.990264s 1.979323s
        utc_time.xmlschema        12.817M               13.334M              2.452M          33.486M i/s -     33.722M times in 2.631081s 2.528956s 13.751355s 1.007040s
         time.xmlschema(6)         8.690M                8.772M              1.607M          12.454M i/s -     23.133M times in 2.661957s 2.637201s 14.398513s 1.857480s
     utc_time.xmlschema(6)         9.126M                8.979M              1.958M          22.482M i/s -     23.996M times in 2.629342s 2.672311s 12.253306s 1.067355s
         time.xmlschema(9)         8.893M                8.146M              1.931M          10.984M i/s -     23.996M times in 2.698347s 2.945848s 12.428324s 2.184630s
     utc_time.xmlschema(9)         9.176M                9.121M              2.129M          19.089M i/s -     24.637M times in 2.684933s 2.701248s 11.570208s 1.290617s
fraction_sec.xmlschema(10)         2.954M                2.970M              1.952M          12.845M i/s -      8.538M times in 2.890307s 2.874170s 4.372857s 0.664676s
     future_time.xmlschema        13.045M               13.012M              2.479M          24.144M i/s -     33.154M times in 2.541459s 2.547970s 13.371908s 1.373208s

Comparison:
                         time.xmlschema
           bin/jruby-local:  15640715.5 i/s
            bin/ruby-3.4.3:  12177512.7 i/s - 1.28x  slower
      bin/ruby-3.4.3 --jit:  12081416.6 i/s - 1.29x  slower
        bin/jruby-10.0.0.1:   1720821.1 i/s - 9.09x  slower

                     utc_time.xmlschema
           bin/jruby-local:  33486294.1 i/s
      bin/ruby-3.4.3 --jit:  13334371.6 i/s - 2.51x  slower
            bin/ruby-3.4.3:  12816800.0 i/s - 2.61x  slower
        bin/jruby-10.0.0.1:   2452270.3 i/s - 13.66x  slower

                      time.xmlschema(6)
           bin/jruby-local:  12453839.4 i/s
      bin/ruby-3.4.3 --jit:   8771706.8 i/s - 1.42x  slower
            bin/ruby-3.4.3:   8690130.6 i/s - 1.43x  slower
        bin/jruby-10.0.0.1:   1606607.2 i/s - 7.75x  slower

                  utc_time.xmlschema(6)
           bin/jruby-local:  22481555.0 i/s
            bin/ruby-3.4.3:   9126162.4 i/s - 2.46x  slower
      bin/ruby-3.4.3 --jit:   8979419.7 i/s - 2.50x  slower
        bin/jruby-10.0.0.1:   1958312.5 i/s - 11.48x  slower

                      time.xmlschema(9)
           bin/jruby-local:  10983925.4 i/s
            bin/ruby-3.4.3:   8892782.5 i/s - 1.24x  slower
      bin/ruby-3.4.3 --jit:   8145638.5 i/s - 1.35x  slower
        bin/jruby-10.0.0.1:   1930736.1 i/s - 5.69x  slower

                  utc_time.xmlschema(9)
           bin/jruby-local:  19089438.3 i/s
            bin/ruby-3.4.3:   9176080.0 i/s - 2.08x  slower
      bin/ruby-3.4.3 --jit:   9120658.3 i/s - 2.09x  slower
        bin/jruby-10.0.0.1:   2129362.0 i/s - 8.96x  slower

             fraction_sec.xmlschema(10)
           bin/jruby-local:  12844915.2 i/s
      bin/ruby-3.4.3 --jit:   2970492.7 i/s - 4.32x  slower
            bin/ruby-3.4.3:   2953908.0 i/s - 4.35x  slower
        bin/jruby-10.0.0.1:   1952430.9 i/s - 6.58x  slower

                  future_time.xmlschema
           bin/jruby-local:  24143685.4 i/s
            bin/ruby-3.4.3:  13045383.0 i/s - 1.85x  slower
      bin/ruby-3.4.3 --jit:  13012047.2 i/s - 1.86x  slower
        bin/jruby-10.0.0.1:   2479399.7 i/s - 9.74x  slower


________________________________________________________
Executed in  211.40 secs    fish           external
   usr time  241.36 secs    0.09 millis  241.36 secs
   sys time    4.67 secs    2.87 millis    4.67 secs

The main difference with CRuby is that I make use of getNanos() for the fraction digits since subsec is derived from getNanos() in JRuby, so we can skip the subsec logic I believe. Similar to inspect.

I tried a couple of iterations of a similar implementation to CRuby but it would always result in worse performance.

I noticed some methods in ConvertBytes that have very similar number / digit to byte(s) logic, but I ended up making the logic a bit more focused on xmlschema needs (for performance), so I didn't re-use the existing ones.

Let me know if there are any suggestions / ideas, or feel free to change it yourself directly.

Solves: jruby#8476
Related: ruby/ruby#11510

I was looking for something to learn (J)Ruby internals and this one
looked like something within my ability, so here is my stab at it.

Benchmarks: ~5-10x faster than JRuby 10.0.0.1 + ~1-2x faster than CRuby 3.4.3.

The main difference with CRuby is that I make use of getNanos() for the
fraction digits since `subsec` is derived from getNanos() in JRuby, so
we can skip the subsec logic I believe. Similar to `inspect`.

I tried a couple of iterations of a similar implementation to CRuby but
it would always result in worse performance.

I noticed some methods in ConvertBytes that have very similar number / digit
to byte(s) logic, but I ended up making the logic a bit more focused on
xmlschema needs (for performance), so I didn't re-use the existing ones.

Let me know if there are any suggestions / ideas, or feel free to change
it yourself directly.
@enebo enebo added this to the JRuby 10.0.1.0 milestone Jun 3, 2025
@enebo
Copy link
Member

enebo commented Jun 3, 2025

@PChambino This looks great. I do think at times we need some standard utilities for number to bytes but ConvertBytes is I think too ambitious in that it tries to solve all the things. Just running against CI since it required approval to run.

@headius
Copy link
Member

headius commented Jun 3, 2025

Great work! The pure-Ruby version was always intended to be temporary, so we would pass tests needed to get JRuby 10 out the door. I'm very happy you went ahead and finished porting this into Java!

@enebo enebo merged commit 5488069 into jruby:master Jun 3, 2025
72 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants