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

update HTTP.default_status_message_for to use official IANA Registry #4540

Merged
merged 1 commit into from Jun 15, 2017
Merged

update HTTP.default_status_message_for to use official IANA Registry #4540

merged 1 commit into from Jun 15, 2017

Conversation

ujifgc
Copy link
Contributor

@ujifgc ujifgc commented Jun 10, 2017

This PR moves HTTP status codes from 'http/common.cr' to 'http/status.cr' and updates them to conform with the official IANA registry of HTTP status codes: https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml.

Inspired by https://bugs.ruby-lang.org/issues/12935

@RX14
Copy link
Contributor

RX14 commented Jun 10, 2017

Can you benchmark HTTP.default_status_message_for before and after this change? I'm concerned that the hash lookup will be detrimental.

@ujifgc
Copy link
Contributor Author

ujifgc commented Jun 10, 2017

$ crystal bench.cr
Warning: benchmarking without the `--release` flag won't yield useful results
           user     system      total        real
case   0.860000   0.000000   0.860000 (  0.861665)
hash   0.210000   0.000000   0.210000 (  0.211948)

$ crystal bench.cr --release
           user     system      total        real
case   0.000000   0.000000   0.000000 (  0.002637)
hash   0.080000   0.000000   0.080000 (  0.069170)

Yes, it's significantly slower on release.

https://gist.github.com/ujifgc/479e0261bda871923f82efa4715342ab

Should I just extract this method to status.cr and add links and update instructions ?

@RX14
Copy link
Contributor

RX14 commented Jun 10, 2017

@ujifgc could you run that benchmark again using Benchmark.ips so we can see the performance difference properly?

We could use macros to create the case from the hash, which would allow applications to access the hash flexibly if needed but keep the performance of the current method.

@ujifgc
Copy link
Contributor Author

ujifgc commented Jun 10, 2017

$ crystal bench.cr
Warning: benchmarking without the `--release` flag won't yield useful results
case   1.16  (859.93ms) (± 0.22%)  4.06× slower
hash   4.72  (211.93ms) (± 0.16%)       fastest

$ crystal bench.cr --release
case 387.13  (  2.58ms) (± 0.27%)       fastest
hash  14.64  (  68.3ms) (± 0.08%) 26.44× slower

@jhass
Copy link
Member

jhass commented Jun 10, 2017

I guess as the number of codes increases, the case will only stay significantly faster if we order the common codes (200, 301, 302, 404, 403, 201, 500, 422, ...) to the top.

@ujifgc
Copy link
Contributor Author

ujifgc commented Jun 10, 2017

I'm new to macros. It this a proper way to generate this method?

  def self.default_status_message_for2(status_code : Int) : String
    {% for code, message in STATUS_CODES %}
    return {{message}} if status_code == {{code}}
    {% end %}
    ""
  end
$ crystal bench.cr
Warning: benchmarking without the `--release` flag won't yield useful results
 case   1.16  ( 859.4ms) (± 0.04%)  4.02× slower
 hash   4.68  (213.56ms) (± 0.31%)       fastest
macro   4.41  ( 226.6ms) (± 0.19%)  1.06× slower

$ crystal bench.cr --release
 case 379.81  (  2.63ms) (± 3.29%)  1.02× slower
 hash  14.64  ( 68.33ms) (± 0.33%) 26.44× slower
macro 386.94  (  2.58ms) (± 0.36%)       fastest

@Sija
Copy link
Contributor

Sija commented Jun 10, 2017

418 I'm a teapot (RFC 2324) got nuked with this, could we have it back? ;)

@RX14
Copy link
Contributor

RX14 commented Jun 10, 2017

@jhass that's assuming that llvm doesn't optimise it to a lookup table. I'm not sure if llvm can perform that optimization at all or if it would be up to crystal to do that.

@ujifgc please only put one iteration in the Benchmark.ips block so we can see the speed of one operation not many. ips is a full micro benchmark harness ich does everything for you.

@ujifgc
Copy link
Contributor Author

ujifgc commented Jun 10, 2017

$ crystal bench.cr
Warning: benchmarking without the `--release` flag won't yield useful results
 case sample   3.33M (300.44ns) (± 2.73%)  1.41× slower
 hash sample   4.55M ( 219.9ns) (± 1.10%)  1.03× slower
macro sample   4.69M (213.32ns) (± 0.25%)       fastest
Warning: benchmarking without the `--release` flag won't yield useful results
 case 100 134.06M (  7.46ns) (± 1.04%)  1.53× slower
 hash 100  39.11M ( 25.57ns) (± 1.06%)  5.24× slower
macro 100  205.0M (  4.88ns) (± 1.80%)       fastest
Warning: benchmarking without the `--release` flag won't yield useful results
 case 200  60.56M ( 16.51ns) (± 1.05%)  2.42× slower
 hash 200   40.2M ( 24.87ns) (± 0.72%)  3.65× slower
macro 200 146.82M (  6.81ns) (± 0.74%)       fastest
Warning: benchmarking without the `--release` flag won't yield useful results
 case 404  10.83M ( 92.35ns) (± 1.21%)  4.02× slower
 hash 404  35.93M ( 27.83ns) (± 6.41%)  1.21× slower
macro 404  43.57M ( 22.95ns) (± 0.24%)       fastest
Warning: benchmarking without the `--release` flag won't yield useful results
 case 500   5.45M (183.38ns) (± 0.50%)  7.37× slower
 hash 500  40.18M ( 24.89ns) (± 1.81%)       fastest
macro 500  24.75M (  40.4ns) (± 0.56%)  1.62× slower
$ crystal bench.cr --release
 case sample  43.51M ( 22.98ns) (± 4.75%)  1.01× slower
 hash sample  26.76M ( 37.37ns) (± 2.97%)  1.63× slower
macro sample  43.74M ( 22.86ns) (± 4.12%)       fastest
 case 100 341.41M (  2.93ns) (± 0.74%)       fastest
 hash 100 146.65M (  6.82ns) (± 2.96%)  2.33× slower
macro 100 341.29M (  2.93ns) (± 0.75%)  1.00× slower
 case 200  341.2M (  2.93ns) (± 0.81%)  1.00× slower
 hash 200 146.93M (  6.81ns) (± 1.01%)  2.32× slower
macro 200 341.21M (  2.93ns) (± 0.92%)       fastest
 case 404 341.27M (  2.93ns) (± 1.16%)  1.00× slower
 hash 404 134.24M (  7.45ns) (± 0.43%)  2.54× slower
macro 404 341.41M (  2.93ns) (± 0.68%)       fastest
 case 500 341.55M (  2.93ns) (± 0.64%)  1.00× slower
 hash 500 146.72M (  6.82ns) (± 2.47%)  2.33× slower
macro 500 341.59M (  2.93ns) (± 0.43%)       fastest

The Benchmark code is like this:

Benchmark.ips do |x|
  keys = HTTP::STATUS_CODES.keys
  x.report("case sample") { HTTP.default_status_message_for(keys.sample) }
  x.report("hash sample") { HTTP.default_status_message_for1(keys.sample) }
  x.report("macro sample") { HTTP.default_status_message_for2(keys.sample) }
end

[100, 200, 404, 500].each do |status|
  Benchmark.ips do |x|
    x.report("case #{status}") { HTTP.default_status_message_for(status) }
    x.report("hash #{status}") { HTTP.default_status_message_for1(status) }
    x.report("macro #{status}") { HTTP.default_status_message_for2(status) }
  end
end

https://gist.github.com/ujifgc/479e0261bda871923f82efa4715342ab

@ysbaddaden
Copy link
Contributor

Hash is significantly slower; using a macro increases complexity, and impacts compilation time.

What did this PR intended to achieve?

@RX14
Copy link
Contributor

RX14 commented Jun 10, 2017

I'm a little confused at why the macro-generated if statement is so much faster than the case here. As far as I recall, case statements were lowered to chained if statements. Shouldn't they be the same performance?

@asterite
Copy link
Member

For now, let's just add/fix the status code list without removing that method, moving it to a constant or using a hash literal. Please open a separate issue discussing how we can better model that, but just using the IANA registry list should be good for now.

@ujifgc ujifgc changed the title add HTTP::STATUS_CODES, use official IANA Registry update HTTP.default_status_message_for to use official IANA Registry Jun 10, 2017
@ujifgc
Copy link
Contributor Author

ujifgc commented Jun 10, 2017

I updated this PR so it just updates status codes, adds the comment on the authority info and a spec.

@mverzilli mverzilli merged commit f026c47 into crystal-lang:master Jun 15, 2017
@mverzilli
Copy link

Thanks ❤️ !

@mverzilli mverzilli added this to the Next milestone Jun 15, 2017
@ujifgc ujifgc deleted the http-status branch June 18, 2017 11:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants