Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
The Value of Being Lazy
Search
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
Erik Berlin
November 24, 2015
Programming
3
850
The Value of Being Lazy
…or How I Made OpenStruct 10X Faster
Presented at Rails Israel 2015.
Erik Berlin
November 24, 2015
Tweet
Share
More Decks by Erik Berlin
See All by Erik Berlin
Enumerator::Lazy
sferik
2
620
Ruby Trivia 3
sferik
0
750
Ruby Trivia 2
sferik
0
810
Ruby Trivia
sferik
2
1.3k
💀 Symbols
sferik
5
2k
Content Negotiation for REST APIs
sferik
8
1k
Writing Fast Ruby
sferik
630
62k
Mutation Testing with Mutant
sferik
5
1.1k
Other Decks in Programming
See All in Programming
NetBSD+Raspberry Piで 本物のPSGを鳴らすデモを OSC駆動の7日間で作った話 / OSC2026Osaka
tsutsui
1
120
AWS re:Invent 2025参加 直前 Seattle-Tacoma Airport(SEA)におけるハードウェア紛失インシデントLT
tetutetu214
2
130
go directiveを最新にしすぎないで欲しい話──あるいは、Go 1.26からgo mod initで作られるgo directiveの値が変わる話 / Go 1.26 リリースパーティ
arthur1
2
270
生成AIを活用したソフトウェア開発ライフサイクル変革の現在値
hiroyukimori
PRO
0
140
kintone + ローカルLLM = ?
akit37
0
120
米国のサイバーセキュリティタイムラインと見る Goの暗号パッケージの進化
tomtwinkle
1
200
並行開発のためのコードレビュー
miyukiw
2
2k
JPUG勉強会 OSSデータベースの内部構造を理解しよう
oga5
2
220
CSC307 Lecture 12
javiergs
PRO
0
450
AIコーディングの理想と現実 2026 | AI Coding: Expectations vs. Reality 2026
tomohisa
0
580
Claude Codeセッション現状確認 2026福岡 / fukuoka-aicoding-00-beacon
monochromegane
3
300
izumin5210のプロポーザルのネタ探し #tskaigi_msup
izumin5210
1
440
Featured
See All Featured
Building AI with AI
inesmontani
PRO
1
740
How to Build an AI Search Optimization Roadmap - Criteria and Steps to Take #SEOIRL
aleyda
1
1.9k
Intergalactic Javascript Robots from Outer Space
tanoku
273
27k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
128
55k
Winning Ecommerce Organic Search in an AI Era - #searchnstuff2025
aleyda
1
1.9k
Building a A Zero-Code AI SEO Workflow
portentint
PRO
0
350
From Legacy to Launchpad: Building Startup-Ready Communities
dugsong
0
160
HDC tutorial
michielstock
1
460
Odyssey Design
rkendrick25
PRO
2
520
How Fast Is Fast Enough? [PerfNow 2025]
tammyeverts
3
460
How to audit for AI Accessibility on your Front & Back End
davetheseo
0
200
Are puppies a ranking factor?
jonoalderson
1
3k
Transcript
THE VALUE OF BEING LAZY or How I Made OpenStruct
10X Faster Erik Michaels-Ober @sferik
In Ruby, everything is an object. ∀ thing thing.is_a?(Object) #=>
true
In Ruby, every object has a class. ∀ object object.respond_to?(:class)
#=> true
In Ruby, every class has a class. ∴ Object.respond_to?(:class) #=>
true Object.class #=> Class
You can use classes to create new objects: object =
Object.new object.class #=> Object
You can use classes to create new classes: klass =
Class.new klass.class #=> Class
Usually, we create classes like this: class Point attr_accessor :x,
:y def initialize(x, y) @x, @y = x, y end end
You can replace such simple classes with structs: Point =
Struct.new(:x, :y)
OpenStruct requires even less definition: point = OpenStruct.new point.x =
1 point.y = 2
In this way, OpenStruct is similar to Hash: point =
Hash.new point[:x] = 1 point[:y] = 2
You can even initialize OpenStruct with a Hash: point =
OpenStruct.new(x: 1, y: 2) point.x #=> 1 point.y #=> 2
So why use OpenStruct instead of Hash?
Test double validator = OpenStruct.new expect(validator).to receive(:validate) code = PostalCode.new("94102",
validator) code.valid?
API response user = OpenStruct.new(JSON.parse(response)) user.name #=> Erik
Configuration object def options opts = OpenStruct.new yield opts opts
end
So OpenStruct is useful…but slow.
None
Steps to optimize code 1. Complain that code is slow
on Twitter 2. ??? 3. Profit
Actual steps to optimize code 1. Benchmark 2. Read code
3. Profit
Actual steps to optimize code 1. Benchmark 2. Read code
3. Profit
require "benchmark/ips" Point = Struct.new(:x, :y) def struct Point.new(0, 1)
end def ostruct OpenStruct.new(x: 0, y: 1) end Benchmark.ips do |x| x.report("ostruct") { ostruct } x.report("struct") { struct } end
Comparison: struct: 2927800.2 i/s ostruct: 84741.1 i/s - 34.55x slower
Actual steps to optimize code 1. Benchmark 2. Read code
3. Profit
def initialize(hash = nil) @table = {} if hash hash.each_pair
do |k, v| k = k.to_sym @table[k] = v new_ostruct_member(k) end end end
def new_ostruct_member(name) name = name.to_sym unless respond_to?(name) define_singleton_method(name) { @table[name]
} define_singleton_method("#{name}=") { |x| @table[name] = x } end name end
def method_missing(mid, *args) len = args.length if mname = mid[/.*(?==\z)/m]
@table[new_ostruct_member(mname)] = args[0] elsif len == 0 if @table.key?(mid) new_ostruct_member(mid) @table[mid] end end end
def initialize(hash = nil) @table = {} if hash hash.each_pair
do |k, v| k = k.to_sym @table[k] = v new_ostruct_member(k) end end end
Before: struct: 2927800.2 i/s ostruct: 84741.1 i/s - 34.55x slower
After: struct: 2927800.2 i/s ostruct: 940170.4 i/s - 3.11x slower
None
None
git log --reverse lib/ostruct.rb
None
Lazy evaluation
Enumerator::Lazy
lazy_integers = (1..Float::INFINITY).lazy lazy_integers.collect { |x| x ** 2 }.
select { |x| x.even? }. reject { |x| x < 1000 }. first(5) #=> [1024, 1156, 1296, 1444, 1600]
require "prime" lazy_primes = Prime.lazy lazy_primes.select { |x| (x -
2).prime? }. collect { |x| [x - 2, x] }. first(5) #=> [[3, 5], [5, 7], [11, 13], [17, 19], [29, 31]]
module Enumerable def repeat_after_first unless block_given? return to_enum(__method__) { size
* 2 - 1 if size } end each.with_index do |*val, index| index == 0 ? yield *val : 2.times { yield *val } end end end
require "prime" lazy_primes = Prime.lazy lazy_primes.repeat_after_first. each_slice(2). select { |x,
y| x + 2 == y }. first(5) #=> [[3, 5], [5, 7], [11, 13], [17, 19], [29, 31]]
require "date" lazy_dates = (Date.today..Date.new(9999)).lazy lazy_dates.select { |d| d.day ==
13 }. select { |d| d.friday? }. first(10)
lazy_file = File.readlines("/path/to/file").lazy lazy_file.detect { |x| x =~ /regexp/ }
Being lazy is efficient.
Being lazy is elegant.
Thanks to: Zachary Scott ROSS Conf Rails Israel
Thank you