From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Original-To: poffice@blade.nagaokaut.ac.jp Delivered-To: poffice@blade.nagaokaut.ac.jp Received: from kankan.nagaokaut.ac.jp (kankan.nagaokaut.ac.jp [133.44.2.24]) by blade.nagaokaut.ac.jp (Postfix) with ESMTP id 2B85F19E0056 for ; Thu, 10 Dec 2015 01:01:33 +0900 (JST) Received: from voscc.nagaokaut.ac.jp (voscc.nagaokaut.ac.jp [133.44.1.100]) by kankan.nagaokaut.ac.jp (Postfix) with ESMTP id D5142B5D8FA for ; Thu, 10 Dec 2015 01:33:17 +0900 (JST) Received: from neon.ruby-lang.org (neon.ruby-lang.org [221.186.184.75]) by voscc.nagaokaut.ac.jp (Postfix) with ESMTP id C39D218CC7D0 for ; Thu, 10 Dec 2015 01:33:17 +0900 (JST) Received: from [221.186.184.76] (localhost [IPv6:::1]) by neon.ruby-lang.org (Postfix) with ESMTP id C4F2412058C; Thu, 10 Dec 2015 01:33:15 +0900 (JST) X-Original-To: ruby-core@ruby-lang.org Delivered-To: ruby-core@ruby-lang.org Received: from o2.heroku.sendgrid.net (o2.heroku.sendgrid.net [67.228.50.55]) by neon.ruby-lang.org (Postfix) with ESMTPS id A8F20120457 for ; Thu, 10 Dec 2015 01:33:11 +0900 (JST) DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=sendgrid.me; h=from:to:references:subject:mime-version:content-type:content-transfer-encoding:list-id; s=smtpapi; bh=0YU6KRkzzroYyP5rZQxMdqVi53Y=; b=Rj6KSh6fyvTtoZVFxB sRc/wiyn2+6cztaZsBjDFj5Q/MiORWXDGeDtjlHGjdPVbCXuhq+SuECzoFjtJUrF 3a1qPEigK7q/iPDngq6DpFPGvBMpWiHgBm+kEXNPxoOx3hJI4i1Vr56ooH86lGuo EoxgaENTswCDDNe5Vci9uqQl8= Received: by filter0562p1mdw1.sendgrid.net with SMTP id filter0562p1mdw1.20652.566857B350 2015-12-09 16:32:51.482500207 +0000 UTC Received: from herokuapp.com (ec2-54-196-144-109.compute-1.amazonaws.com [54.196.144.109]) by ismtpd0003p1iad1.sendgrid.net (SG) with ESMTP id rDUM5u1MRMy4O40X6qIQyA for ; Wed, 09 Dec 2015 16:32:51.358 +0000 (UTC) Date: Wed, 09 Dec 2015 16:32:51 +0000 From: ko1@atdot.net To: ruby-core@ruby-lang.org Message-ID: References: Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 7bit X-Redmine-MailingListIntegration-Message-Ids: 46707 X-Redmine-Project: ruby-trunk X-Redmine-Issue-Id: 11725 X-Redmine-Issue-Author: ko1 X-Redmine-Issue-Assignee: ko1 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-SG-EID: ync6xU2WACa70kv/Ymy4QrNMhiuLXJG8OTL2vJD1yS6m8O4U8OF8+qzsGtZ8OY+T8obQv5DehzH/Pg 4hlnBh3sS4wTjCC1RtO6EGOmXyylaJKqxvmTGbyjwyhtdgdu8qf2KU0/kueQw/f6+mAy//kPJ0sgck 6EdHzVITI0q7tngnd5068EzyCvajZp15XO/a X-ML-Name: ruby-core X-Mail-Count: 72003 Subject: [ruby-core:72003] [Ruby trunk - Feature #11725] [Assigned] debugging support for frozen string literal 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: , Errors-To: ruby-core-bounces@ruby-lang.org Sender: "ruby-core" Issue #11725 has been updated by Koichi Sasada. Status changed from Closed to Assigned We found performance regression with this feature. "foo".dup with frozen literal also copy location information. Quoted from https://bugs.ruby-lang.org/issues/11782#change-55313 ``` # frozen-string-literal: true user system total real 5.850000 0.010000 5.860000 ( 5.858450) 0.780000 0.000000 0.780000 ( 0.780911) 0.340000 0.000000 0.340000 ( 0.351072) 0.530000 0.000000 0.530000 ( 0.529234) ``` Too slow on "'foo'.dup" (a result on first line). Nobu found the reason why it is slow. And reason is implicit debug information. String#dup copys with such debug information. So I decide to withdraw the proposal of debugging support by default. Same as dynamically created string, debug information is added for static string literal iff --debug or --debug=frozen-string-literal is specified. ``` $ ./miniruby -I../../trunk/lib -I. -I.ext/common ../../trunk/test.rb Rehearsal ------------------------------------------- .dup 2.280000 0.000000 2.280000 ( 2.280500) +@ 0.770000 0.000000 0.770000 ( 0.767608) .freeze 0.340000 0.000000 0.340000 ( 0.343781) -@ 0.480000 0.000000 0.480000 ( 0.479379) ---------------------------------- total: 3.870000sec user system total real .dup 2.260000 0.000000 2.260000 ( 2.260352) +@ 0.770000 0.000000 0.770000 ( 0.771664) .freeze 0.350000 0.000000 0.350000 ( 0.345739) -@ 0.470000 0.000000 0.470000 ( 0.471908) $ ./miniruby --debug -I../../trunk/lib -I. -I.ext/common ../../trunk/test.rb Rehearsal ------------------------------------------- .dup 5.470000 0.000000 5.470000 ( 5.483195) +@ 0.790000 0.000000 0.790000 ( 0.789157) .freeze 0.340000 0.000000 0.340000 ( 0.342538) -@ 0.470000 0.000000 0.470000 ( 0.473341) ---------------------------------- total: 7.070000sec user system total real .dup 5.510000 0.010000 5.520000 ( 5.519788) +@ 0.770000 0.000000 0.770000 ( 0.767551) .freeze 0.340000 0.000000 0.340000 ( 0.342797) -@ 0.470000 0.000000 0.470000 ( 0.473512) ``` Benchmark program: ```ruby # frozen-string-literal: true require 'benchmark' N = 10_000_000 Benchmark.bmbm{|x| x.report('.dup'){ N.times{ 'foo'.dup } } x.report('+@'){ N.times{ +'foo' } } x.report('.freeze'){ N.times{ 'foo'.freeze } } x.report('-@'){ N.times{ -'foo' } } } ``` ---------------------------------------- Feature #11725: debugging support for frozen string literal https://bugs.ruby-lang.org/issues/11725#change-55412 * Author: Koichi Sasada * Status: Assigned * Priority: Normal * Assignee: Koichi Sasada ---------------------------------------- # Background Debug frozen string literal is difficult because frozen string can be modified at far from created locations. For example, library X creates one string and modify this string is common situation. Also library X can pass the created string to library Y and also Y passes it to library Z. Clients receives a string from Z, then clients can not understand where the created location and can not debug it. To help such situation, I introduced --enable-frozen-string-literal-debug option (a command line option and a compile option). This option enable to embed created location information into frozen string and to display RuntimeError message with this location information such as: ``` ../../trunk/test.rb:21:in `
': can't modify frozen String, created at ../../trunk/test.rb:17 (RuntimeError) ``` (maybe only few people knows about this feature because NEWS doesn't describe it) # Proposal: Make it as default behavior At first, I introduced this option as an optional feature because this option seems slow down applications. However, including this information for sting literals is not so big impact because adding information for each string literal only at compile time. Another drawback is that we can't de-duplicate same frozen strings, but it is trivial issue. One problem is string interpolation. Adding debug information for such dynamically created strings is not so easy. So I leave --enable-frozen-string-literal-debug for this type of strings. It is too long to use it, so -d or --debug option should enable this feature. Conclusion: * adding string literals as default. * --enable-frozen-string-literal-debug for string interpolation. -d or --debug also enable this feature. I hope we can change every string literals to frozen strings. Any comments are welcome. # Naming Now, I prefer --debug-frozen-string-literal than --enable-frozen-string-literal-debug. # Misc: benchmark script and result ```ruby require 'benchmark' N = 5_000_000 def static_test1 "foo" end def static_test2 "foo".freeze end line = __LINE__ + 1 RubyVM::InstructionSequence.compile(%q{ def static_test3 "foo" end }, __FILE__, __FILE__, line, {frozen_string_literal: true}).eval line = __LINE__ + 1 RubyVM::InstructionSequence.compile(%q{ def static_test4 "foo" end }, __FILE__, __FILE__, line, {frozen_string_literal: true, frozen_string_literal_debug: true}).eval S = 'baz' def dynamic_test1 "foo#{S}bar" end def dynamic_test2 "foo#{S}bar".freeze end line = __LINE__ + 1 RubyVM::InstructionSequence.compile(%q{ def dynamic_test3 "foo#{S}bar" end }, __FILE__, __FILE__, line, {frozen_string_literal: true}).eval line = __LINE__ + 1 RubyVM::InstructionSequence.compile(%q{ def dynamic_test4 "foo#{S}bar" end }, __FILE__, __FILE__, line, {frozen_string_literal: true, frozen_string_literal_debug: true}).eval Benchmark.bmbm{|x| x.report('STATIC: without freeze' ){N.times{static_test1}} x.report('STATIC: freeze by method'){N.times{static_test2}} x.report('STATIC: freeze by pragma'){N.times{static_test3}} x.report('STATIC: freeze by pragma/debug'){N.times{static_test3}} x.report('DYNAMIC:without freeze' ){N.times{dynamic_test1}} x.report('DYNAMIC:freeze by method'){N.times{dynamic_test2}} x.report('DYNAMIC:freeze by pragma'){N.times{dynamic_test3}} x.report('DYNAMIC:freeze by pragma/debug'){N.times{dynamic_test4}} } # error message check ['static', 'dynamic'].each{|feature| (1..4).each{|type| begin m = "#{feature}_test#{type}" send(m) << "str" rescue RuntimeError => e p [m, e] else p [m, nil] end } } ``` ``` user system total real STATIC: without freeze 0.550000 0.000000 0.550000 ( 0.545465) STATIC: freeze by method 0.330000 0.000000 0.330000 ( 0.337528) STATIC: freeze by pragma 0.360000 0.000000 0.360000 ( 0.366031) STATIC: freeze by pragma/debug 0.360000 0.000000 0.360000 ( 0.353534) DYNAMIC:without freeze 1.230000 0.000000 1.230000 ( 1.230027) DYNAMIC:freeze by method 1.580000 0.000000 1.580000 ( 1.585025) DYNAMIC:freeze by pragma 1.420000 0.000000 1.420000 ( 1.424006) DYNAMIC:freeze by pragma/debug 3.510000 0.000000 3.510000 ( 3.510279) # error message check results ["static_test1", nil] ["static_test2", #] ["static_test3", #] ["static_test4", #] ["dynamic_test1", nil] ["dynamic_test2", #] ["dynamic_test3", #] ["dynamic_test4", #] ``` -- https://bugs.ruby-lang.org/