rubocop-isucon 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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