Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

ผลกระทบของ OpenStruct ต่อ Performance: ทำไมมันอาจทำให้ Ruby Apps ช้าลง

เรา Rubyists รัก Hashes แต่ Hashes มีข้อบกพร่องที่รู้จักกันดี ตามที่ Richard 指出ใน Hashie Considered Harmful มันอาจ flexible เกินไป - typo เล็กน้อยก็ assign/reference keys ที่ไม่ได้ตั้งใจ

a = { type: "F150" } a[:typo] # nil 

Hash Alternatives ปוצบบ

ถ้าใช้ hashes เก็บ structured data อาจตัดสินใจว่าไม่ต้องการ flexibility มันแค่ทำให้ปวดหัว

มีทางเลือกいくつか สมมติต้องเก็บ XY coordinates ทางหนึ่งคือ define class ที่ทำหน้าที่เก็บคู่ XY coordinates

class PointClass # ไม่แนะนำลงท้าย class ด้วย Class :) attr_accessor :x, :y def initialize(args) @x = args.fetch(:x) @y = args.fetch(:y) end end point_class = PointClass.new(x: 1, y: 2) point_class.x # 1 

ถ้าแค่ encapsulate data Struct เรียบง่ายกว่า:

PointStruct = Struct.new(:x, :y) point_struct = PointStruct.new(1, 2) point_struct.x # 1 

ทางเลือกที่ 3 OpenStruct คล้าย Struct แต่ตั้งค่า arbitrary ได้เหมือน hash:

point_os = OpenStruct.new(x: 1, y: 2) point_os.x # 1 

Performance Implications

[UPDATE 7/10/2015: Benchmark script ผม unfair กับ hashes ตาม Patrick Helm ชี้ให้เห็น ใช้วิธี initialize ไม่ efficient โปรด ignore hash results แต่จุดหลัก OpenStruct ช้ามากยังคงถูกต้อง ดูการเปลี่ยนแปลงของเขาที่นี่]

ดู 4 ทางเลือก ผมสงสัย performance implications ถ้า deal กับข้อมูลน้อย ทุกอย่างพอดี แต่ถ้า thousands/millions items performance ของ hash vs OpenStruct vs struct vs class เริ่มสำคัญ

ที่ Honeybadger มี thousands exceptions reported ต่อวินาที เข้าใจ performance อย่างนี้อยู่ในใจตลอด

เขียน benchmark script 簡單 ใช้ benchmark-ips gem เพราะ auto calculate sample size และ report standard deviation

Initialization

Benchmark initialization times ของ PointClass, PointStruct, Hash, OpenStruct พบว่า PointClass และ PointStruct ชนะชัดเจน ~10x เร็วกว่า OpenStruct และ ~2x เร็วกว่า hash

ผลกระทบของ OpenStruct ต่อ Performance: ทำไมมันอาจทำให้ Ruby Apps ช้าลง

ผลกระทบของ OpenStruct ต่อ Performance: ทำไมมันอาจทำให้ Ruby Apps ช้าลง PointClass และ PointStruct เร็วกว่า OpenStruct เกือบ 10x

ผลลัพธ์สมเหตุสมผล Struct เรียบที่สุด เร็วสุด OpenStruct 複雜ที่สุด (wrapper ของ Hash) ช้าสุด แต่มากนิกของความต่างเร็วช้าค่อนข้าง surprising

หลัง experiment นี้ ผมจะหน่อยมากที่จะใช้ OpenStruct ใน code ที่สนใจ speed และจะมอง hashes ใน performance-critical code ด้วยตาสงสัย

Read / Write

ต่างจาก initialization ทั้ง 4 options เหมือนกันประมาณเวลา set และ access values

ผลกระทบของ OpenStruct ต่อ Performance: ทำไมมันอาจทำให้ Ruby Apps ช้าลง

ผลกระทบของ OpenStruct ต่อ Performance: ทำไมมันอาจทำให้ Ruby Apps ช้าลง Reading/writing benchmarks ไม่มีความต่างใหญ่ระหว่าง Struct, class, hash และ OpenStruct

Benchmarking Script

อยากรัน benchmark เอง ใช้ script ด้านล่าง ผมรันบน MRI 2.1 on OSX สนใจ performance อื่นๆ Michael Cohen มี gist สำหรับ MRI 2.2, JRuby และอื่นๆ

require 'benchmark/ips' require 'ostruct' data = { x: 100, y: 200 } PointStruct = Struct.new(:x, :y) class PointClass attr_accessor :x, :y def initialize(args) @x = args.fetch(:x) @y = args.fetch(:y) end end puts "\n\nINITIALIZATION ==========" Benchmark.ips do |x| x.report("PointStruct") { PointStruct.new(100, 200) } x.report("PointClass") { PointClass.new(data) } x.report("Hash") { Hash.new.merge(data) } x.report("OpenStruct") { OpenStruct.new(data) } end puts "\n\nREAD ==========" point_struct = PointStruct.new(100, 200) point_class = PointClass.new(data) point_hash = Hash.new.merge(data) point_open_struct = OpenStruct.new(data) Benchmark.ips do |x| x.report("PointStruct") { point_struct.x } x.report("PointClass") { point_class.x } x.report("Hash") { point_hash.fetch(:x) } x.report("OpenStruct") { point_open_struct.x } end puts "\n\nWRITE ==========" Benchmark.ips do |x| x.report("PointStruct") { point_struct.x = 1 } x.report("PointClass") { point_class.x = 1 } x.report("Hash") { point_hash[:x] = 1 } x.report("OpenStruct") { point_open_struct.x = 1 } end