Repository: icyleaf/fast-crystal Branch: master Commit: da672edb3b67 Files: 38 Total size: 27.6 KB Directory structure: gitextract_y1nbnp85/ ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── code/ │ ├── array/ │ │ ├── first-vs-index[0].cr │ │ ├── insert-vs-unshift.cr │ │ ├── last-vs-index[-1].cr │ │ └── range-vs-times.map.cr │ ├── enumerable/ │ │ ├── each-push-vs-map.cr │ │ ├── each-vs-loop.cr │ │ ├── each_with_index-vs-while-loop.cr │ │ ├── map-flatten-vs-flat_map.cr │ │ ├── reverse.each-vs-reverse_each.cr │ │ └── sort-vs-sort_by.cr │ ├── general/ │ │ ├── assignment.cr │ │ ├── hash-vs-struct-vs-namedtuple.cr │ │ ├── loop-vs-while_true.cr │ │ ├── positional_argument-vs-named_argument.cr │ │ └── property-vs-getter_and_setter.cr │ ├── hash/ │ │ ├── []?-vs-has_key?.cr │ │ ├── bracket-vs-fetch.cr │ │ ├── clone-vs-dup.cr │ │ ├── keys-each-vs-each_key.cr │ │ └── merge-bang-vs-[]=.cr │ ├── namedtuple/ │ │ ├── bracket-vs-fetch.cr │ │ └── fetch-vs-fetch_with_block.cr │ ├── proc-and-block/ │ │ ├── block-vs-to_proc.cr │ │ └── proc-call-vs-yield.cr │ └── string/ │ ├── concatenation.cr │ ├── ends-string-matching-match-vs-end_with.cr │ ├── equal-substring-of-char.cr │ ├── equal-vs-match.cr │ ├── gsub-vs-sub.cr │ ├── includes-vs-to_s.includes.cr │ ├── nil-vs-to_s.empty.cr │ └── sub-vs-chomp.cr ├── shard.yml └── src/ └── fast-crystal.cr ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ /doc/ /lib/ /bin/ /.shards/ ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2017-2019 icyleaf Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: Makefile ================================================ CRYSTAL_BIN ?= $(shell which crystal) PREFIX ?= /usr/local run: build ./bin/fast-crystal clean: rm -rf bin mkdir bin build: clean $(CRYSTAL_BIN) build --release --no-debug -o bin/fast-crystal src/fast-crystal.cr $(CRFLAGS) ================================================ FILE: README.md ================================================ # 💎 Fast Crystal It's Crystal version based on [ruby version](https://github.com/JuanitoFatas/fast-ruby). Each idiom has a corresponding code example that resides in [code](code). All results listed in README.md are running with Crystal 0.25.0 (2018-06-15) LLVM 5.0.1 on OS X 10.13.5. Machine information: MacBook Pro (Retina, 15-inch, Mid 2015), 2.2 GHz Intel Core i7, 16 GB 1600 MHz DDR3. Your results may vary, but you get the idea. : ) > Doubt the results? please discuss in [Crystal Issue#4383](https://github.com/crystal-lang/crystal/issues/4383). **Let's write faster code, together! :trollface:** ## Measurement Tool Use Crystal's built-in [benchmark](https://crystal-lang.org/api/0.22.0/Benchmark.html). ## Run the Benchmarks ```bash $ make ``` ### Template ```crystal require "benchmark" def fast end def slow end Benchmark.ips do |x| x.report("fast code description") { fast } x.report("slow code description") { slow } end ``` ## Idioms ### Index - [Array](#array) - [Enumerable](#enumerable) - [General](#general) - [Hash](#hash) - [NamedTuple](#namedtuple) - [Proc & Block](#proc--block) - [String](#string) > Test in Crystal 0.35.1 (2020-06-19) LLVM: 10.0.0 Default target: x86_64-apple-macosx ### Array #### `first` vs `index[0]` [code](code/array/first-vs-index[0].cr) ``` $ crystal build --release --no-debug -o bin/code/array/first-vs-index[0] code/array/first-vs-index[0].cr $ ./bin/code/array/first-vs-index[0] Array#first 265.31M ( 3.77ns) (±11.17%) 0.0B/op 1.01× slower Array#[0] 267.85M ( 3.73ns) (± 6.86%) 0.0B/op fastest ``` #### `insert` vs `unshift` [code](code/array/insert-vs-unshift.cr) ``` $ crystal build --release --no-debug -o bin/code/array/insert-vs-unshift code/array/insert-vs-unshift.cr $ ./bin/code/array/insert-vs-unshift Array#insert 1.30 (768.66ms) (± 1.33%) 1.5MB/op fastest Array#unshift 1.29 (775.05ms) (± 1.81%) 1.5MB/op 1.01× slower ``` #### `last` vs `index[-1]` [code](code/array/last-vs-index[-1].cr) ``` $ crystal build --release --no-debug -o bin/code/array/last-vs-index[-1] code/array/last-vs-index[-1].cr $ ./bin/code/array/last-vs-index[-1] Array#[-1] 273.97M ( 3.65ns) (± 4.16%) 0.0B/op fastest Array#last 273.61M ( 3.65ns) (± 4.75%) 0.0B/op 1.00× slower ``` #### `range` vs `times.map` [code](code/array/range-vs-times.map.cr) ``` $ crystal build --release --no-debug -o bin/code/array/range-vs-times.map code/array/range-vs-times.map.cr $ ./bin/code/array/range-vs-times.map Range#to_a 1.11M (897.91ns) (±17.84%) 1.67kB/op fastest Times#to_a 1.02M (980.17ns) (±17.56%) 1.69kB/op 1.09× slower ``` ### Enumerable #### `each push` vs `map` [code](code/enumerable/each-push-vs-map.cr) ``` $ crystal build --release --no-debug -o bin/code/enumerable/each-push-vs-map code/enumerable/each-push-vs-map.cr $ ./bin/code/enumerable/each-push-vs-map Array#map 507.91k ( 1.97µs) (±11.92%) 3.96kB/op fastest Array#each + push 145.04k ( 6.89µs) (±18.89%) 12.7kB/op 3.50× slower Array#each_with_object 155.85k ( 6.42µs) (±17.07%) 12.7kB/op 3.26× slower ``` #### `each` vs `loop` [code](code/enumerable/each-vs-loop.cr) ``` $ crystal build --release --no-debug -o bin/code/enumerable/each-vs-loop code/enumerable/each-vs-loop.cr $ ./bin/code/enumerable/each-vs-loop While Loop 1.64M (609.64ns) (± 7.66%) 0.0B/op 159.20× slower #each 261.15M ( 3.83ns) (±10.82%) 0.0B/op fastest ``` #### `each_with_index` vs `while loop` [code](code/enumerable/each_with_index-vs-while-loop.cr) ``` $ crystal build --release --no-debug -o bin/code/enumerable/each_with_index-vs-while-loop code/enumerable/each_with_index-vs-while-loop.cr $ ./bin/code/enumerable/each_with_index-vs-while-loop While Loop 1.51M (661.13ns) (± 9.29%) 0.0B/op 6.94× slower each_with_index 10.50M ( 95.23ns) (±17.95%) 0.0B/op fastest ``` #### `map flatten` vs `flat_map` [code](code/enumerable/map-flatten-vs-flat_map.cr) ``` $ crystal build --release --no-debug -o bin/code/enumerable/map-flatten-vs-flat_map code/enumerable/map-flatten-vs-flat_map.cr $ ./bin/code/enumerable/map-flatten-vs-flat_map Array#flat_map (Tuple) 902.86k ( 1.11µs) (± 6.63%) 3.65kB/op fastest Array#map.flatten (Tuple) 664.00k ( 1.51µs) (± 6.00%) 4.69kB/op 1.36× slower Array#flat_map (Array) 238.37k ( 4.20µs) (± 5.73%) 7.18kB/op 3.79× slower Array#map.flatten (Array) 193.64k ( 5.16µs) (± 3.78%) 9.39kB/op 4.66× slower ``` #### `reverse.each` vs `reverse_each` [code](code/enumerable/reverse.each-vs-reverse_each.cr) ``` $ crystal build --release --no-debug -o bin/code/enumerable/reverse.each-vs-reverse_each code/enumerable/reverse.each-vs-reverse_each.cr $ ./bin/code/enumerable/reverse.each-vs-reverse_each Array#reverse.each 4.03M (248.39ns) (± 5.02%) 480B/op 4.94× slower Array#reverse_each 19.88M ( 50.30ns) (± 2.49%) 0.0B/op fastest ``` #### `sort` vs `sort_by` [code](code/enumerable/sort-vs-sort_by.cr) ``` $ crystal build --release --no-debug -o bin/code/enumerable/sort-vs-sort_by code/enumerable/sort-vs-sort_by.cr $ ./bin/code/enumerable/sort-vs-sort_by Enumerable#sort 145.32k ( 6.88µs) (± 2.89%) 3.07kB/op 1.17× slower Enumerable#sort_by 170.71k ( 5.86µs) (± 4.47%) 1.04kB/op fastest ``` ### General #### Assignment [code](code/general/assignment.cr) ``` $ crystal build --release --no-debug -o bin/code/general/assignment code/general/assignment.cr $ ./bin/code/general/assignment Sequential Assignment 611.21M ( 1.64ns) (± 4.98%) 0.0B/op 1.00× slower Parallel Assignment 613.61M ( 1.63ns) (± 5.04%) 0.0B/op fastest ``` #### `hash` vs `struct` vs `namedtuple` [code](code/general/hash-vs-struct-vs-namedtuple.cr) ``` $ crystal build --release --no-debug -o bin/code/general/hash-vs-struct-vs-namedtuple code/general/hash-vs-struct-vs-namedtuple.cr $ ./bin/code/general/hash-vs-struct-vs-namedtuple NamedTuple 515.36M ( 1.94ns) (± 4.05%) 0.0B/op fastest Struct 503.85M ( 1.98ns) (± 6.54%) 0.0B/op 1.02× slower Hash 9.60M (104.18ns) (± 2.76%) 208B/op 53.69× slower ``` #### `loop` vs `while_true` [code](code/general/loop-vs-while_true.cr) ``` $ crystal build --release --no-debug -o bin/code/general/loop-vs-while_true code/general/loop-vs-while_true.cr $ ./bin/code/general/loop-vs-while_true While Loop 512.11M ( 1.95ns) (± 5.15%) 0.0B/op fastest Kernel Loop 482.98M ( 2.07ns) (±16.94%) 0.0B/op 1.06× slower ``` #### `positional_argument` vs `named_argument` [code](code/general/positional_argument-vs-named_argument.cr) ``` $ crystal build --release --no-debug -o bin/code/general/positional_argument-vs-named_argument code/general/positional_argument-vs-named_argument.cr $ ./bin/code/general/positional_argument-vs-named_argument Named arguments 564.18M ( 1.77ns) (±16.11%) 0.0B/op 1.03× slower Positional arguments 578.90M ( 1.73ns) (±10.46%) 0.0B/op fastest ``` #### `property` vs `getter_and_setter` [code](code/general/property-vs-getter_and_setter.cr) ``` $ crystal build --release --no-debug -o bin/code/general/property-vs-getter_and_setter code/general/property-vs-getter_and_setter.cr $ ./bin/code/general/property-vs-getter_and_setter property 50.89M ( 19.65ns) (± 5.34%) 32.0B/op fastest getter_and_setter 49.68M ( 20.13ns) (± 7.27%) 32.0B/op 1.02× slower ``` ### Hash #### `[]?` vs `has_key?` [code](code/hash/[]?-vs-has_key?.cr) ``` $ crystal build --release --no-debug -o bin/code/hash/[]?-vs-has_key? code/hash/[]?-vs-has_key?.cr $ ./bin/code/hash/[]?-vs-has_key? Hash#[]? 41.12M ( 24.32ns) (±12.09%) 0.0B/op 1.01× slower Hash#has_key? 41.48M ( 24.11ns) (± 8.25%) 0.0B/op fastest ``` #### `bracket` vs `fetch` [code](code/hash/bracket-vs-fetch.cr) ``` $ crystal build --release --no-debug -o bin/code/hash/bracket-vs-fetch code/hash/bracket-vs-fetch.cr $ ./bin/code/hash/bracket-vs-fetch Hash#[] 95.60M ( 10.46ns) (± 6.16%) 0.0B/op 1.02× slower Hash#fetch 97.08M ( 10.30ns) (± 9.36%) 0.0B/op fastest ``` #### `clone` vs `dup` [code](code/hash/clone-vs-dup.cr) ``` $ crystal build --release --no-debug -o bin/code/hash/clone-vs-dup code/hash/clone-vs-dup.cr $ ./bin/code/hash/clone-vs-dup Hash#dup 5.39M (185.50ns) (±17.96%) 480B/op fastest Hash#clone 293.35k ( 3.41µs) (±10.17%) 5.94kB/op 18.38× slower ``` #### `keys each` vs `each_key` [code](code/hash/keys-each-vs-each_key.cr) ``` $ crystal build --release --no-debug -o bin/code/hash/keys-each-vs-each_key code/hash/keys-each-vs-each_key.cr $ ./bin/code/hash/keys-each-vs-each_key Hash#keys.each 4.25M (235.11ns) (± 8.09%) 240B/op 1.11× slower Hash#each_key 4.71M (212.43ns) (±22.16%) 160B/op fastest ``` #### `merge bang` vs `[]=` [code](code/hash/merge-bang-vs-[]=.cr) ``` $ crystal build --release --no-debug -o bin/code/hash/merge-bang-vs-[]= code/hash/merge-bang-vs-[]=.cr $ ./bin/code/hash/merge-bang-vs-[]= Hash#merge! 67.40k ( 14.84µs) (±23.77%) 16.6kB/op 4.19× slower Hash#[]= 282.73k ( 3.54µs) (±21.37%) 4.14kB/op fastest ``` ### Namedtuple #### `bracket` vs `fetch` [code](code/namedtuple/bracket-vs-fetch.cr) ``` $ crystal build --release --no-debug -o bin/code/namedtuple/bracket-vs-fetch code/namedtuple/bracket-vs-fetch.cr $ ./bin/code/namedtuple/bracket-vs-fetch NamedTuple#[] 294.37M ( 3.40ns) (±19.52%) 0.0B/op 1.00× slower NamedTuple#fetch 295.49M ( 3.38ns) (±19.80%) 0.0B/op fastest ``` #### `fetch` vs `fetch_with_block` [code](code/namedtuple/fetch-vs-fetch_with_block.cr) ``` $ crystal build --release --no-debug -o bin/code/namedtuple/fetch-vs-fetch_with_block code/namedtuple/fetch-vs-fetch_with_block.cr $ ./bin/code/namedtuple/fetch-vs-fetch_with_block NamedTuple#fetch + const 168.24M ( 5.94ns) (± 6.53%) 0.0B/op 1.81× slower NamedTuple#fetch + block 304.53M ( 3.28ns) (± 4.50%) 0.0B/op fastest NamedTuple#fetch + arg 296.07M ( 3.38ns) (± 6.99%) 0.0B/op 1.03× slower ``` ### Proc & Block #### `block` vs `to_proc` [code](code/proc-and-block/block-vs-to_proc.cr) ``` $ crystal build --release --no-debug -o bin/code/proc-and-block/block-vs-to_proc code/proc-and-block/block-vs-to_proc.cr $ ./bin/code/proc-and-block/block-vs-to_proc Block 331.06k ( 3.02µs) (±13.18%) 2.6kB/op 1.10× slower Symbol#to_proc 362.78k ( 2.76µs) (± 5.27%) 2.6kB/op fastest ``` #### `proc call` vs `yield` [code](code/proc-and-block/proc-call-vs-yield.cr) ``` $ crystal build --release --no-debug -o bin/code/proc-and-block/proc-call-vs-yield code/proc-and-block/proc-call-vs-yield.cr $ ./bin/code/proc-and-block/proc-call-vs-yield block.call 513.72M ( 1.95ns) (± 4.51%) 0.0B/op fastest block + yield 501.67M ( 1.99ns) (± 7.25%) 0.0B/op 1.02× slower block argument 512.94M ( 1.95ns) (± 5.41%) 0.0B/op 1.00× slower yield 482.96M ( 2.07ns) (±15.43%) 0.0B/op 1.06× slower ``` ### String #### Concatenation [code](code/string/concatenation.cr) ``` $ crystal build --release --no-debug -o bin/code/string/concatenation code/string/concatenation.cr $ ./bin/code/string/concatenation String#+ 44.62M ( 22.41ns) (± 8.00%) 32.0B/op fastest String#{} 23.68M ( 42.22ns) (±16.74%) 32.0B/op 1.88× slower String#% 4.28M (233.43ns) (±20.03%) 176B/op 10.41× slower ``` #### `ends string-matching-match` vs `end_with` [code](code/string/ends-string-matching-match-vs-end_with.cr) ``` $ crystal build --release --no-debug -o bin/code/string/ends-string-matching-match-vs-end_with code/string/ends-string-matching-match-vs-end_with.cr $ ./bin/code/string/ends-string-matching-match-vs-end_with String#end_with? 238.71M ( 4.19ns) (±11.61%) 0.0B/op fastest String#=~ 7.93M (126.04ns) (± 4.61%) 16.0B/op 30.09× slower ``` #### Equal-substring-of-char [code](code/string/equal-substring-of-char.cr) ``` $ crystal build --release --no-debug -o bin/code/string/equal-substring-of-char code/string/equal-substring-of-char.cr $ ./bin/code/string/equal-substring-of-char "==="[0] == '=' 298.29M ( 3.35ns) (± 7.06%) 0.0B/op fastest "==="[0].to_s == "=" 23.29M ( 42.94ns) (± 6.52%) 48.0B/op 12.81× slower "==="[0] == "=".chars[0] 27.62M ( 36.21ns) (± 4.66%) 48.0B/op 10.80× slower ``` #### `equal` vs `match` [code](code/string/equal-vs-match.cr) ``` $ crystal build --release --no-debug -o bin/code/string/equal-vs-match code/string/equal-vs-match.cr $ ./bin/code/string/equal-vs-match String#match 15.00M ( 66.65ns) (± 8.74%) 16.0B/op 1.02× slower Regexp#=== 15.32M ( 65.27ns) (± 9.61%) 16.0B/op fastest String#=~ 14.67M ( 68.17ns) (± 8.60%) 16.0B/op 1.04× slower ``` #### `gsub` vs `sub` [code](code/string/gsub-vs-sub.cr) ``` $ crystal build --release --no-debug -o bin/code/string/gsub-vs-sub code/string/gsub-vs-sub.cr $ ./bin/code/string/gsub-vs-sub String#sub 3.67M (272.77ns) (± 5.43%) 1.22kB/op fastest String#gsub 1.37M (728.87ns) (± 4.13%) 1.22kB/op 2.67× slower ``` #### `includes` vs `to_s.includes` [code](code/string/includes-vs-to_s.includes.cr) ``` $ crystal build --release --no-debug -o bin/code/string/includes-vs-to_s.includes code/string/includes-vs-to_s.includes.cr $ ./bin/code/string/includes-vs-to_s.includes String#includes? 368.22M ( 2.72ns) (± 8.30%) 0.0B/op 1.02× slower Nil#to_s#includes? 376.21M ( 2.66ns) (± 6.76%) 0.0B/op fastest ``` #### `nil` vs `to_s.empty` [code](code/string/nil-vs-to_s.empty.cr) ``` $ crystal build --release --no-debug -o bin/code/string/nil-vs-to_s.empty code/string/nil-vs-to_s.empty.cr $ ./bin/code/string/nil-vs-to_s.empty String#nil? 468.25M ( 2.14ns) (±14.49%) 0.0B/op fastest Nil#to_s#empty? 450.24M ( 2.22ns) (±14.74%) 0.0B/op 1.04× slower ``` #### `sub` vs `chomp` [code](code/string/sub-vs-chomp.cr) ``` $ crystal build --release --no-debug -o bin/code/string/sub-vs-chomp code/string/sub-vs-chomp.cr $ ./bin/code/string/sub-vs-chomp String#chomp"string" 43.85M ( 22.81ns) (±12.35%) 32.0B/op fastest String#sub/regexp/ 3.57M (280.13ns) (± 5.92%) 176B/op 12.28× slower ``` ## You may also like - [halite](https://github.com/icyleaf/halite) - HTTP Requests Client with a chainable REST API, built-in sessions and middlewares. - [totem](https://github.com/icyleaf/totem) - Load and parse a configuration file or string in JSON, YAML, dotenv formats. - [markd](https://github.com/icyleaf/markd) - Yet another markdown parser built for speed, Compliant to CommonMark specification. - [poncho](https://github.com/icyleaf/poncho) - A .env parser/loader improved for performance. - [popcorn](https://github.com/icyleaf/popcorn) - Easy and Safe casting from one type to another. ================================================ FILE: code/array/first-vs-index[0].cr ================================================ require "benchmark" ARRAY = (1..100).to_a def fast ARRAY.first end def slow ARRAY[0] end Benchmark.ips do |x| x.report("Array#first") { fast } x.report("Array#[0]") { slow } end ================================================ FILE: code/array/insert-vs-unshift.cr ================================================ require "benchmark" Benchmark.ips do |x| x.report("Array#insert") do array = [] of Int32 100_000.times { |i| array.insert(0, i) } end x.report("Array#unshift") do array = [] of Int32 100_000.times { |i| array.unshift(i) } end end ================================================ FILE: code/array/last-vs-index[-1].cr ================================================ require "benchmark" ARRAY = (1..100).to_a def fast ARRAY[-1] end def slow ARRAY.last end Benchmark.ips do |x| x.report("Array#[-1]") { fast } x.report("Array#last") { slow } end ================================================ FILE: code/array/range-vs-times.map.cr ================================================ require "benchmark" Benchmark.ips do |x| x.report("Range#to_a") do (1..100).to_a end x.report("Times#to_a") do 100.times.map { |i| i + 1 }.to_a end end ================================================ FILE: code/enumerable/each-push-vs-map.cr ================================================ require "benchmark" ARRAY = (1..1000).to_a def fastest ARRAY.map { |i| i } end def fast array = [] of Int32 ARRAY.each { |i| array.push i } array end def slow ARRAY.each_with_object([] of Int32) { |i, obj| obj << i } end Benchmark.ips do |x| x.report("Array#map") { fastest } x.report("Array#each + push") { fast } x.report("Array#each_with_object") { slow } end ================================================ FILE: code/enumerable/each-vs-loop.cr ================================================ require "benchmark" ARRAY = (1..100).to_a def fast i = 0 while i < ARRAY.size ARRAY[i] i += 1 end end def slow ARRAY.each do |number| number end end Benchmark.ips do |x| x.report("While Loop") { fast } x.report("#each") { slow } end ================================================ FILE: code/enumerable/each_with_index-vs-while-loop.cr ================================================ require "benchmark" ARRAY = (1..100).to_a def fast index = 0 while index < ARRAY.size ARRAY[index] + index index += 1 end ARRAY end def slow ARRAY.each_with_index do |number, index| number + index end end Benchmark.ips do |x| x.report("While Loop") { fast } x.report("each_with_index") { slow } end ================================================ FILE: code/enumerable/map-flatten-vs-flat_map.cr ================================================ require "benchmark" ARRAY = (1..100).to_a def fastest ARRAY.flat_map { |e| {e, e} } end def fast ARRAY.map { |e| {e, e} }.flatten end def slow ARRAY.flat_map { |e| [e, e] } end def slowest ARRAY.map { |e| [e, e] }.flatten end Benchmark.ips do |x| x.report("Array#flat_map (Tuple)") { fastest } x.report("Array#map.flatten (Tuple)") { fast } x.report("Array#flat_map (Array)") { slow } x.report("Array#map.flatten (Array)") { slowest } end ================================================ FILE: code/enumerable/reverse.each-vs-reverse_each.cr ================================================ require "benchmark" ARRAY = (1..100).to_a def slow ARRAY.reverse.each { |x| x } end def fast ARRAY.reverse_each { |x| x } end Benchmark.ips do |x| x.report("Array#reverse.each") { slow } x.report("Array#reverse_each") { fast } end ================================================ FILE: code/enumerable/sort-vs-sort_by.cr ================================================ require "benchmark" struct User property name def initialize(@name : String) end end ARRAY = Array.new(100) do User.new(sprintf "%010d", rand(1_000_000_000)) end def fast ARRAY.sort_by(&.name) end def slow ARRAY.sort { |a, b| a.name <=> b.name } end Benchmark.ips do |x| x.report("Enumerable#sort") { fast } x.report("Enumerable#sort_by") { slow } end ================================================ FILE: code/general/assignment.cr ================================================ require "benchmark" def fast _a, _b, _c, _d, _e, _f, _g, _h = 1, 2, 3, 4, 5, 6, 7, 8 nil end def slow _a = 1 _b = 2 _c = 3 _d = 4 _e = 5 _f = 6 _g = 7 _h = 8 nil end Benchmark.ips do |x| x.report("Sequential Assignment") { slow } x.report("Parallel Assignment") { fast } end ================================================ FILE: code/general/hash-vs-struct-vs-namedtuple.cr ================================================ require "benchmark" struct SampleStruct property name, year def initialize(@name : String, @year : Int32) end end def slow {"name" => "Crystal", "year" => 2011} end def fast SampleStruct.new("Crystal", 2011) end def fastest {name: "Crystal", year: 2011} end Benchmark.ips do |x| x.report("NamedTuple") { fastest } x.report("Struct") { fast } x.report("Hash") { slow } end ================================================ FILE: code/general/loop-vs-while_true.cr ================================================ require "benchmark" NUMBER = 100_000_000 def fast index = 0 while true break if index > NUMBER index += 1 end end def slow index = 0 loop do break if index > NUMBER index += 1 end end Benchmark.ips do |x| x.report("While Loop") { fast } x.report("Kernel Loop") { slow } end ================================================ FILE: code/general/positional_argument-vs-named_argument.cr ================================================ require "benchmark" module M def self.func(a, b, c) end end def fast M.func(a: 1, b: 2, c: 3) end def slow M.func(1, 2, 3) end Benchmark.ips do |x| x.report("Named arguments") { fast } x.report("Positional arguments") { slow } end ================================================ FILE: code/general/property-vs-getter_and_setter.cr ================================================ require "benchmark" class User property :first_name def initialize @first_name = "" @last_name = "" end def last_name @last_name end def last_name=(value) @last_name = value end end def slow user = User.new user.last_name = "Wang" user.last_name end def fast user = User.new user.first_name = "Wang" user.first_name end Benchmark.ips do |x| x.report("property") { fast } x.report("getter_and_setter") { slow } end ================================================ FILE: code/hash/[]?-vs-has_key?.cr ================================================ require "benchmark" HASH = {"a" => "z"} Benchmark.ips do |x| x.report("Hash#[]?") do HASH["a"]? HASH["b"]? end x.report("Hash#has_key?") do HASH.has_key? "a" HASH.has_key? "b" end end ================================================ FILE: code/hash/bracket-vs-fetch.cr ================================================ require "benchmark" HASH = {"fast" => "ruby"} NAMEDTUPLE = {fast: "ruby"} Benchmark.ips do |x| x.report("Hash#[]") do HASH["fast"] end x.report("Hash#fetch") do HASH.fetch("fast") { } end end ================================================ FILE: code/hash/clone-vs-dup.cr ================================================ require "benchmark" HASH = ("a".."z").map { |v| {v => v.bytes} } def fast HASH.dup end def slow HASH.clone end Benchmark.ips do |x| x.report("Hash#dup") { fast } x.report("Hash#clone") { slow } end ================================================ FILE: code/hash/keys-each-vs-each_key.cr ================================================ require "benchmark" HASH = { "provider" => "facebook", "uid" => "1234567", "info" => { "nickname" => "jbloggs", "email" => "joe@bloggs.com", "name" => "Joe Bloggs", "first_name" => "Joe", "last_name" => "Bloggs", "image" => "http://graph.facebook.com/1234567/picture?type=square", "urls" => {"Facebook" => "http://www.facebook.com/jbloggs"}, "location" => "Palo Alto, California", "verified" => true, }, "credentials" => { "token" => "ABCDEF...", "expires_at" => 1321747205, "expires" => true, }, "extra" => { "raw_info" => { "id" => "1234567", "name" => "Joe Bloggs", "first_name" => "Joe", "last_name" => "Bloggs", "link" => "http://www.facebook.com/jbloggs", "username" => "jbloggs", "location" => {"id" => "123456789", "name" => "Palo Alto, California"}, "gender" => "male", "email" => "joe@bloggs.com", "timezone" => -8, "locale" => "en_US", "verified" => true, "updated_time" => "2011-11-11T06:21:03+0000", }, }, } def slow HASH.keys.each(&.downcase) end def fast HASH.each_key(&.downcase) end Benchmark.ips do |x| x.report("Hash#keys.each") { slow } x.report("Hash#each_key") { fast } end ================================================ FILE: code/hash/merge-bang-vs-[]=.cr ================================================ require "benchmark" ENUM = (1..100) def slow ENUM.each_with_object({} of Int32 => Int32) do |e, h| h.merge!({e => e}) end end def fast ENUM.each_with_object({} of Int32 => Int32) do |e, h| h[e] = e end end Benchmark.ips do |x| x.report("Hash#merge!") { slow } x.report("Hash#[]=") { fast } end ================================================ FILE: code/namedtuple/bracket-vs-fetch.cr ================================================ require "benchmark" NAMEDTUPLE = {fast: "ruby"} def fast NAMEDTUPLE[:fast] end def slow NAMEDTUPLE.fetch(:fast, "") end Benchmark.ips do |x| x.report("NamedTuple#[]") { fast } x.report("NamedTuple#fetch") { slow } end ================================================ FILE: code/namedtuple/fetch-vs-fetch_with_block.cr ================================================ require "benchmark" HASH = {writing: :fast_ruby} DEFAULT = "fast ruby" Benchmark.ips do |x| x.report("NamedTuple#fetch + const") { HASH.fetch(:writing, DEFAULT) } x.report("NamedTuple#fetch + block") { HASH.fetch(:writing) { "fast ruby" } } x.report("NamedTuple#fetch + arg") { HASH.fetch(:writing, "fast ruby") } end ================================================ FILE: code/proc-and-block/block-vs-to_proc.cr ================================================ require "benchmark" RANGE = (1..100) def slow RANGE.map { |i| i.to_s } end def fast RANGE.map(&.to_s) end Benchmark.ips do |x| x.report("Block") { slow } x.report("Symbol#to_proc") { fast } end ================================================ FILE: code/proc-and-block/proc-call-vs-yield.cr ================================================ require "benchmark" def slow(&block) block.call end def slow2(&block) yield end def slow3(&block) end def fast yield end Benchmark.ips do |x| x.report("block.call") { slow { 1 + 1 } } x.report("block + yield") { slow2 { 1 + 1 } } x.report("block argument") { slow3 { 1 + 1 } } x.report("yield") { fast { 1 + 1 } } end ================================================ FILE: code/string/concatenation.cr ================================================ require "benchmark" WORLD = "world" def fastest "hello " + WORLD end def fast "hello #{WORLD}" end def slow "hello %s" % WORLD end Benchmark.ips do |x| x.report("String#+") { fastest } x.report("String\#{}") { fast } x.report("String#%") { slow } end ================================================ FILE: code/string/ends-string-matching-match-vs-end_with.cr ================================================ require "benchmark" SLUG = "root_url" def fast SLUG.ends_with?("_url") end def slow SLUG =~ /_url$/ end Benchmark.ips do |x| x.report("String#end_with?") { fast } x.report("String#=~") { slow } end ================================================ FILE: code/string/equal-substring-of-char.cr ================================================ require "benchmark" def fastest "===="[0] == '=' end def fast "===="[0].to_s == "=" end def slow "===="[0] == "=".chars[0] end Benchmark.ips do |x| x.report("\"===\"[0] == '='") { fastest } x.report("\"===\"[0].to_s == \"=\"") { fast } x.report("\"===\"[0] == \"=\".chars[0]") { slow } end ================================================ FILE: code/string/equal-vs-match.cr ================================================ require "benchmark" def fastest "foo".match(/boo/) end def fast /boo/ === "foo" end def slow "foo" =~ /boo/ end Benchmark.ips do |x| x.report("String#match") { fastest } x.report("Regexp#===") { fast } x.report("String#=~") { slow } end ================================================ FILE: code/string/gsub-vs-sub.cr ================================================ require "benchmark" URL = "http://www.thelongestlistofthelongeststuffatthelongestdomainnameatlonglast.com/wearejustdoingthistobestupidnowsincethiscangoonforeverandeverandeverbutitstilllookskindaneatinthebrowsereventhoughitsabigwasteoftimeandenergyandhasnorealpointbutwehadtodoitanyways.html" def slow URL.gsub("http://", "https://") end def fast URL.sub("http://", "https://") end Benchmark.ips do |x| x.report("String#sub") { fast } x.report("String#gsub") { slow } end ================================================ FILE: code/string/includes-vs-to_s.includes.cr ================================================ require "benchmark" def slow "foobar".includes?("crystal") end def fast nil.to_s.includes?("crystal") end Benchmark.ips do |x| x.report("String#includes?") { fast } x.report("Nil#to_s#includes?") { slow } end ================================================ FILE: code/string/nil-vs-to_s.empty.cr ================================================ require "benchmark" def slow nil.to_s.empty? end def fast "".nil? end Benchmark.ips do |x| x.report("String#nil?") { fast } x.report("Nil#to_s#empty?") { slow } end ================================================ FILE: code/string/sub-vs-chomp.cr ================================================ require "benchmark" SLUG = "YourSubclassType" def fast SLUG.chomp("Type") end def slow SLUG.sub(/Type\z/, "") end Benchmark.ips do |x| x.report("String#chomp\"string\"") { fast } x.report("String#sub/regexp/") { slow } end ================================================ FILE: shard.yml ================================================ name: fast-crystal version: 0.2.2 authors: - icyleaf targets: fast-crystal: main: src/fast-crystal.cr crystal: 0.35.1 license: MIT ================================================ FILE: src/fast-crystal.cr ================================================ require "file_utils" BIN_PATH = "bin/code" SOURCE_PATH = File.expand_path("code") CRYSTAL_BIN = `which crystal`.strip CRYSTAL_VERSION = `#{CRYSTAL_BIN} -v`.strip if Dir.exists?(BIN_PATH) FileUtils.rm_rf(BIN_PATH) end puts puts "> Test in #{CRYSTAL_VERSION.split("\n").join(" ")}" puts section = "" files = if (file = ARGV[0]?) && File.exists? file {file} else Dir.glob("code/**/*.cr").sort end files.each do |file| test_file = File.basename(file) test_section = file.sub(test_file, "").sub("code/", "")[0..-2] bin_section = File.join(BIN_PATH, test_section) bin_file = File.join(bin_section, File.basename(test_file, File.extname(test_file))) FileUtils.mkdir_p(bin_section) if section.empty? || section != test_section section = test_section puts "### " + (section == "proc-and-block" ? "Proc & Block" : section.capitalize) puts end compile_command = [CRYSTAL_BIN, "build", "--release", "--no-debug", "-o", bin_file, file] print_title(file) puts puts "```" puts "$ " + compile_command.join(" ") `#{compile_command.join(" ")}` puts "$ ./" + bin_file puts puts `./#{bin_file}` puts "```" puts end def print_title(file) filename = File.basename(file) file_path = file.sub(SOURCE_PATH, "") title = filename.sub(File.extname(filename), "") title = if title.includes?("-vs-") title_sections = [] of String title.split("-vs-").each do |str| title_sections << "`" + (str.includes?("[-1]") ? str : str.sub("-", " ")) + "`" end title_sections.join(" vs ") else title.capitalize end puts "#### " + title + " [code](" + file_path + ")" end