aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport/lib/active_support/core_ext/object
Commit message (Collapse)AuthorAgeFilesLines
...
* Speed up String#blank? Regexschneems2016-04-201-6/+3
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Follow up on https://github.com/rails/rails/commit/697384df36a939e565b7c08725017d49dc83fe40#commitcomment-17184696. The regex to detect a blank string `/\A[[:space:]]*\z/` will loop through every character in the string to ensure that all of them are a `:space:` type. We can invert this logic and instead look for any non-`:space:` characters. When that happens, we would return on the first character found and the regex engine does not need to keep looking. Thanks @nellshamrell for the regex talk at LSRC. By defining a "blank" string as any string that does not have a non-whitespace character (yes, double negative) we can get a substantial speed bump. Also an inline regex is (barely) faster than a regex in a constant, since it skips the constant lookup. A regex literal is frozen by default. ```ruby require 'benchmark/ips' def string_generate str = " abcdefghijklmnopqrstuvwxyz\t".freeze str[rand(0..(str.length - 1))] * rand(0..23) end strings = 100.times.map { string_generate } ALL_WHITESPACE_STAR = /\A[[:space:]]*\z/ Benchmark.ips do |x| x.report('current regex ') { strings.each {|str| str.empty? || ALL_WHITESPACE_STAR === str } } x.report('+ instead of * ') { strings.each {|str| str.empty? || /\A[[:space:]]+\z/ === str } } x.report('not a non-whitespace char') { strings.each {|str| str.empty? || !(/[[:^space:]]/ === str) } } x.compare! end # Warming up -------------------------------------- # current regex # 1.744k i/100ms # not a non-whitespace char # 2.264k i/100ms # Calculating ------------------------------------- # current regex # 18.078k (± 8.9%) i/s - 90.688k # not a non-whitespace char # 23.580k (± 7.1%) i/s - 117.728k # Comparison: # not a non-whitespace char: 23580.3 i/s # current regex : 18078.2 i/s - 1.30x slower ``` This makes the method roughly 30% faster `(23.580 - 18.078)/18.078 * 100`. cc/ @fxn
* ~3.5x speedup of String#blank? for empty stringsXavier Noria2016-04-201-1/+6
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | See the rationale in the comment in this patch. To benchmark this I ran a number of variations, ultimately narrowing to require 'benchmark/ips' str = '' regexp = /\A[[:space:]]*\z/ Benchmark.ips do |x| x.report('regexp') { regexp === str } x.report('empty') { str.empty? || regexp === str } x.compare! end This benchmark has consistently reported speedups around 3.5x: Calculating ------------------------------------- regexp 69.197k i/100ms empty 115.468k i/100ms ------------------------------------------------- regexp 2. 6.3%) i/s - 13.839M empty 9. 8.8%) i/s - 47.804M Comparison: empty: 9642607.6 i/s regexp: 2768351.9 i/s - 3.48x slower Sometimes even reaching 4x. Running the same bechmark on strings of 10 or 100 characters (with whitespace or present) has shown a slowdown of just about 1.01/1.02. Marginal, we seem to have a worthwhile trade-off here.
* Fix behavior of JSON encoding for Exceptionnamusyaka2016-04-091-0/+6
|
* drop array allocations when iterating over the hashAaron Patterson2015-10-151-1/+3
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | `each_with_object` allocates an array for each kv pair. Switching to the slightly more verbose but less allocatey `each_pair` eliminates array allocations. Eliminating this allocation returns AR objects to have constant array allocations regardless of the number of columns the object has. Here is test code: ```ruby require 'active_record' class Topic < ActiveRecord::Base end 20.times do |i| Process.waitpid fork { ActiveRecord::Base.establish_connection adapter: 'sqlite3', database: ':memory:' ActiveRecord::Base.connection.instance_eval do create_table(:topics) do |t| t.string :title, limit: 250 t.string :author_name t.string :author_email_address t.string :parent_title t.string :type t.string :group i.times do |j| t.string :"aaa#{j}" end t.timestamps null: true end end ObjectSpace::AllocationTracer.setup(%i{type}) Topic.create title: "aaron" # heat cache result = ObjectSpace::AllocationTracer.trace do 10.times do |i| Topic.create title: "aaron #{i}" end end puts "#{Topic.columns.length},#{(result.find { |k,v| k.first == :T_ARRAY }.last.first / 10)}" } end ``` Before this commit: ``` 9,166 10,167 11,168 12,169 13,170 14,171 15,172 16,173 17,174 18,175 19,176 20,177 21,178 22,179 23,180 24,181 25,182 26,183 27,184 28,185 ``` After: ``` 9,157 10,157 11,157 12,157 13,157 14,157 15,157 16,157 17,157 18,157 19,157 20,157 21,157 22,157 23,157 24,157 25,157 26,157 27,157 28,157 ``` Left side is the number of columns, right is the number of allocations
* make string allocation constant regardless of column countAaron Patterson2015-10-151-2/+6
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | deep_dup'ing a hash will dup the keys as well as the values. Since string keys from the source hash will be frozen, and the dup'd objects are immediately dup'd and frozen on insert in to the hash, the end user will only ever see two frozen strings. Since the strings are immutable, this commit just cheats a little and reuses the immutable strings. Just to reiterate, before this commit, deep duping a hash that looks like this: `{ "foo" => "bar" }` will generate two new instances of "foo". One is created when `deep_dup` is called on "foo", and the other is created when the newly allocated "foo" string is inserted in to the hash. The user never sees the intermediate "foo", and both copies of "foo" that the user *can* access will be frozen, so in this case we just reuse the existing frozen key. The upshot is that after this change, string allocations on AR allocations become constant regardless of the number of columns the model has. ```ruby require 'active_record' class Topic < ActiveRecord::Base end 20.times do |i| Process.waitpid fork { ActiveRecord::Base.establish_connection adapter: 'sqlite3', database: ':memory:' ActiveRecord::Base.connection.instance_eval do create_table(:topics) do |t| t.string :title, limit: 250 t.string :author_name t.string :author_email_address t.string :parent_title t.string :type t.string :group i.times do |j| t.integer :"aaa#{j}" end t.timestamps null: true end end ObjectSpace::AllocationTracer.setup(%i{type}) Topic.create title: "aaron" # heat cache result = ObjectSpace::AllocationTracer.trace do 10.times do |i| Topic.create title: "aaron #{i}" end end puts "#{Topic.columns.length},#{(result.find { |k,v| k.first == :T_STRING }.last.first / 10)}" } end ``` If you run the above script before this commit, the output looks like this: ``` [aaron@TC rails (master)]$ be ruby -rallocation_tracer test.rb 9,105 10,107 11,109 12,111 13,113 14,115 15,117 16,119 17,121 18,123 19,125 20,127 21,129 22,131 23,133 24,135 25,137 26,139 27,141 28,143 ``` The left column is the number of methods, the right column is the number of string allocations. Running against this commit, the output is: ``` [aaron@TC rails (master)]$ be ruby -rallocation_tracer test.rb 9,87 10,87 11,87 12,87 13,87 14,87 15,87 16,87 17,87 18,87 19,87 20,87 21,87 22,87 23,87 24,87 25,87 26,87 27,87 28,87 ``` As you can see, there is now only a constant number of strings allocated, regardless of the number of columns the model has.
* Improve readability of docs by using code tag [ci skip]amitkumarsuroliya2015-09-242-3/+3
|
* Short-circuit `blank?` on date and time valuesAndrew White2015-09-211-0/+11
| | | | | | | The concept of a blank date or time doesn't make sense so we can short circuit the calls for `blank?` on these classes to gain small speed boost. Fixes #21657
* File encoding is defaulted to utf-8 in Ruby >= 2.1Akira Matsuda2015-09-181-2/+0
|
* docs, make `blank?` behavior clear. Closes #21468. [ci skip]Yves Senn2015-09-021-2/+2
|
* Use == 0 instead of .zero? in #tryJean Boussier2015-08-171-1/+1
| | | | | | | | | | | | | | | | | | | The perf gain is relatively minor but consistent: ``` Calculating ------------------------------------- 0.zero? 137.091k i/100ms 1.zero? 137.350k i/100ms 0 == 0 142.207k i/100ms 1 == 0 144.724k i/100ms ------------------------------------------------- 0.zero? 8.893M (± 6.5%) i/s - 44.280M 1.zero? 8.751M (± 6.4%) i/s - 43.677M 0 == 0 10.033M (± 7.0%) i/s - 49.915M 1 == 0 9.814M (± 8.0%) i/s - 48.772M ``` And try! is quite a big hotspot for us so every little gain is appreciable.
* Tiny documentation edits [ci skip]Robin Dupret2015-07-281-1/+1
|
* improve duplicable documentation [ci skip]Julio Lopez2015-07-101-1/+5
|
* Small stylistic tweaks for `Delegator#try` patchGodfrey Chan2015-05-191-5/+5
| | | | | | * Rename `ActiveSupport::Try` => `ActiveSupport::Tryable` * Include the modules inline * `private` indentation
* Patch `Delegator` to work with `#try`Nate Smith2015-05-191-17/+57
| | | | | | | | | | | | `Delegator` inherits from `BasicObject`, which means that it will not have `Object#try` defined. It will then delegate the call to the underlying object, which will not (necessarily) respond to the method defined in the enclosing `Delegator`. This patches `Delegator` with the `#try` method to work around the surprising behaviour. Fixes #5790
* deep_dup method, remove old key from duplicated hash to avoid unnecessary pairsMehmet Emin İNAÇ2015-05-041-0/+1
|
* [ci skip] Remove unnecessary `>`yui-knk2015-03-311-1/+1
|
* Mark some constants as nodoc and remove unneeded namespaceRafael Mendonça França2015-03-201-11/+9
|
* Use Module#prepend instead of alias_method_chainKir Shatrov2015-03-201-12/+16
| | | | | | | Thanks @fbernier for suggestion! <3 At this moment we can use Module#prepend in all all cases except of Range because of the bug [1] in MRI 2.2 [1] https://bugs.ruby-lang.org/issues/10847
* Remove some comments about Ruby 1.9 behaviorsRafael Mendonça França2015-01-041-1/+1
|
* Remove hack to support BigDecimal in Ruby 1.9claudiob2015-01-041-11/+2
| | | | | Now that Rails requires Ruby >= 2.0, there is no need to check whether `BigDecimal` exists or not.
* Fix a few typos [ci skip]Robin Dupret2015-01-031-1/+1
|
* Merge branch 'master' of github.com:rails/docrailsVijay Dev2015-01-031-3/+9
|\
| * Add docs for `Object.nil!`claudiob2014-12-221-3/+9
| | | | | | | | | | | | Also add doc examples for `Object.nil`. [ci skip]
* | Removed Object#itself as it's implemented in ruby 2.2Cristian Bica2014-12-291-15/+0
|/
* Replace `#=>` with `# =>` [ci skip]claudiob2014-12-171-2/+2
| | | | | | | | @rafaelfranca suggested in f7c7bcd9 that code examples should display the result after `# =>` and not after `#=>`. This commit replaces *all* the occurrences of `#=>` in the code documentation (mostly added by me :sob:) with the suggested `# =>`.
* Pass symbol as an argument instead of a blockErik Michaels-Ober2014-11-293-3/+3
|
* Revert a change made to the example in 1ac4525Godfrey Chan2014-10-241-1/+1
| | | | | | | @carlosantoniodasilva pointed out that when `@person` is nil then this would blow up when you ended up calling `#first`on `nil`. > "there’s no way to break a try chain when you enter it :D" [ci skip]
* Include return value in examples added in #17378 [ci skip]Zachary Scott2014-10-241-2/+2
|
* Improved try documentation [ci skip]Eugene Gilburg2014-10-241-8/+25
| | | | | | | | | | - better `if` example - Added chaining example to the try method description - Documented the `respond_to?` check to the try method description - Clearer wording to explain that argument error is raised on argument mismatch to responding method, rather than to non-responding method (which is handled without exception by `try`) - `.any?` is more precise than `! .blank?` - Don't need to use `try` on `children` as (for regular associations) they will always be a collection or array that responds to `first` - Fix typos/grammar
* DRY up try/try!Ari Pollak2014-10-231-10/+6
|
* Bring try! into parity with try.Ari Pollak2014-10-221-1/+1
| | | | Based on commit 5e51bdda.
* [ci skip] Add Doc of with_options for the case when inherited default ↵Pramod Sharma2014-10-181-1/+15
| | | | options and original options have same keys
* Methods are not duplicable.Peter Jaros2014-09-031-0/+10
|
* We tenderized the wrong method! Object#try already had the yield option, ↵David Heinemeier Hansson2014-08-292-15/+12
| | | | just needed some tenderloving instance_eval to fit the bill
* Use instance_eval on @tenderlove's suggestion :trollface:David Heinemeier Hansson2014-08-291-4/+5
|
* Update examples to show real worthDavid Heinemeier Hansson2014-08-291-1/+1
|
* Added yield to Object#presenceDavid Heinemeier Hansson2014-08-291-1/+12
|
* Add documentation intro to example for `Object#itself`.Zachary Scott2014-08-191-2/+5
| | | | | Also moved comment for removal above `unless()` to not confuse RDoc with the documentation for this method.
* Fix AS docs / changelog with wrong method [ci skip]Carlos Antonio da Silva2014-08-051-1/+1
|
* Update itself.rb [ci skip]Jon Atack2014-08-051-1/+1
|
* Only define Objetc#itself when it is not definedRafael Mendonça França2014-08-041-8/+10
| | | | | | | Ruby 2.2 will include Kernel#itself so we don't need to define again. See https://github.com/ruby/ruby/commit/0a0160d6b659f6131a525fe1579e7c463d4c197e
* Rename Object#self to Object#itself to have parity with matz sanctioned ↵David Heinemeier Hansson2014-08-011-2/+2
| | | | method name for Ruby 2.2
* Added Object#self which returns the object itselfDavid Heinemeier Hansson2014-08-011-0/+10
|
* Add implicit receiver support to `Object#with_options`Pavel Pravosud2014-07-291-2/+15
|
* Fixed a compatibility issue with the `Oj` gemGodfrey Chan2014-07-291-3/+3
| | | | | | | | | | | | | `Time#as_json`, `Date#as_json` and `DateTime#as_json` incorrectly depends on a delegation that is set up in `active_support/json/encoding`. We cannot simply require that file in `core_ext/object/json` because it would cause a circular dependency problem (see #12203 for background). We should instead rely on AS's autoload to load that file for us on-demand. To trigger autoload correctly, we need to reference the `AS::JSON::Encoding` constant instead of using the delegated version. Fixes #16131.
* Merge pull request #15629 from akshay-vishnoi/test-to_paramRafael Mendonça França2014-07-282-66/+59
|\ | | | | Define Hash#to_query and set Hash#to_param as alias to it; with test cases
| * Move to_param to to_query, also Improve testsAkshay Vishnoi2014-06-202-66/+59
| |
* | [ci skip] BigDecimal#duplicable? returns false in 1.9.xAkshay Vishnoi2014-06-131-1/+1
| |
* | Revert "remove unnecssary require of `to_param`, as `to_query` is already ↵Rafael Mendonça França2014-06-111-0/+1
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | require `to_param`" Revert "No need to require to_param, it is already required in to_query.rb" This reverts commits ccdd97662e1fb00c23c90d59f65c091904959561 and d697ee14264a90a39cdbe87857656d8b314ac9b7. Reason by @jeremy: These requires are not for implementation dependency. They ensure that requiring array conversions provides to_query, to_param, and other array conversion behaviors. The fact that to_query is implemented in terms of to_param is just a coincidence. If to_query removed its to_param require, then someone requiring array conversions would no longer have to_param available. This change removes these intentional dependencies on to_param in favor of implementation side effects—an undesirable move that's susceptible to regression.
* | No need to require to_param, it is already required in to_query.rbAkshay Vishnoi2014-06-111-1/+0
|/