rubocop-isucon 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/gh-pages.yml +44 -0
  3. data/.github/workflows/test.yml +91 -0
  4. data/.gitignore +13 -0
  5. data/.rspec +3 -0
  6. data/.rubocop.yml +43 -0
  7. data/.yardopts +7 -0
  8. data/CHANGELOG.md +6 -0
  9. data/Gemfile +8 -0
  10. data/LICENSE.txt +21 -0
  11. data/README.md +108 -0
  12. data/Rakefile +35 -0
  13. data/benchmark/README.md +69 -0
  14. data/benchmark/memorize.rb +86 -0
  15. data/benchmark/parse_table.rb +103 -0
  16. data/benchmark/shell.rb +26 -0
  17. data/bin/console +15 -0
  18. data/bin/setup +8 -0
  19. data/config/default.yml +83 -0
  20. data/config/enable-only-performance.yml +30 -0
  21. data/gemfiles/activerecord_6_1.gemfile +14 -0
  22. data/gemfiles/activerecord_7_0.gemfile +14 -0
  23. data/lib/rubocop/cop/isucon/correctors/mysql2_n_plus_one_query_corrector/correctable_methods.rb +66 -0
  24. data/lib/rubocop/cop/isucon/correctors/mysql2_n_plus_one_query_corrector/replace_methods.rb +127 -0
  25. data/lib/rubocop/cop/isucon/correctors/mysql2_n_plus_one_query_corrector.rb +112 -0
  26. data/lib/rubocop/cop/isucon/mixin/database_methods.rb +59 -0
  27. data/lib/rubocop/cop/isucon/mixin/mysql2_xquery_methods.rb +176 -0
  28. data/lib/rubocop/cop/isucon/mixin/sinatra_methods.rb +37 -0
  29. data/lib/rubocop/cop/isucon/mysql2/join_without_index.rb +100 -0
  30. data/lib/rubocop/cop/isucon/mysql2/many_join_table.rb +86 -0
  31. data/lib/rubocop/cop/isucon/mysql2/n_plus_one_query.rb +179 -0
  32. data/lib/rubocop/cop/isucon/mysql2/prepare_execute.rb +136 -0
  33. data/lib/rubocop/cop/isucon/mysql2/select_asterisk.rb +171 -0
  34. data/lib/rubocop/cop/isucon/mysql2/where_without_index.rb +105 -0
  35. data/lib/rubocop/cop/isucon/shell/backtick.rb +36 -0
  36. data/lib/rubocop/cop/isucon/shell/system.rb +36 -0
  37. data/lib/rubocop/cop/isucon/sinatra/disable_logging.rb +83 -0
  38. data/lib/rubocop/cop/isucon/sinatra/logger.rb +52 -0
  39. data/lib/rubocop/cop/isucon/sinatra/rack_logger.rb +58 -0
  40. data/lib/rubocop/cop/isucon/sinatra/serve_static_file.rb +73 -0
  41. data/lib/rubocop/cop/isucon_cops.rb +20 -0
  42. data/lib/rubocop/isucon/database_connection.rb +42 -0
  43. data/lib/rubocop/isucon/gda/client.rb +184 -0
  44. data/lib/rubocop/isucon/gda/gda_ext.rb +119 -0
  45. data/lib/rubocop/isucon/gda/join_condition.rb +25 -0
  46. data/lib/rubocop/isucon/gda/join_operand.rb +46 -0
  47. data/lib/rubocop/isucon/gda/node_location.rb +42 -0
  48. data/lib/rubocop/isucon/gda/node_patcher.rb +101 -0
  49. data/lib/rubocop/isucon/gda/where_condition.rb +73 -0
  50. data/lib/rubocop/isucon/gda/where_operand.rb +32 -0
  51. data/lib/rubocop/isucon/gda.rb +28 -0
  52. data/lib/rubocop/isucon/inject.rb +20 -0
  53. data/lib/rubocop/isucon/memorize_methods.rb +38 -0
  54. data/lib/rubocop/isucon/version.rb +7 -0
  55. data/lib/rubocop/isucon.rb +20 -0
  56. data/lib/rubocop-isucon.rb +16 -0
  57. data/rubocop-isucon.gemspec +52 -0
  58. metadata +286 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: c235faec7d2edaadc48681992ad139ffc81d0745e010d923132a509f8012daa3
