Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cache RubyDateParser object in ThreadContext to reduce the cost of object creations #4675

Closed
wants to merge 2 commits into from

Conversation

muga
Copy link
Contributor

@muga muga commented Jun 18, 2017

To improve performance of Java strptime implemented on #4635, this PR caches a RubyDateParser object in ThreadContext to reduce the cost of RubyDateParser object creations. The cache is basically similar approach to RubyDateFormatter in ThreadContext.

The following is my micro benchmark based on https://github.com/macournoyer/tinyrb. The performance by this PR is 1.5 times faster maximumly.

1,000 times Date._strptime('2001-02-03', '%Y-%m-%d')
                     user     system      total        real
mri 2.4.1        0.000000   0.000000   0.070000 (  0.092492)
jruby 9.1.9.0    0.000000   0.000000   8.220000 (  3.861129)
jruby (master)   0.000000   0.000000   7.130000 (  3.671143)

10,000 times Date._strptime('2001-02-03', '%Y-%m-%d')
                     user     system      total        real
mri 2.4.1        0.000000   0.000000   0.100000 (  0.126605)
jruby 9.1.9.0    0.000000   0.000000  11.570000 (  5.183052)
jruby (master)   0.000000   0.000000   8.280000 (  3.905535)

100,000 times Date._strptime('2001-02-03', '%Y-%m-%d')
                     user     system      total        real
mri 2.4.1        0.000000   0.000000   0.200000 (  0.219390)
jruby 9.1.9.0    0.000000   0.000000  17.000000 ( 10.131313)
jruby (master)   0.000000   0.000000  10.620000 (  5.003686)

1,000,000 times Date._strptime('2001-02-03', '%Y-%m-%d')
                     user     system      total        real
mri 2.4.1        0.000000   0.000000   1.240000 (  1.361164)
jruby 9.1.9.0    0.000000   0.000000  57.490000 ( 51.720962)
jruby (master)   0.000000   0.000000  17.010000 ( 10.431316)

10,000,000 times Date._strptime('2001-02-03', '%Y-%m-%d')
                     user     system      total        real
mri 2.4.1        0.000000   0.000000  12.750000 ( 15.315754)
jruby 9.1.9.0    0.000000   0.000000 525.980000 (586.105106)
jruby (master)   0.000000   0.000000  66.050000 ( 65.739327)

The raw test data: https://gist.github.com/muga/b83262ed03009d2f765f8fd0c5319e54

@muga muga changed the title [WIP] Cache RubyDateParser object in ThreadContext to reduce the cost of object creations Cache RubyDateParser object in ThreadContext to reduce the cost of object creations Jun 18, 2017
@kares
Copy link
Member

kares commented Jun 18, 2017

first we should understand why its still min 5x slower than MRI. hane nothing against this but it might be optimization done in the wrong end ...

@muga
Copy link
Contributor Author

muga commented Jun 19, 2017

@kares Thank you for your comment. I'm closing this PR. and will reopen it if needed.

@muga muga closed this Jun 19, 2017
@enebo
Copy link
Member

enebo commented Jun 19, 2017

I have been looking at this a bit this afternoon and caching the parser 3x the current results on HEAD. I believe the cost is object creation and initialization overhead for somethign which runs short. Making stateless lexer/parser (e.g. single result values with inputs passed as params) or other patterns could reduce this allocation cost but at the moment it seems pretty big.

With the current state we are now down to about 6x slower so things are getting a little closer. The caching makes us only like 40% slower but I think we can get this cost down other ways first before thinking about it.

A lexer which used ByteList+Joni would be decent. Another one interesting is not creating a linked list of patterns. A smaller one is eliminating bag and populating the hash directly. A tiny one is not re-retrieving all symbols out of the symbol cache but making them fields somewhere.

@muga
Copy link
Contributor Author

muga commented Jun 19, 2017

@enebo Thank you for your comment. Make sense. I will take a profile later.

@enebo
Copy link
Member

enebo commented Jun 27, 2017

@muga @kares I am curious on number or potential issues with f2e7b82 ... I got pretty big speed jump but I was using a very trivial format string and perhaps the actual matching logic will show up being slower than MRI?

@kares
Copy link
Member

kares commented Jun 28, 2017

@enebo looking good, maybe I would not count on most apps being reasonable much, guess we'll see 😉

@muga
Copy link
Contributor Author

muga commented Jun 28, 2017

@enebo Great, looks good 👍 My application, Embulk, should get big benefits from your change. It usually receives a dozen of format strings during org.jruby.Ruby instance's life. strptimeFormatCache size never grows.

I'm not sure (I haven't seen) but, if there would be actual applications that need to parse timestamp strings by a lot of types of formats, this logic might be slower than MRI's. A kind of cache size limit (like -Djruby.xxx.cache=16) might be necessary in the future.

@enebo
Copy link
Member

enebo commented Jun 28, 2017

@muga the memory size of this cache probably scales into 10k-100k without a lot of memory. A linked list of strings for format values seems like it should take very little memory? Although we may end up adding a cache size in the future if anyone reports this as a leak...

@kares kares added this to the Invalid or Duplicate milestone Jun 28, 2017
@muga
Copy link
Contributor Author

muga commented Jun 28, 2017

@enebo Ah I see. The linked lists might not consume a lot of memory because the number of elements in one linked list is about 10 or 20 usually. Your change should be OK.

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.

None yet

3 participants