From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on dcvr.yhbt.net X-Spam-Level: X-Spam-ASN: AS4713 221.184.0.0/13 X-Spam-Status: No, score=-3.0 required=3.0 tests=AWL,BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED, SPF_HELO_NONE,SPF_PASS,UNPARSEABLE_RELAY,URIBL_SBL,URIBL_SBL_A shortcircuit=no autolearn=ham autolearn_force=no version=3.4.2 Received: from neon.ruby-lang.org (neon.ruby-lang.org [221.186.184.75]) by dcvr.yhbt.net (Postfix) with ESMTP id 8EC9B1F5AE for ; Wed, 9 Jun 2021 00:31:07 +0000 (UTC) Received: from neon.ruby-lang.org (localhost [IPv6:::1]) by neon.ruby-lang.org (Postfix) with ESMTP id 29FE4120B46; Wed, 9 Jun 2021 09:29:57 +0900 (JST) Received: from o1678948x4.outbound-mail.sendgrid.net (o1678948x4.outbound-mail.sendgrid.net [167.89.48.4]) by neon.ruby-lang.org (Postfix) with ESMTPS id AADED120B0A for ; Wed, 9 Jun 2021 09:29:54 +0900 (JST) Received: by filterdrecv-79b856d5bc-gbz25 with SMTP id filterdrecv-79b856d5bc-gbz25-1-60C00BC5-CA 2021-06-09 00:31:01.67837491 +0000 UTC m=+445268.678660428 Received: from herokuapp.com (unknown) by ismtpd0145p1mdw1.sendgrid.net (SG) with ESMTP id 20q0hjIxRQmIY9Y1xExLoQ for ; Wed, 09 Jun 2021 00:31:01.489 +0000 (UTC) Date: Wed, 09 Jun 2021 00:31:01 +0000 (UTC) From: ko1@atdot.net Message-ID: References: Mime-Version: 1.0 X-Redmine-Project: ruby-master X-Redmine-Issue-Tracker: Feature X-Redmine-Issue-Id: 17763 X-Redmine-Issue-Author: eileencodes X-Redmine-Sender: ko1 X-Mailer: Redmine X-Redmine-Host: bugs.ruby-lang.org X-Redmine-Site: Ruby Issue Tracking System X-Auto-Response-Suppress: All Auto-Submitted: auto-generated X-Redmine-MailingListIntegration-Message-Ids: 80292 X-SG-EID: =?us-ascii?Q?fVTMYOBjtdvXNcWwrscBhLsHItUXVK5L4mtnq0mdcRchNitOddu3JfcxjyTZyq?= =?us-ascii?Q?4xZnIsA90ehV0fJpq=2FYTu7ftvybOzEtXSU=2FHQoU?= =?us-ascii?Q?JxsZsVUy1JrQrYw1H5x+KNdVt1UVgPHgB9CBieN?= =?us-ascii?Q?fYX+gqrkkxNZR3TJEKZeSM5EHPnwGuwH0ZPXxRT?= =?us-ascii?Q?0Z7wNPnpV0qAcroknUtiJCzxlewhPiBuAnw=3D=3D?= To: ruby-core@ruby-lang.org X-Entity-ID: b/2+PoftWZ6GuOu3b0IycA== X-ML-Name: ruby-core X-Mail-Count: 104210 Subject: [ruby-core:104210] [Ruby master Feature#17763] Implement cache for cvars X-BeenThere: ruby-core@ruby-lang.org X-Mailman-Version: 2.1.15 Precedence: list Reply-To: Ruby developers List-Id: Ruby developers List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: quoted-printable Errors-To: ruby-core-bounces@ruby-lang.org Sender: "ruby-core" Issue #17763 has been updated by ko1 (Koichi Sasada). Thank you for the benchmark. How about that? >> We also measured database queries in Rails and with the cvar cache they = are about ~9% faster. > It seems w/o cache is faster. Could you check it? (opposite results?) ---------------------------------------- Feature #17763: Implement cache for cvars https://bugs.ruby-lang.org/issues/17763#change-92389 * Author: eileencodes (Eileen Uchitelle) * Status: Open * Priority: Normal ---------------------------------------- # Introduce inline cache for class variable reads @tenderlove and I would like to introduce an inline cache for class variabl= e reads. We've attached a patch that introduces the cache. Class variable r= eads are popular in Rails applications for example, Active Record's `#logge= r`. GitHub PR: https://github.com/ruby/ruby/pull/4340 ## Cache Design This patch introduces a hash table that's stored on the same class as the c= lass variable value. For example: ```ruby class A @@foo =3D 1 end class B < A def self.read_foo @@foo end end ``` The above code stores the value for `@@foo` on the `A` class and stores an = inline cache value on the `A` class as well. The instruction sequences for = the `read_foo` method point at the CVAR inline cache entry stored on class = `A`. The lifecycle of these caches are similar to instance variable inline cache= s. ### Diagram of the cache: ![cvar cache](https://gist.githubusercontent.com/eileencodes/ddd95be978df27= eb76543d352d516449/raw/13e969320159a4e1bff9444694a1ac198e892237/cvar%2520ca= che@2x%2520(6).png) ## Performance Characteristics When class variables are read, Ruby needs to check each class in the inheri= tance tree to ensure that the class variable isn't set on any other classes= in the tree. If the same cvar is set on a class in the inheritance tree th= en a "cvar overtaken" error will be raised. Because of how cvar reads work, the more classes in the inheritance tree th= e more expensive a cvar read is. To demonstrate this here is a benchmark th= at reads a cvar from a class with 1 module, 30 modules, and 100 modules in = the inheritance chain. On Ruby master 100 modules is 8.5x slower than inclu= ding 1 module. With the cache, there is no performance difference between i= ncluding 1 module and including 100 modules. Benchmark script: ```ruby require "benchmark/ips" MODULES =3D ["B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N= ", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "AA", "BB", = "CC", "DD", "EE", "FF", "GG", "HH", "II", "JJ", "KK", "LL", "MM", "NN", "OO= ", "PP", "QQ", "RR", "SS", "TT", "UU", "VV", "WW", "XX", "YY", "ZZ", "AAA",= "BBB", "CCC", "DDD", "EEE", "FFF", "GGG", "HHH", "III", "JJJ", "KKK", "LLL= ", "MMM", "NNN", "OOO", "PPP", "QQQ", "RRR", "SSS", "TTT", "UUU", "VVV", "W= WW", "XXX", "YYY", "ZZZ", "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "= GGGG", "HHHH", "IIII", "JJJJ", "KKKK", "LLLL", "MMMM", "NNNN", "OOOO", "PPP= P", "QQQQ", "RRRR", "SSSS", "TTTT", "UUUU", "VVVV", "WWWW"] class A @@foo =3D 1 def self.foo @@foo end eval <<-EOM module #{MODULES.first} end include #{MODULES.first} EOM end class Athirty @@foo =3D 1 def self.foo @@foo end MODULES.take(30).each do |module_name| eval <<-EOM module #{module_name} end include #{module_name} EOM end end class Ahundred @@foo =3D 1 def self.foo @@foo end MODULES.each do |module_name| eval <<-EOM module #{module_name} end include #{module_name} EOM end end Benchmark.ips do |x| x.report "1 module" do A.foo end x.report "30 modules" do Athirty.foo end x.report "100 modules" do Ahundred.foo end x.compare! end ``` Ruby 3.0 master: ``` Warming up -------------------------------------- 1 module 1.231M i/100ms 30 modules 432.020k i/100ms 100 modules 145.399k i/100ms Calculating ------------------------------------- 1 module 12.210M (=B1 2.1%) i/s - 61.553M in 5.043400s 30 modules 4.354M (=B1 2.7%) i/s - 22.033M in 5.063839s 100 modules 1.434M (=B1 2.9%) i/s - 7.270M in 5.072531s Comparison: 1 module: 12209958.3 i/s 30 modules: 4354217.8 i/s - 2.80x (=B1 0.00) slower 100 modules: 1434447.3 i/s - 8.51x (=B1 0.00) slower ``` Ruby 3.0 with cvar cache: ``` Warming up -------------------------------------- 1 module 1.641M i/100ms 30 modules 1.655M i/100ms 100 modules 1.620M i/100ms Calculating ------------------------------------- 1 module 16.279M (=B1 3.8%) i/s - 82.038M in 5.046923s 30 modules 15.891M (=B1 3.9%) i/s - 79.459M in 5.007958s 100 modules 16.087M (=B1 3.6%) i/s - 81.005M in 5.041931s Comparison: 1 module: 16279458.0 i/s 100 modules: 16087484.6 i/s - same-ish: difference falls within er= ror 30 modules: 15891406.2 i/s - same-ish: difference falls within er= ror ``` ### Rails Application Benchmarks We also benchmarked `ActiveRecord::Base.logger` since `logger` is a cvar an= d there are 63 modules in the inheritance chain. This is an example of a re= al-world improvement to Rails applications. Benchmark: ```ruby require "benchmark/ips" require_relative "config/environment" Benchmark.ips do |x| x.report "logger" do ActiveRecord::Base.logger end end ``` Ruby 3.0 master: ``` Warming up -------------------------------------- logger 155.251k i/100ms Calculating ------------------------------------- ``` Ruby 3.0 with cvar cache: ``` Warming up -------------------------------------- logger 1.546M i/100ms Calculating ------------------------------------- logger 14.857M (=B1 4.8%) i/s - 74.198M in 5.006202s ``` We also measured database queries in Rails and with the cvar cache they are= about ~9% faster. Benchmark code: ```ruby class BugTest < Minitest::Test = = = def test_association_stuff = = = post =3D Post.create! = = = = = = Benchmark.ips do |x| = = = x.report "query" do = = = Post.first = = = end = = = end = = = end = = = end = = = ``` Ruby 3.0 master / Rails 6.1: ``` Warming up -------------------------------------- query 790.000 i/100ms Calculating ------------------------------------- query 7.601k (=B1 3.8%) i/s - 38.710k in 5.100534s ``` Ruby 3.0 cvar cache / Rails 6.1: ``` Warming up -------------------------------------- query 731.000 i/100ms Calculating ------------------------------------- query 7.089k (=B1 3.3%) i/s - 35.819k in 5.058215s ``` -- = https://bugs.ruby-lang.org/ Unsubscribe: