Top Banner
Attributes Unwrapped
77

Attributes Unwrapped: Lessons under the surface of active record

May 15, 2015

Download

Technology

.toster

Ведущий разработчик Ruby on Rails (Rails Core member) Джон Лейтон не так давно работал над совершенствованием реализации работы с атрибутами в Active Record. Он расскажет о своем опыте работы над важной для производительности областью Rails, даст советы и расскажет о техниках, которые могут быть применены к собственным приложениям слушателей.
Говоря о будущем, Джон также расскажет о своих идеях по изменению API работы с атрибутами в лучшую сторону; эти изменения могут появиться в Rails 4.0.
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Attributes Unwrapped: Lessons under the surface of active record

Attributes Unwrapped

Page 2: Attributes Unwrapped: Lessons under the surface of active record

@jonleighton

Page 3: Attributes Unwrapped: Lessons under the surface of active record
Page 4: Attributes Unwrapped: Lessons under the surface of active record

I lied to you

Page 5: Attributes Unwrapped: Lessons under the surface of active record
Page 6: Attributes Unwrapped: Lessons under the surface of active record

Measure

Page 7: Attributes Unwrapped: Lessons under the surface of active record

Measure

Refactor

Page 8: Attributes Unwrapped: Lessons under the surface of active record

Measure

Refactor

Optimise

Page 9: Attributes Unwrapped: Lessons under the surface of active record

LET’S DO SCIENCE

Page 10: Attributes Unwrapped: Lessons under the surface of active record

gem 'rails', '3.2.0'

require 'active_record'

ActiveRecord::Base

.establish_connection(

:adapter => 'sqlite3',

:database => ':memory:')

Page 11: Attributes Unwrapped: Lessons under the surface of active record

ActiveRecord::Schema.define do

create_table :posts do |t|

t.string :title

end

end

class Post < ActiveRecord::Base

end

p = Post.create(:title => "lol")

Page 12: Attributes Unwrapped: Lessons under the surface of active record

require 'benchmark'

n = 1_000_000

Benchmark.report(20) do |r|

r.report('attribute') do

n.times { p.title }

end

r.report('read_attribute') do

n.times { p[:title] }

end

end

Page 13: Attributes Unwrapped: Lessons under the surface of active record

attribute

read_attribute

1.09 s

2.87 s

Page 14: Attributes Unwrapped: Lessons under the surface of active record

gem install

benchmark_suite

Page 15: Attributes Unwrapped: Lessons under the surface of active record

require 'benchmark/ips'

Benchmark.ips do |r|

r.report('attribute') do

p.title

end

r.report('read_attribute') do

p[:title]

end

end

Page 16: Attributes Unwrapped: Lessons under the surface of active record

attribute

read_attribute

828,648

299,856

Page 17: Attributes Unwrapped: Lessons under the surface of active record

Ruby 1.9

Page 18: Attributes Unwrapped: Lessons under the surface of active record

Ruby 1.8

Page 19: Attributes Unwrapped: Lessons under the surface of active record

Ruby 1.8

oops!

Page 20: Attributes Unwrapped: Lessons under the surface of active record

define_method

Page 21: Attributes Unwrapped: Lessons under the surface of active record

method compilation

Page 22: Attributes Unwrapped: Lessons under the surface of active record
Page 23: Attributes Unwrapped: Lessons under the surface of active record
Page 24: Attributes Unwrapped: Lessons under the surface of active record

create_table :roflcopters do |t|

t.string " ROFL:ROFL:ROFL:ROFL"

t.string " _^____ "

t.string " L __/ []\ "

t.string "LOL===_ \ "

t.string " L \_________] "

t.string " I I "

t.string " --------/ "

end

Page 25: Attributes Unwrapped: Lessons under the surface of active record

if compilable?

class_eval <<-STR

def #{attr_name}

...

end

STR

else

define_method attr_name do

...

end

end

Page 26: Attributes Unwrapped: Lessons under the surface of active record

attr_name

=~

/\A[a-zA-Z_]\w*[!?=]?\z/

Page 27: Attributes Unwrapped: Lessons under the surface of active record

:title

=~

/\A[a-zA-Z_]\w*[!?=]?\z/

Page 28: Attributes Unwrapped: Lessons under the surface of active record

:title =~ /.../

# => true

Ruby 1.9

Page 29: Attributes Unwrapped: Lessons under the surface of active record

Ruby 1.8

:title =~ /.../

# => false

Page 30: Attributes Unwrapped: Lessons under the surface of active record

def __temp__

...

end

alias "@#>" :__temp__

undef_method :__temp__

Page 31: Attributes Unwrapped: Lessons under the surface of active record

def __temp__

...

end

alias "@#>" :__temp__

undef_method :__temp__

DON'T USE THIS

Page 32: Attributes Unwrapped: Lessons under the surface of active record

API Changes

Page 33: Attributes Unwrapped: Lessons under the surface of active record
Page 34: Attributes Unwrapped: Lessons under the surface of active record

def title

self[:title].upcase

end

Page 35: Attributes Unwrapped: Lessons under the surface of active record

def title

super.upcase

end

Page 36: Attributes Unwrapped: Lessons under the surface of active record

module A

def foo

"bar"

end

end

Page 37: Attributes Unwrapped: Lessons under the surface of active record

class B

include A

def foo

super.upcase

end

end

Page 38: Attributes Unwrapped: Lessons under the surface of active record

Don’t fight Ruby

<3 <3 <3

Page 39: Attributes Unwrapped: Lessons under the surface of active record

#read_attribute

#[]

Page 40: Attributes Unwrapped: Lessons under the surface of active record

def read_attribute(name)

name = "_#{name}"

if respond_to?(name)

send(name)

else

# other stuff

end

end

Page 41: Attributes Unwrapped: Lessons under the surface of active record

Module.new

Page 42: Attributes Unwrapped: Lessons under the surface of active record

def read_attribute(name)

mod = self.class.methods_module

if mod.respond_to?(name)

mod.send(name, @attributes)

else

# other stuff

end

end

Page 43: Attributes Unwrapped: Lessons under the surface of active record

Module.new.respond_to?(:name)

# => true

Page 44: Attributes Unwrapped: Lessons under the surface of active record

Module.new { extend self }

Page 45: Attributes Unwrapped: Lessons under the surface of active record

Module.new { extend self }

mod.method_defined?(:name)

Page 46: Attributes Unwrapped: Lessons under the surface of active record

Module.new { extend self }

INSANE HACKmod.method_defined?(:name)

Page 47: Attributes Unwrapped: Lessons under the surface of active record

Still too slow☹

Page 48: Attributes Unwrapped: Lessons under the surface of active record

gem install

perftools.rb

Page 49: Attributes Unwrapped: Lessons under the surface of active record
Page 50: Attributes Unwrapped: Lessons under the surface of active record

if attr_name == 'id'

attr_name =

self.class.primary_key

end

Page 51: Attributes Unwrapped: Lessons under the surface of active record

No code is fasterthan no code

Page 52: Attributes Unwrapped: Lessons under the surface of active record

def title

cast @attributes['title']

end

Page 53: Attributes Unwrapped: Lessons under the surface of active record

def title

cast @attributes[:title]

end

Page 54: Attributes Unwrapped: Lessons under the surface of active record
Page 55: Attributes Unwrapped: Lessons under the surface of active record

class A

def initialize

@attributes = { :foo => 1 }

end

def foo

@attributes[:foo]

end

end

Page 56: Attributes Unwrapped: Lessons under the surface of active record

class B

def initialize

@attributes = { 'foo' => 1 }

end

def foo

@attributes['foo']

end

end

Page 57: Attributes Unwrapped: Lessons under the surface of active record

Benchmark.ips do |r|

r.report('symbol') { a.foo }

r.report('string') { b.foo }

end

Page 58: Attributes Unwrapped: Lessons under the surface of active record
Page 59: Attributes Unwrapped: Lessons under the surface of active record

code = "@attributes['foo']"

iseq =

RubyVM::InstructionSequence

.compile(code)

puts iseq.disassemble

Page 60: Attributes Unwrapped: Lessons under the surface of active record

trace 1

getinstancevariable :@attributes

putstring "foo"

opt_aref <ic:2>

leave

Page 61: Attributes Unwrapped: Lessons under the surface of active record

trace 1

getinstancevariable :@attributes

putobject :foo

opt_aref <ic:2>

leave

Page 62: Attributes Unwrapped: Lessons under the surface of active record

DEFINE_INSN

putstring

(VALUE str)

()

(VALUE val)

{

val = rb_str_resurrect(str);

}

Page 63: Attributes Unwrapped: Lessons under the surface of active record

DEFINE_INSN

putobject

(VALUE val)

()

(VALUE val)

{

/* */

}

Page 64: Attributes Unwrapped: Lessons under the surface of active record
Page 65: Attributes Unwrapped: Lessons under the surface of active record

code = "@attributes['foo']"

compiled =

Rubinius::Compiler

.compile_string(code)

puts compiled.decode

Page 66: Attributes Unwrapped: Lessons under the surface of active record

push_ivar 0

push_literal "foo"

string_dup

send_stack :[], 1

pop

push_true

ret

Page 67: Attributes Unwrapped: Lessons under the surface of active record

push_ivar 0

push_literal :foo

send_stack :[], 1

pop

push_true

ret

Page 68: Attributes Unwrapped: Lessons under the surface of active record
Page 69: Attributes Unwrapped: Lessons under the surface of active record

jruby --bytecode

-e "@attributes['foo']"

Page 70: Attributes Unwrapped: Lessons under the surface of active record

RubyString

Page 71: Attributes Unwrapped: Lessons under the surface of active record

RubyString

RubySymbol

Page 72: Attributes Unwrapped: Lessons under the surface of active record

Performance problemsare a code smell

Page 73: Attributes Unwrapped: Lessons under the surface of active record

Be a scientist

Page 74: Attributes Unwrapped: Lessons under the surface of active record

But...

Page 75: Attributes Unwrapped: Lessons under the surface of active record

Avoid roflscaling!

Page 76: Attributes Unwrapped: Lessons under the surface of active record

Thanks!IT'S OVER!

Page 77: Attributes Unwrapped: Lessons under the surface of active record

Thanks!IT'S OVER!

(♥ @tenderlove ♥)