Skip to content

early return from a block is slow #5933

@ahorek

Description

@ahorek

Environment

jruby 9.2.9.0-SNAPSHOT (2.5.7) 2019-10-22 c3171f5 Java HotSpot(TM) 64-Bit Server VM 11.0.2+9-LTS on 11.0.2+9-LTS +jit [linux-x86_64]

refs #5928

I've investigated why is String#unpack1 so slow

using an early return inside a block is a very expensive operation on jruby. (13x! slower on graal)

jruby (9.2.9.0 dev)
 unpack early return    700.842k (± 7.8%) i/s -      3.485M in   5.012230s
        unpack block      2.289M (± 9.2%) i/s -     11.253M in   4.992273s
        unpack first      2.853M (± 5.8%) i/s -     14.189M in   4.994309s

jruby + indy (9.2.9.0 dev)
 unpack early return    923.728k (±10.2%) i/s -      4.545M in   4.987390s
        unpack block      2.914M (± 9.3%) i/s -     14.266M in   4.985981s
        unpack first      3.770M (± 7.0%) i/s -     18.699M in   4.991746s

jruby + indy + graal (9.2.9.0 dev)
 unpack early return    348.480k (±10.8%) i/s -      1.720M in   5.013014s
        unpack block      2.973M (±10.6%) i/s -     14.613M in   4.995835s
        unpack first      4.495M (± 7.3%) i/s -     22.248M in   4.990273s

ruby (2.5.3)
 unpack early return      2.212M (± 9.2%) i/s -     10.946M in   5.000249s
        unpack block      2.688M (± 5.4%) i/s -     13.407M in   5.007471s
        unpack first      2.868M (± 4.1%) i/s -     14.313M in   5.001607s

ruby (2.7 dev)
 unpack early return      2.698M (± 7.2%) i/s -     13.406M in   5.008788s
        unpack block      3.253M (± 5.6%) i/s -     16.222M in   5.010958s
        unpack first      2.974M (± 5.1%) i/s -     14.827M in   5.007237s

ruby + jit (2.7 dev)
 unpack early return      2.783M (± 5.3%) i/s -     13.902M in   5.014952s
        unpack block      3.941M (± 9.2%) i/s -     19.537M in   5.007289s
        unpack first      3.444M (± 8.5%) i/s -     17.083M in   5.006996s

truffleruby (19.0.0)
 unpack early return      5.583M (±28.9%) i/s -     21.127M in   5.003905s
        unpack block     10.755M (±14.5%) i/s -     48.633M in   5.147068s
        unpack first      7.976M (±18.9%) i/s -     33.639M in   4.990572s

script

require 'benchmark/ips'

STRING = "".freeze

def unpack_block
  STRING.unpack('m') { |x| return x }
end

def unpack_block2
  value = nil
  STRING.unpack('m') { |x| value ||= x }
  value
end

def unpack_first
  STRING.unpack('m').first
end

Benchmark.ips do |x|
  x.report('unpack early return') { unpack_block  }
  x.report('unpack block')        { unpack_block2 }
  x.report('unpack first')        { unpack_first  }
end

I don't know if there's a way to improve it or how to fix it, but after profiling I see an exception is being generated
obrazek

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions