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

Setup benchmark testing #1393

Merged
merged 2 commits into from
Mar 10, 2016
Merged

Setup benchmark testing #1393

merged 2 commits into from
Mar 10, 2016

Conversation

bf4
Copy link
Member

@bf4 bf4 commented Dec 23, 2015

Based on #872

Update March 8, 2016

PR now contains:

  • Setup dummy app files in test/dummy
  • Setup dummy test server `bin/serve_dummy
    • Note: Serializer caching can be completely disabled by passing in
      CACHE_ON=off bin/serve_dummy start since Serializer#_cache is only
      set at boot.
  • run with
    • ./bin/bench
    • bin/bench etc adapted from ruby-bench-suite
    • target files are test/dummy/bm_*.rb. Just add another to run it.
    • benchmark cache/no cache
    • remove rake dependency that loads unnecessary files
  • remove git gem dependency
    • Running over revisions to be added in subsequent PR
  • Removed

Update Feb 12, 2016

bin/bench runs files in test/dummy that start with bm_. type bin/bench --help for more on the options and see bm_caching.rb for an example implementation. Based on the ruby-bench-suite

bin/bench -e
caching on: caching serializers: gc off 805.7643471872851/ips   # turning gc on didn't seem to make a difference
caching off: caching serializers: gc off 774.1753998716134/ips
caching on: non-caching serializers: gc off 974.3776823158275/ips
caching off: non-caching serializers: gc off 875.7715343734802/ips
Benchmark results:
{
  "commit_hash": "fb376b7",
  "version": "0.10.0.rc4",
  "benchmark_run[environment]": "2.2.3p173",
  "runs": [
    {
      "benchmark_type[category]": "caching on: caching serializers: gc off",
      "benchmark_run[result][iterations_per_second]": 805.764,
      "benchmark_run[result][total_allocated_objects_per_iteration]": 1621
    },
    {
      "benchmark_type[category]": "caching off: caching serializers: gc off",
      "benchmark_run[result][iterations_per_second]": 774.175,
      "benchmark_run[result][total_allocated_objects_per_iteration]": 1621
    },
    {
      "benchmark_type[category]": "caching on: non-caching serializers: gc off",
      "benchmark_run[result][iterations_per_second]": 974.378,
      "benchmark_run[result][total_allocated_objects_per_iteration]": 1202
    },
    {
      "benchmark_type[category]": "caching off: non-caching serializers: gc off",
      "benchmark_run[result][iterations_per_second]": 875.772,
      "benchmark_run[result][total_allocated_objects_per_iteration]": 1202
    }
  ]

bin/bench -e CACHE_ON=off
caching on: caching serializers: gc off 932.0809512554744/ips
caching off: caching serializers: gc off 903.6931586377681/ips
caching on: non-caching serializers: gc off 1019.5621569821045/ips
caching off: non-caching serializers: gc off 938.1154973492726/ips
Benchmark results:
{
  "commit_hash": "fb376b7",
  "version": "0.10.0.rc4",
  "benchmark_run[environment]": "2.2.3p173",
  "runs": [
    {
      "benchmark_type[category]": "caching on: caching serializers: gc off",
      "benchmark_run[result][iterations_per_second]": 932.081,
      "benchmark_run[result][total_allocated_objects_per_iteration]": 1361
    },
    {
      "benchmark_type[category]": "caching off: caching serializers: gc off",
      "benchmark_run[result][iterations_per_second]": 903.693,
      "benchmark_run[result][total_allocated_objects_per_iteration]": 1361
    },
    {
      "benchmark_type[category]": "caching on: non-caching serializers: gc off",
      "benchmark_run[result][iterations_per_second]": 1019.562,
      "benchmark_run[result][total_allocated_objects_per_iteration]": 1202
    },
    {
      "benchmark_type[category]": "caching off: non-caching serializers: gc off",
      "benchmark_run[result][iterations_per_second]": 938.115,
      "benchmark_run[result][total_allocated_objects_per_iteration]": 1202
    }
  ]
}

The caching slowdown here matches what was described in https://blog.codeship.com/building-a-json-api-with-rails-5/

Original PR description below for posterity

Usage: bin/bench with optional arguments <ref1> <ref2>. <ref1> defaults to the current branch and <ref2> defaults to the master branch. bin/branch current will run on the current branch


Comparison:
792fb8a9053f8db3c562dae4f40907a582dd1720--caching on: caching serializers:        0.6 i/s
master--caching on: caching serializers:        0.3 i/s - 1.83x slower



Comparison:
792fb8a9053f8db3c562dae4f40907a582dd1720--caching off: caching serializers:        0.6 i/s
master--caching off: caching serializers:        0.3 i/s - 1.83x slower



Comparison:
792fb8a9053f8db3c562dae4f40907a582dd1720--caching on: non-caching serializers:        0.6 i/s
master--caching on: non-caching serializers:        0.3 i/s - 2.09x slower



Comparison:
792fb8a9053f8db3c562dae4f40907a582dd1720--caching off: non-caching serializers:        0.6 i/s
master--caching off: non-caching serializers:        0.3 i/s - 2.16x slower
ref caching on: caching serializers [ips (stdev)] caching off: caching serializers [ips (stdev)] caching on: non-caching serializers [ips (stdev)] caching off: non-caching serializers [ips (stdev)] vs. earliest ref (6a0564a) (x faster) caching speedup (on / off)
6a0564a 1064.0346 (142) 1066.4938 (155) 1087.5685 (168) 1139.758 (86) 1.0 0.9977 / 0.9542
cf1c57d 1174.7944 (101) 1188.6521 (60) 1123.4026 (127) 1123.327 (110) 1.1041 0.9883 / 1.0001
7a62d31 1184.2514 (77) 1148.305 (100) 1175.3671 (76) 1177.1972 (76) 1.113 1.0313 / 0.9984
9355416 1113.2661 (91) 1136.5814 (84) 1114.7285 (89) 1117.7963 (92) 1.0463 0.9795 / 0.9973
a40df8f 1160.8604 (74) 1157.8892 (82) 1162.0469 (88) 1166.6558 (74) 1.091 1.0026 / 0.996
e117615 1149.0035 (93) 1123.5847 (94) 1132.4047 (80) 1152.9832 (94) 1.0799 1.0226 / 0.9822
37114e9 1149.9465 (85) 1163.5836 (69) 1083.1217 (137) 1094.4516 (84) 1.0807 0.9883 / 0.9896
d061b2e 900.8557 (118) 947.2523 (88) 932.4367 (112) 855.1346 (125) 0.8466 0.951 / 1.0904
81935c8 910.2482 (168) 845.0272 (134) 946.8795 (132) 997.8861 (72) 0.8555 1.0772 / 0.9489
d3649d5 943.9228 (126) 966.0014 (128) 840.4264 (204) 902.7007 (139) 0.8871 0.9771 / 0.931
952d8ad 904.0438 (104) 902.935 (138) 878.1083 (126) 895.6053 (110) 0.8496 1.0012 / 0.9805
506739d 1023.5322 (71) 1006.1904 (88) 953.5007 (118) 826.2965 (215) 0.9619 1.0172 / 1.1539
456f915 978.3657 (86) 1003.4718 (95) 1018.4155 (65) 1003.507 (94) 0.9195 0.975 / 1.0149
6266b6a 1000.2075 (73) 938.3252 (118) 1022.2972 (66) 991.6113 (109) 0.94 1.0659 / 1.0309
7bc1666 1003.8724 (134) 1026.1987 (79) 1055.1638 (65) 972.6973 (98) 0.9435 0.9782 / 1.0848
f6e3d4e 1043.22 (74) 1040.2331 (84) 572.0073 (76) 567.0417 (59) 0.9804 1.0029 / 1.0088
8568ed5 1058.2187 (68) 1071.5488 (58) 580.5213 (53) 575.1097 (49) 0.9945 0.9876 / 1.0094
f7c77c1 981.5471 (65) 944.739 (58) 534.9561 (76) 550.2427 (62) 0.9225 1.039 / 0.9722
77a8f66 908.0118 (83) 939.7208 (62) 536.9038 (62) 525.0104 (79) 0.8534 0.9663 / 1.0227
b73ffe2 872.0797 (106) 924.3929 (78) 525.2912 (62) 517.7921 (63) 0.8196 0.9434 / 1.0145
7afdad8 1007.062 (55) 1000.6449 (70) 576.3515 (61) 571.5198 (49) 0.9465 1.0064 / 1.0085
bac812f 1003.1465 (56) 1006.2235 (57) 560.1034 (60) 573.7427 (51) 0.9428 0.9969 / 0.9762
343f8b9 1010.0868 (67) 1020.4852 (46) 563.3218 (54) 568.6059 (47) 0.9493 0.9898 / 0.9907
f7612f2 990.8702 (61) 976.6378 (74) 531.0616 (56) 541.5315 (52) 0.9312 1.0146 / 0.9807
bdfe13c 1023.8082 (61) 1025.0687 (51) 565.156 (55) 572.5836 (47) 0.9622 0.9988 / 0.987
f27f13c 979.337 (75) 995.1732 (51) 540.9404 (47) 560.2987 (54) 0.9204 0.9841 / 0.9655
8634503 1016.3668 (59) 1017.354 (53) 573.0917 (51) 571.0286 (56) 0.9552 0.999 / 1.0036
a9d07cd 996.9842 (51) 982.0299 (73) 548.2356 (52) 553.2191 (53) 0.937 1.0152 / 0.991
ce7a839 1017.9673 (78) 1024.0641 (69) 571.7085 (52) 560.7853 (64) 0.9567 0.994 / 1.0195
d7534a6 996.1083 (73) 1019.4502 (63) 578.4693 (57) 580.3471 (52) 0.9362 0.9771 / 0.9968
26277ea 1034.5458 (48) 1044.6784 (59) 540.3205 (96) 544.9802 (93) 0.9723 0.9903 / 0.9914
0091be8 1040.4204 (58) 1036.6299 (74) 579.9426 (67) 558.0834 (70) 0.9778 1.0037 / 1.0392
701eb17 967.3361 (75) 920.6767 (86) 508.8966 (80) 543.0353 (57) 0.9091 1.0507 / 0.9371
ab1e2af 907.1604 (120) 965.4726 (93) 558.7317 (59) 537.2244 (53) 0.8526 0.9396 / 1.04
8f2dd66 1021.9825 (51) 1016.6125 (72) 564.2432 (73) 534.0571 (68) 0.9605 1.0053 / 1.0565
359f290 976.5442 (95) 969.3181 (63) 543.2991 (54) 496.5705 (69) 0.9178 1.0075 / 1.0941
2dd569a 955.5897 (81) 984.0243 (66) 514.3126 (97) 343.0799 (146) 0.8981 0.9711 / 1.4991
b8781c4 908.4687 (107) 937.0411 (78) 482.696 (64) 386.3403 (67) 0.8538 0.9695 / 1.2494
5706e7d 929.1165 (72) 931.7516 (75) 534.4808 (58) 530.1489 (65) 0.8732 0.9972 / 1.0082
eccb359 929.0141 (118) 916.9969 (70) 497.9641 (70) 496.4119 (85) 0.8731 1.0131 / 1.0031
526b56e 976.5585 (72) 977.6628 (61) 537.4672 (45) 554.1429 (48) 0.9178 0.9989 / 0.9699
73cb9f7 951.194 (50) 934.3032 (68) 544.9307 (49) 551.0009 (53) 0.894 1.0181 / 0.989
86fa7a9 932.5861 (71) 954.0019 (64) 528.6259 (38) 541.1496 (44) 0.8765 0.9776 / 0.9769
cf34a66 1023.9295 (47) 1016.7152 (51) 544.2685 (57) 532.5794 (51) 0.9623 1.0071 / 1.0219
168ec0e 982.9171 (60) 953.5031 (92) 557.5438 (51) 572.7305 (41) 0.9238 1.0308 / 0.9735
31172b1 1014.4365 (43) 999.6442 (57) 556.0305 (61) 552.3549 (49) 0.9534 1.0148 / 1.0067
d1c44c7 909.7585 (48) 920.4687 (44) 532.91 (40) 538.3787 (44) 0.855 0.9884 / 0.9898
a9ce4fb 909.3205 (60) 922.7608 (46) 525.1773 (41) 523.2841 (44) 0.8546 0.9854 / 1.0036
170db3b 883.5954 (58) 871.3582 (80) 506.8117 (48) 519.1062 (47) 0.8304 1.014 / 0.9763
7cbef1b 892.1101 (50) 886.7945 (48) 524.6199 (44) 485.2093 (66) 0.8384 1.006 / 1.0812
3e8290a 851.6867 (57) 877.1135 (61) 505.6237 (51) 519.8595 (39) 0.8004 0.971 / 0.9726
488370f 894.6282 (44) 901.1973 (48) 513.9783 (57) 513.3232 (46) 0.8408 0.9927 / 1.0013
58937f4 901.7422 (43) 912.1338 (45) 525.5763 (45) 522.9754 (43) 0.8475 0.9886 / 1.005
94ca0e0 896.5575 (57) 857.7918 (66) 472.1844 (59) 504.9971 (47) 0.8426 1.0452 / 0.935
fd06a8a 886.9686 (41) 891.8515 (40) 522.3645 (39) 519.1874 (44) 0.8336 0.9945 / 1.0061

Using bin/bench revisions 792fb8a9053f8db3c562dae4f40907a582dd1720 | tee data.txt

# given = <<-OUTPUT
# 6a0564a241baf3da414874ada567be2cef99cae5
#         [{"name"=>"caching on: caching serializers", "ips"=>1064.034614720691, "stddev"=>142}, {"name"=>"caching off: caching serializers", "ips"=>1066.4938325086475, "stddev"=>155}, {"name"=>"caching on: non-caching serializers", "ips"=>1087.5685499776823, "stddev"=>168}, {"name"=>"caching off: non-caching serializers", "ips"=>1139.758005558973, "stddev"=>86}]
# 6a0564a241baf3da414874ada567be2cef99cae5
#         [{"name"=>"caching on: caching serializers", "ips"=>1064.034614720691, "stddev"=>142}, {"name"=>"caching off: caching serializers", "ips"=>1066.4938325086475, "stddev"=>155}, {"name"=>"caching on: non-caching serializers", "ips"=>1087.5685499776823, "stddev"=>168}, {"name"=>"caching off: non-caching serializers", "ips"=>1139.758005558973, "stddev"=>86}]
# OUTPUT

def write(content)
  f = STDOUT
  yield f
end

def parse_line(line)
  eval(line[1].strip).each_with_object('ref' => line[0]) {|entry, result|
    key = entry.delete('name')
    result[key] = entry
  }
end

given = File.read('data.txt')
lines = given.split("\n")

first_entry_key = 'caching on: caching serializers'
first_entry = parse_line(lines.first(2))
first_entry_ref = first_entry['ref']
first_entry_value = Float(first_entry[first_entry_key]['ips'])
headers = [
  'ref',
  'caching on: caching serializers',
  'caching off: caching serializers',
  'caching on: non-caching serializers',
  'caching off: non-caching serializers',
  "vs. earliest ref (#{first_entry_ref[0..8]}) (x faster)",
  "caching speedup (on / off)"
]
write(lines) do |f|
  f.write '|'
  f.write headers.join(' | ')
  f.write "|\n|"
  f.write (0..headers.size).map { '----' }.join(' | ')
  f.write "|"
  lines.each_slice(2) do |l|
    l = parse_line(l)
    row = []
    headers.each do |key|
      entry = l[key]
      row <<
      case entry
      when String then entry[0..8]
      when Hash then "#{Float(entry['ips']).round(4)} (#{entry['stddev']})"
      else
        if key =~ /ref/
          # calculate from oldest ref
          (Float(l[first_entry_key]['ips']) / first_entry_value).round(4)
        elsif key =~ /speedup/
          caching = (Float(l['caching on: caching serializers']['ips']) / Float(l['caching off: caching serializers']['ips'])).round(4)
          non_caching = (Float(l['caching on: non-caching serializers']['ips']) / Float(l['caching off: non-caching serializers']['ips'])).round(4)
          "#{caching} / #{non_caching}"
        else
          fail "#{key} isn't a know key in #{l}"
        end
      end
    end
    f.write "\n|"
    f.write row.join(' | ')
  end
  f.write "\n"
end

AMS Benchmark tests #832

Adding a benchmak test structure to help contributors to keep track of how
their PR will impact overall performance.

It enables developers to create test inside of tests/benchmark.

This implementation adds a rake task: rake benchmark that checkout
one commit before, run the test of tests/benchmark, then mover back to the
last commit and run it again. By comparing the benchmark results between
both commits the contributor will notice if and how much his contribution
will impact overall performance.

end
end
end
class Comment < Model; end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we consistently preferred Comment = Class.new(Model) for empty class definitions.

@beauby
Copy link
Contributor

beauby commented Dec 27, 2015

I'm generally 👍 on this. We definitely need a benchmark.

@bf4 bf4 force-pushed the benchmarking branch 2 times, most recently from 43e4e3c to 1a19b3f Compare December 28, 2015 07:30
get action
i += 1
end
end
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I were benchmarking on one branch, I'd put the benchmark code in here. But, because we're also comparing branches, I haven't settled on where I think the benchmarking should happen: in the test, or in the tool?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what you mean, but a possible solution would be to make a benchmark directory in which you clone both branches.

require 'pathname'
require 'shellwords'
require 'English'

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Document usage up here

This was referenced Dec 30, 2015
@beauby
Copy link
Contributor

beauby commented Jan 4, 2016

This PR is clearly a must. Let me know if I can help.

@bf4
Copy link
Member Author

bf4 commented Jan 4, 2016

@beauby If you think the current form is good enough to work from, I'd say merge. Lemme add some docs on what bin/bench does and how to use it (and misuse it). You thinking CONTRIBUTING is the right place or a DEVELOPERS?

@bf4 bf4 force-pushed the benchmarking branch 2 times, most recently from c8c7212 to 5f78e5e Compare January 4, 2016 06:47
@bf4
Copy link
Member Author

bf4 commented Jan 4, 2016

Yeah, I am having a hard time getting the interface right to do what i want... Eg find the commit where caching broke

B mobile phone

On Jan 3, 2016, at 9:13 PM, Lucas Hosseini notifications@github.com wrote:

This PR is clearly a must. Let me know if I can help.


Reply to this email directly or view it on GitHub.

@bf4
Copy link
Member Author

bf4 commented Jan 5, 2016

@beauby I updated this with, I think, a much better design. No longer uses minitest. Also see #1412 where I added the dummy app server used in this PR

# TODO: support /caching/{on,off}
def get_caching
get(url_base + "/caching")
end
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

basically, the benchmark is a some combination or requests made against the server spawned at that ref. This way, everything can be controlled from the benchmarking file. e.g., if I wanted to run benchmark-ips, I would run that in here, not in the dummy controller. I think this makes it more flexible.

Also, cache toggling is now implemented in the server

+Rails.application.routes.draw do
+  get '/status(/:on)' => 'post#render_cache_status'
+  get '/caching(/:on)' => 'post#render_with_caching_serializer'
+  get '/non_caching(/:on)' => 'post#render_with_non_caching_serializer'
+end


output = {
label: label,
version: ::ActiveModel::Serializer::VERSION.to_s,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • rails_version: :Rails.version.to_s

@bf4 bf4 force-pushed the benchmarking branch 2 times, most recently from 9c6c9a6 to c957411 Compare February 16, 2016 04:37
@ianks
Copy link

ianks commented Feb 18, 2016

@bf4 This looks really solid. Out of curiousity, how would one go about adding custom benchmarks to this framework? For example, how could my example in #1511 fit in this model?

@bf4
Copy link
Member Author

bf4 commented Feb 19, 2016

@ianks Yeah, you'd stick it in test/dummy with a name that begins with bm_. I should probably rename dummy to benchmark

@bf4 bf4 added this to the 0.10 milestone Feb 26, 2016
@@ -10,7 +10,7 @@ rvm:
- ruby-head
- rbx-2

install: bundle install --path=vendor/bundle --retry=3 --jobs=3
install: bundle install --path=vendor/bundle --retry=3 --jobs=3 --without bench
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like travis is not working because it uses an old version of bundler that does not handle the --without parameter as expected. We can either follow travis-ci/travis-ci#3531 (comment) or maybe use --without development bench?

@bf4 bf4 changed the title Add benchmarks testing; compare across branches Add benchmarks testing Mar 9, 2016
@bf4 bf4 changed the title Add benchmarks testing Setup benchmark testing Mar 9, 2016
@bf4
Copy link
Member Author

bf4 commented Mar 9, 2016

I just squashed the work, updated the loggers, and removed all the revision running code and extra per tools.

TODO:

  • Docs
    • when and how devs should use
    • command line options to bin/bench
      bin/bench --help

      Usage: bin/bench [options]

      -r, --repeat-count [NUM] Run benchmarks [NUM] times taking the best result

      -p <PATTERN1,PATTERN2,PATTERN3>, Benchmark name pattern

      --pattern

      -e <var1=val1,var2=val2,var3=vale>,

      --env ENV variables to pass in
    • ENV vars that can be passed to bin/bench
      • SUMMARIZE, CACHE_ON, FAIL_ASSERTION, DEBUG, BENCH_STRESS, ENABLE_ACTIVE_RECORD
    • adding another runner (just model off of exiting)
  • consolidate dummy app with isolated app and test helper app
  • consider renaming dummy to benchmark
  • consider removing from this PR:
    • active record
    • bench stress
    • bin/serve_dummy
    • summary
    • logger options
  • review for dead code, options, comments, etc.
  • add Rails version to output Setup benchmark testing #1393 (comment)
  • after merge followup with @ianks in Add benchmarks for adapters #1511
  • decide what goes in a followup pr

@@ -1,6 +1,7 @@
inherit_from: .rubocop_todo.yml

AllCops:
TargetRubyVersion: 2.2
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

target ruby version!

joaomdmoura and others added 2 commits March 9, 2016 12:42
Adding a benchmak test structure to help contributors to keep track
of how their PR will impact overall performance.

It enables developers to create test inside of tests/benchmark.

This implementation adds a rake task: ```rake benchmark``` that checkout
one commit before, run the test of  tests/benchmark, then mover back to
the last commit and run it again. By comparing the benchmark results between
both commits the contributor will notice if and how much  his contribution
will impact overall performance.
- Setup dummy app files in `test/dummy`
- Setup dummy test server `bin/serve_dummy
  - Note:  Serializer caching can be completely disabled by passing in
  `CACHE_ON=off bin/serve_dummy start` since Serializer#_cache is only
  set at boot.
- run with
  - ./bin/bench
  - `bin/bench` etc adapted from ruby-bench-suite
  - target files are `test/dummy/bm_*.rb`. Just add another to run it.
  - benchmark cache/no cache
  - remove rake dependency that loads unnecessary files
- remove git gem dependency
  - Running over revisions to be added in subsequent PR
@NullVoxPopuli
Copy link
Contributor

LGTM

NullVoxPopuli added a commit that referenced this pull request Mar 10, 2016
@NullVoxPopuli NullVoxPopuli merged commit 821dcda into rails-api:master Mar 10, 2016
@bf4 bf4 mentioned this pull request Mar 10, 2016
25 tasks
@bf4 bf4 deleted the benchmarking branch March 10, 2016 04:10
@bf4
Copy link
Member Author

bf4 commented Mar 10, 2016

Followup issue in #1575

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.

6 participants