4
+ data.tar.gz: eecb16448d52f4a48860171045e9fc6b6108ce68db7d5aa4601a6fcc865828b0
5
+ SHA512:
6
+ metadata.gz: 2a82eca957548836926ef797d18ed5e014ab26c03407b1bdfd82a659b082479f1d5ede81c05baacd0c172b5856b38b839262fb9ba238618b03597f524f2ddeda
7
+ data.tar.gz: 3571157d23e59636c0af87617111d0146c00dedfe3da74f4b64fd4e4e108886ef32d1fa56f65f531578be251eca6f0c8182a263cec18c098c497cd9fcc8be6a3
@@ -0,0 +1,44 @@
1
+ name: gh-pages
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+
8
+ jobs:
9
+ publish_yard:
10
+ runs-on: ubuntu-latest
11
+
12
+ steps:
13
+ - uses: actions/checkout@v2
14
+
15
+ - name: Install packages
16
+ run: |
17
+ set -xe
18
+ sudo apt-get update
19
+ sudo apt-get install -y libgda-5.0
20
+
21
+ - uses: ruby/setup-ruby@v1
22
+ with:
23
+ ruby-version: "3.1"
24
+ bundler-cache: true
25
+
26
+ - run: bundle exec yard
27
+
28
+ - name: Deploy to GitHub Pages
29
+ uses: peaceiris/actions-gh-pages@v3
30
+ with:
31
+ github_token: ${{ secrets.GITHUB_TOKEN }}
32
+ publish_dir: ./doc
33
+ publish_branch: gh-pages
34
+
35
+ - name: Slack Notification (not success)
36
+ uses: lazy-actions/slatify@master
37
+ if: "! success()"
38
+ continue-on-error: true
39
+ with:
40
+ job_name: ${{ format('*publish_yard*') }}
41
+ type: ${{ job.status }}
42
+ icon_emoji: ":octocat:"
43
+ url: ${{ secrets.SLACK_WEBHOOK }}
44
+ token: ${{ secrets.GITHUB_TOKEN }}
@@ -0,0 +1,91 @@
1
+ name: test
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ pull_request:
8
+ types:
9
+ - opened
10
+ - synchronize
11
+ - reopened
12
+ schedule:
13
+ - cron: "0 10 * * 5" # JST 19:00 (Fri)
14
+
15
+ jobs:
16
+ test:
17
+ runs-on: ubuntu-latest
18
+
19
+ strategy:
20
+ fail-fast: false
21
+
22
+ matrix:
23
+ ruby:
24
+ - "2.6"
25
+ - "2.7"
26
+ - "3.0"
27
+ - "3.1"
28
+ gemfile:
29
+ - activerecord_6_1
30
+ - activerecord_7_0
31
+ exclude:
32
+ # activerecord 7.0+ requires Ruby 2.7+
33
+ - ruby: "2.6"
34
+ gemfile: activerecord_7_0
35
+
36
+ env:
37
+ BUNDLE_GEMFILE: gemfiles/${{ matrix.gemfile }}.gemfile
38
+
39
+ steps:
40
+ - uses: actions/checkout@v2
41
+
42
+ - name: Install packages
43
+ run: |
44
+ set -xe
45
+ sudo apt-get update
46
+ sudo apt-get install -y libgda-5.0
47
+
48
+ - uses: ruby/setup-ruby@v1
49
+ with:
50
+ ruby-version: ${{ matrix.ruby }}
51
+ bundler-cache: true
52
+ cache-version: ${{ matrix.gemfile }}
53
+
54
+ - name: bundle update
55
+ run: |
56
+ set -xe
57
+ bundle config path vendor/bundle
58
+ bundle update --jobs $(nproc) --retry 3
59
+
60
+ - run: bundle exec rspec
61
+ - run: bundle exec rubocop
62
+ - run: bundle exec yard --fail-on-warning
63
+
64
+ - name: Slack Notification (not success)
65
+ uses: lazy-actions/slatify@master
66
+ if: "! success()"
67
+ continue-on-error: true
68
+ with:
69
+ job_name: ${{ format('*build* ({0}, {1})', matrix.ruby, matrix.gemfile) }}
70
+ type: ${{ job.status }}
71
+ icon_emoji: ":octocat:"
72
+ url: ${{ secrets.SLACK_WEBHOOK }}
73
+ token: ${{ secrets.GITHUB_TOKEN }}
74
+
75
+ notify:
76
+ needs:
77
+ - test
78
+
79
+ runs-on: ubuntu-latest
80
+
81
+ steps:
82
+ - name: Slack Notification (success)
83
+ uses: lazy-actions/slatify@master
84
+ if: always()
85
+ continue-on-error: true
86
+ with:
87
+ job_name: '*build*'
88
+ type: ${{ job.status }}
89
+ icon_emoji: ":octocat:"
90
+ url: ${{ secrets.SLACK_WEBHOOK }}
91
+ token: ${{ secrets.GITHUB_TOKEN }}
data/.gitignore ADDED
@@ -0,0 +1,13 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
12
+
13
+ Gemfile.lock
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format progress
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,43 @@
1
+ require:
2
+ - rubocop-performance
3
+
4
+ AllCops:
5
+ NewCops: enable
6
+ SuggestExtensions: false
7
+ TargetRubyVersion: 2.6
8
+ Exclude:
9
+ - 'gemfiles/vendor/**/*'
10
+ - 'benchmark/**/*'
11
+
12
+ # https://github.com/rubocop/rubocop/blob/v1.20.0/config/default.yml
13
+ - 'node_modules/**/*'
14
+ - 'tmp/**/*'
15
+ - 'vendor/**/*'
16
+ - '.git/**/*'
17
+
18
+ Layout/DotPosition:
19
+ EnforcedStyle: trailing
20
+
21
+ Layout/LineLength:
22
+ Max: 130
23
+
24
+ Metrics/BlockLength:
25
+ Exclude:
26
+ - "spec/**/*"
27
+ - "*.gemspec"
28
+
29
+ Naming/FileName:
30
+ Exclude:
31
+ - lib/rubocop-isucon.rb
32
+
33
+ Style/StringLiterals:
34
+ EnforcedStyle: double_quotes
35
+
36
+ Style/TrailingCommaInArguments:
37
+ EnforcedStyleForMultiline: comma
38
+
39
+ Style/TrailingCommaInArrayLiteral:
40
+ EnforcedStyleForMultiline: comma
41
+
42
+ Style/TrailingCommaInHashLiteral:
43
+ EnforcedStyleForMultiline: comma
data/.yardopts ADDED
@@ -0,0 +1,7 @@
1
+ --markup markdown
2
+ --markup-provider redcarpet
3
+ --no-private
4
+ --hide-void-return
5
+ -
6
+ CHANGELOG.md
7
+ LICENSE.txt
data/CHANGELOG.md ADDED
@@ -0,0 +1,6 @@
1
+ ## [Unreleased]
2
+ [full changelog](http://github.com/sue445/rubocop-isucon/compare/v0.1.0...main)
3
+
4
+ ## [0.1.0] - 2022-07-23
5
+
6
+ - Initial release
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in rubocop-isucon.gemspec
6
+ gemspec
7
+
8
+ # eval_gemfile "#{__dir__}/gemfiles/common.gemfile"
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 sue445
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,108 @@
1
+ # RuboCop ISUCON
2
+ RuboCop plugin for ruby reference implementation of [ISUCON](https://github.com/isucon)
3
+
4
+ [![Build Status](https://github.com/sue445/rubocop-isucon/workflows/test/badge.svg?branch=main)](https://github.com/sue445/rubocop-isucon/actions?query=workflow%3Atest)
5
+
6
+ ## Installation
7
+ At first, install [libgda](https://gitlab.gnome.org/GNOME/libgda)
8
+
9
+ ### for Mac (recommended)
10
+ ```bash
11
+ brew install libgda
12
+ ```
13
+
14
+ ### for Ubuntu, Debian (recommended)
15
+ ```bash
16
+ apt-get install -y libgda-5.0
17
+ ```
18
+
19
+ ### for CentOS 7
20
+ ```bash
21
+ yum install -y epel-release
22
+ yum --enablerepo=epel install -y libgda-devel
23
+ ```
24
+
25
+ ### for CentOS 8+
26
+ ```bash
27
+ dnf install -y https://pkgs.dyn.su/el8/base/x86_64/raven-release-1.0-2.el8.noarch.rpm
28
+ dnf --enablerepo=raven install -y libgda-devel
29
+ ```
30
+
31
+ ### Installing gem
32
+ Add this line to your application's Gemfile:
33
+
34
+ ```ruby
35
+ group :development do
36
+ gem 'rubocop-isucon', require: false
37
+ end
38
+ ```
39
+
40
+ And then execute:
41
+
42
+ $ bundle install
43
+
44
+ Or install it yourself as:
45
+
46
+ $ gem install rubocop-isucon
47
+
48
+ ## Usage
49
+
50
+ Add this line to your application's `.rubocop.yml`
51
+
52
+ ```yaml
53
+ require:
54
+ - rubocop-isucon
55
+
56
+ inherit_gem:
57
+ rubocop-isucon:
58
+ # Disable default cops (except Performance cops)
59
+ - "config/enable-only-performance.yml"
60
+
61
+ AllCops:
62
+ NewCops: enable
63
+ DisplayStyleGuide: true
64
+ # TargetRubyVersion: 3.1
65
+
66
+ Isucon/Mysql2:
67
+ Database:
68
+ adapter: mysql2
69
+ host: # TODO: Fix this
70
+ database: # TODO: Fix this
71
+ username: isucon
72
+ password: isucon
73
+ encoding: utf8
74
+ port: 3306
75
+ ```
76
+
77
+ `Database` isn't configured in `.rubocop.yml`, some cops doesn't work
78
+
79
+ | cop | offense detection | auto-correct |
80
+ |-----------------------------------|----------------------------|----------------------------|
81
+ | `Isucon/Mysql2/JoinWithoutIndex` | `Database` is **required** | Not supported |
82
+ | `Isucon/Mysql2/NPlusOneQuery` | `Database` is optional | `Database` is **required** |
83
+ | `Isucon/Mysql2/SelectAsterisk` | `Database` is optional | `Database` is **required** |
84
+ | `Isucon/Mysql2/WhereWithoutIndex` | `Database` is **required** | Not supported |
85
+
86
+ ## Documentation
87
+ See. https://sue445.github.io/rubocop-isucon/
88
+
89
+ * `Isucon/Mysql2` department docs : https://sue445.github.io/rubocop-isucon/RuboCop/Cop/Isucon/Mysql2.html
90
+ * `Isucon/Shell` department docs : https://sue445.github.io/rubocop-isucon/RuboCop/Cop/Isucon/Shell.html
91
+ * `Isucon/Sinatra` department docs : https://sue445.github.io/rubocop-isucon/RuboCop/Cop/Isucon/Sinatra.html
92
+
93
+ ## Benchmark
94
+ See [benchmark/](benchmark/)
95
+
96
+ ## Development
97
+
98
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
99
+
100
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
101
+
102
+ ## Contributing
103
+
104
+ Bug reports and pull requests are welcome on GitHub at https://github.com/sue445/rubocop-isucon.
105
+
106
+ ## License
107
+
108
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]
13
+
14
+ RSpec::Core::RakeTask.new(:spec) do |spec|
15
+ spec.pattern = FileList["spec/**/*_spec.rb"]
16
+ end
17
+
18
+ desc "Generate a new cop with a template"
19
+ task :new_cop, [:cop] do |_task, args|
20
+ require "rubocop"
21
+
22
+ cop_name = args.fetch(:cop) do
23
+ warn "usage: bundle exec rake new_cop[Department/Name]"
24
+ exit!
25
+ end
26
+
27
+ generator = RuboCop::Cop::Generator.new(cop_name)
28
+
29
+ generator.write_source
30
+ generator.write_spec
31
+ generator.inject_require(root_file_path: "lib/rubocop/cop/isucon_cops.rb")
32
+ generator.inject_config(config_file_path: "config/default.yml")
33
+
34
+ puts generator.todo
35
+ end
@@ -0,0 +1,69 @@
1
+ # Benchmark report
2
+ ## [parse_table.rb](parse_table.rb)
3
+ ```bash
4
+ $ ruby -v
5
+ ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) [x86_64-darwin21]
6
+
7
+ $ bundle exec ruby benchmark/parse_table.rb
8
+ Warming up --------------------------------------
9
+ SqlParser.parse_tables
10
+ 3.315k i/100ms
11
+ RuboCop::Isucon::GDA::Client#table_names
12
+ 31.000 i/100ms
13
+ Calculating -------------------------------------
14
+ SqlParser.parse_tables
15
+ 31.672k (± 7.5%) i/s - 159.120k in 5.056991s
16
+ RuboCop::Isucon::GDA::Client#table_names
17
+ 418.846 (± 9.8%) i/s - 2.077k in 5.023313s
18
+
19
+ Comparison:
20
+ SqlParser.parse_tables: 31671.6 i/s
21
+ RuboCop::Isucon::GDA::Client#table_names: 418.8 i/s - 75.62x (± 0.00) slower
22
+ ```
23
+
24
+ ## [memorize.rb](memorize.rb)
25
+ ```bash
26
+ $ ruby -v
27
+ ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) [x86_64-darwin21]
28
+
29
+ $ bundle exec ruby benchmark/memorize.rb
30
+ Warming up --------------------------------------
31
+ DefineMethodWithInstanceVariableMemorizer
32
+ 79.346k i/100ms
33
+ DefineMethodWithHashMemorizer
34
+ 158.601k i/100ms
35
+ ClassEvalMemorizer 497.389k i/100ms
36
+ Calculating -------------------------------------
37
+ DefineMethodWithInstanceVariableMemorizer
38
+ 701.502k (±13.8%) i/s - 3.491M in 5.087557s
39
+ DefineMethodWithHashMemorizer
40
+ 1.549M (± 2.3%) i/s - 7.771M in 5.018555s
41
+ ClassEvalMemorizer 4.866M (± 8.1%) i/s - 24.372M in 5.061882s
42
+
43
+ Comparison:
44
+ ClassEvalMemorizer: 4865569.7 i/s
45
+ DefineMethodWithHashMemorizer: 1549386.5 i/s - 3.14x (± 0.00) slower
46
+ DefineMethodWithInstanceVariableMemorizer: 701502.0 i/s - 6.94x (± 0.00) slower
47
+ ```
48
+
49
+ ## [shell.rb](shell.rb)
50
+ ```bash
51
+ $ ruby -v
52
+ ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) [x86_64-darwin21]
53
+
54
+ $ bundle exec ruby benchmark/shell.rb
55
+ Warming up --------------------------------------
56
+ digest_with_shell_openssl
57
+ 4.000 i/100ms
58
+ digest_with_ruby_openssl
59
+ 36.540k i/100ms
60
+ Calculating -------------------------------------
61
+ digest_with_shell_openssl
62
+ 92.458 (±22.7%) i/s - 424.000 in 4.985251s
63
+ digest_with_ruby_openssl
64
+ 453.025k (±10.1%) i/s - 2.265M in 5.057335s
65
+
66
+ Comparison:
67
+ digest_with_ruby_openssl: 453024.6 i/s
68
+ digest_with_shell_openssl: 92.5 i/s - 4899.78x (± 0.00) slower
69
+ ```
@@ -0,0 +1,86 @@
1
+ require "benchmark/ips"
2
+
3
+ class DefineMethodWithInstanceVariableMemorizer
4
+ def self.memorize(method_name)
5
+ define_method "#{method_name}_with_cache" do
6
+ if (ret = instance_variable_get("@#{method_name}_with_cache"))
7
+ ret
8
+ else
9
+ ret = send("#{method_name}_without_cache")
10
+ instance_variable_set("@#{method_name}_with_cache", ret)
11
+ ret
12
+ end
13
+ end
14
+
15
+ alias_method "#{method_name}_without_cache", method_name
16
+ alias_method method_name, "#{method_name}_with_cache"
17
+ end
18
+
19
+ def value
20
+ 1
21
+ end
22
+
23
+ memorize :value
24
+ end
25
+
26
+ class DefineMethodWithHashMemorizer
27
+ def self.memorize(method_name)
28
+ define_method "#{method_name}_with_cache" do
29
+ @__cache ||= {}
30
+ @__cache[method_name] ||= send("#{method_name}_without_cache")
31
+ end
32
+
33
+ alias_method "#{method_name}_without_cache", method_name
34
+ alias_method method_name, "#{method_name}_with_cache"
35
+ end
36
+
37
+ def value
38
+ 1
39
+ end
40
+
41
+ memorize :value
42
+ end
43
+
44
+ # @note This is the current #{RuboCop::Isucon::MemorizeMethods#memorize} implementation
45
+ class ClassEvalMemorizer
46
+ def self.memorize(method_name)
47
+ class_eval <<~RUBY, __FILE__, __LINE__ + 1
48
+ # def foo_with_cache
49
+ # @foo_with_cache ||= foo_without_cache
50
+ # end
51
+ def #{method_name}_with_cache
52
+ @#{method_name}_with_cache ||= #{method_name}_without_cache
53
+ end
54
+ RUBY
55
+ alias_method "#{method_name}_without_cache", method_name
56
+ alias_method method_name, "#{method_name}_with_cache"
57
+ end
58
+
59
+ def value
60
+ 1
61
+ end
62
+
63
+ memorize :value
64
+ end
65
+
66
+ Benchmark.ips do |x|
67
+ x.report("DefineMethodWithInstanceVariableMemorizer") do
68
+ m = DefineMethodWithInstanceVariableMemorizer.new
69
+ m.value
70
+ m.value
71
+ end
72
+
73
+ x.report("DefineMethodWithHashMemorizer") do
74
+ m = DefineMethodWithHashMemorizer.new
75
+ m.value
76
+ m.value
77
+ end
78
+
79
+ x.report("ClassEvalMemorizer") do
80
+ m = ClassEvalMemorizer.new
81
+ m.value
82
+ m.value
83
+ end
84
+
85
+ x.compare!
86
+ end
@@ -0,0 +1,103 @@
1
+ require "benchmark/ips"
2
+ require "rubocop-isucon"
3
+
4
+ # SQL Parser
5
+ #
6
+ # @deprecated Use {RuboCop::Isucon::GDA::Client}
7
+ module SqlParser
8
+ # Parse table names in SQL (SELECT, UPDATE, INSERT, DELETE)
9
+ # @param sql [String]
10
+ # @return [Array<String>]
11
+ #
12
+ # @deprecated Use {RuboCop::Isucon::GDA::Client#table_names}
13
+ def self.parse_tables(sql)
14
+ # Remove `FOR UPDATE` in `SELECT`
15
+ sql = sql.gsub(/FOR\s+UPDATE/i, "")
16
+
17
+ # Remove `ON DUPLICATE KEY UPDATE` in `INSERT`
18
+ sql = sql.gsub(/ON\s+DUPLICATE\s+KEY\s+UPDATE/i, "")
19
+
20
+ sql.scan(/(?:FROM|INTO|UPDATE|JOIN)\s+([^(]+?)[\s(]/i).
21
+ map { |matched| matched[0].strip.delete("`") }.reject(&:empty?).uniq
22
+ end
23
+ end
24
+
25
+ # c.f. https://github.com/isucon/isucon10-final/blob/e858b2588a199f9c7407baacf48b53126b8aeed6/webapp/ruby/app.rb#L250-L318
26
+ sql = <<~SQL
27
+ SELECT
28
+ `teams`.`id` AS `id`,
29
+ `teams`.`name` AS `name`,
30
+ `teams`.`leader_id` AS `leader_id`,
31
+ `teams`.`withdrawn` AS `withdrawn`,
32
+ `team_student_flags`.`student` AS `student`,
33
+ (`best_score_jobs`.`score_raw` - `best_score_jobs`.`score_deduction`) AS `best_score`,
34
+ `best_score_jobs`.`started_at` AS `best_score_started_at`,
35
+ `best_score_jobs`.`finished_at` AS `best_score_marked_at`,
36
+ (`latest_score_jobs`.`score_raw` - `latest_score_jobs`.`score_deduction`) AS `latest_score`,
37
+ `latest_score_jobs`.`started_at` AS `latest_score_started_at`,
38
+ `latest_score_jobs`.`finished_at` AS `latest_score_marked_at`,
39
+ `latest_score_job_ids`.`finish_count` AS `finish_count`
40
+ FROM
41
+ `teams`
42
+ -- latest scores
43
+ LEFT JOIN (
44
+ SELECT
45
+ MAX(`id`) AS `id`,
46
+ `team_id`,
47
+ COUNT(*) AS `finish_count`
48
+ FROM
49
+ `benchmark_jobs`
50
+ WHERE
51
+ `finished_at` IS NOT NULL
52
+ -- score freeze
53
+ AND (`team_id` = ? OR (`team_id` != ? AND (? = TRUE OR `finished_at` < ?)))
54
+ GROUP BY
55
+ `team_id`
56
+ ) `latest_score_job_ids` ON `latest_score_job_ids`.`team_id` = `teams`.`id`
57
+ LEFT JOIN `benchmark_jobs` `latest_score_jobs` ON `latest_score_job_ids`.`id` = `latest_score_jobs`.`id`
58
+ -- best scores
59
+ LEFT JOIN (
60
+ SELECT
61
+ MAX(`j`.`id`) AS `id`,
62
+ `j`.`team_id` AS `team_id`
63
+ FROM
64
+ (
65
+ SELECT
66
+ `team_id`,
67
+ MAX(`score_raw` - `score_deduction`) AS `score`
68
+ FROM
69
+ `benchmark_jobs`
70
+ WHERE
71
+ `finished_at` IS NOT NULL
72
+ -- score freeze
73
+ AND (`team_id` = ? OR (`team_id` != ? AND (? = TRUE OR `finished_at` < ?)))
74
+ GROUP BY
75
+ `team_id`
76
+ ) `best_scores`
77
+ LEFT JOIN `benchmark_jobs` `j` ON (`j`.`score_raw` - `j`.`score_deduction`) = `best_scores`.`score`
78
+ AND `j`.`team_id` = `best_scores`.`team_id`
79
+ GROUP BY
80
+ `j`.`team_id`
81
+ ) `best_score_job_ids` ON `best_score_job_ids`.`team_id` = `teams`.`id`
82
+ LEFT JOIN `benchmark_jobs` `best_score_jobs` ON `best_score_jobs`.`id` = `best_score_job_ids`.`id`
83
+ -- check student teams
84
+ LEFT JOIN (
85
+ SELECT
86
+ `team_id`,
87
+ (SUM(`student`) = COUNT(*)) AS `student`
88
+ FROM
89
+ `contestants`
90
+ GROUP BY
91
+ `contestants`.`team_id`
92
+ ) `team_student_flags` ON `team_student_flags`.`team_id` = `teams`.`id`
93
+ ORDER BY
94
+ `latest_score` DESC,
95
+ `latest_score_marked_at` ASC
96
+ SQL
97
+
98
+ Benchmark.ips do |x|
99
+ x.report("SqlParser.parse_tables") { SqlParser.parse_tables(sql) }
100
+ x.report("RuboCop::Isucon::GDA::Client#table_names") { RuboCop::Isucon::GDA::Client.new(sql).table_names }
101
+
102
+ x.compare!
103
+ end
@@ -0,0 +1,26 @@
1
+ require "benchmark/ips"
2
+ require "openssl"
3
+ require "securerandom"
4
+ require "shellwords"
5
+
6
+ def digest_with_shell_openssl(src)
7
+ `printf "%s" #{Shellwords.shellescape(src)} | openssl dgst -sha512 | sed 's/^.*= //'`.strip
8
+ end
9
+
10
+ def digest_with_ruby_openssl(src)
11
+ OpenSSL::Digest::SHA512.hexdigest(src)
12
+ end
13
+
14
+ SOURCE_TEXT = SecureRandom.alphanumeric(256)
15
+
16
+ Benchmark.ips do |x|
17
+ x.report("digest_with_shell_openssl") do
18
+ digest_with_shell_openssl(SOURCE_TEXT)
19
+ end
20
+
21
+ x.report("digest_with_ruby_openssl") do
22
+ digest_with_ruby_openssl(SOURCE_TEXT)
23
+ end
24
+
25
+ x.compare!
26
+ end