Ruby มีเครื่องมือเปรียบเทียบในไลบรารีมาตรฐานเพื่อช่วยวัดประสิทธิภาพของโค้ดของคุณ มีประโยชน์มากที่สุดเมื่อเปรียบเทียบการใช้งานทั้งสองแบบ เพื่อดูว่าอันไหนเร็วที่สุด
ในตัวอย่างนี้ เราได้รับมอบหมายให้แปลง Hash ด้วยคีย์สตริง (เช่น {"foo" => "bar"}
เป็นหนึ่งเดียวกับสัญลักษณ์ (เช่น {:foo => "bar"}
). ตลอดตัวอย่าง เราจะใช้แฮชที่มีคีย์และค่าสำหรับตัวอักษรแต่ละตัวในภาษาอังกฤษ
หากต้องการสร้างแฮชนี้อย่างรวดเร็วโดยไม่ต้องพิมพ์ เราจะแปลงตัวอักษรจำนวนหนึ่งเป็นแฮชทดสอบของเรา เราจะใส่ไว้ใน input
ตัวแปรเพื่อใช้ในภายหลัง
input = ("a".."z").map {|letter| [letter, letter]}.to_h
# => {"a"=>"a", "b"=>"b", "c"=>"c", "d"=>"d", "e"=>"e", "f"=>"f", "g"=>"g", "h"=>"h", "i"=>"i", "j"=>"j", "k"=>"k", "l"=>"l", "m"=>"m", "n"=>"n", "o"=>"o", "p"=>"p", "q"=>"q", "r"=>"r", "s"=>"s", "t"=>"t", "u"=>"u", "v"=>"v", "w"=>"w", "x"=>"x", "y"=>"y", "z"=>"z"}
ตอนนี้เรามี input
. แล้ว เพื่อทดสอบการใช้งานของเรา เราจะเขียนตัวแปรหนึ่งเพื่อดูว่ามันทำงานอย่างไร บรรทัดเดียวที่ดีในการแปลงคีย์ทั้งหมดในแฮชอินพุตของเราเป็นสัญลักษณ์แทนสตริงจะมีลักษณะดังนี้:
input.map { |key, value| [key.to_sym, value] }.to_h
# => {:a=>"a", :b=>"b", :c=>"c", :d=>"d", :e=>"e", :f=>"f", :g=>"g", :h=>"h", :i=>"i", :j=>"j", :k=>"k", :l=>"l", :m=>"m", :n=>"n", :o=>"o", :p=>"p", :q=>"q", :r=>"r", :s=>"s", :t=>"t", :u=>"u", :v=>"v", :w=>"w", :x=>"x", :y=>"y", :z=>"z"}
การใช้งานนี้ใช้ map
วิธีการวนรอบแฮชเพื่อเรียกใช้บล็อกสำหรับคู่คีย์-ค่าแต่ละคู่ ในบล็อก จะแปลงคีย์เป็นสัญลักษณ์และส่งกลับอาร์เรย์สององค์ประกอบด้วยคีย์สัญลักษณ์ที่สร้างขึ้นใหม่และค่าที่ไม่ถูกแตะต้อง
ผลลัพธ์จาก map
คำสั่งคืออาร์เรย์ที่มีอาร์เรย์คีย์-ค่า 26 ชุด เนื่องจากเราต้องการแฮช เราจึงใช้ #to_h
เพื่อแปลงอาร์เรย์ใหม่ของเรากลับเป็นแฮช
Benchmark.measure
ตอนนี้เรามีการใช้งานจริงแล้ว เราสามารถใช้โมดูลเกณฑ์มาตรฐานของ Ruby เพื่อดูว่ามันทำงานอย่างไร
require 'benchmark'
input = ('a'..'z').map { |letter| [letter, letter] }.to_h
puts Benchmark.measure {
50_000.times do
input.map { |key, value| [key.to_sym, value] }.to_h
end
}
Benchmark.measure
ใช้บล็อกซึ่งถูกดำเนินการในขณะที่ติดตามว่าใช้เวลานานเท่าใดในการดำเนินการ ส่งคืนสตริงรายงานซึ่งพิมพ์ไปยังคอนโซลโดยใช้ puts
.
เนื่องจากเป็นโค้ดสั้นๆ เราจึงเรียกใช้ 50,000 ครั้งเพื่อให้แน่ใจว่าได้ผลลัพธ์ที่มองเห็นได้
$ ruby bench.rb
0.810000 0.000000 0.810000 ( 0.816964)
สตริงรายงานแสดงตัวเลขสี่ตัว ซึ่งแสดงถึงเวลา CPU ของผู้ใช้ (เวลาที่ใช้ในการรันโค้ดของคุณ), เวลา CPU ของระบบ (เวลาที่ใช้ในเคอร์เนล) ทั้งผู้ใช้และเวลา CPU ของระบบเพิ่มขึ้น และเวลาจริง (หรือนาฬิกาแขวน) ที่ใช้สำหรับบล็อกเพื่อดำเนินการในวงเล็บ
เวลากำแพงแสดงให้เราเห็นว่าเราสามารถเรียกใช้บล็อกของโค้ดที่สูงกว่า 50,000 ครั้งในเวลาน้อยกว่า 800 มิลลิวินาที แม้ว่าตัวเลขนั้นจะเป็นตัวเลขที่น่าประทับใจ แต่เราไม่รู้ว่ามันหมายถึงอะไร เว้นแต่เราจะเปรียบเทียบกับการใช้งานโค้ดแบบอื่น
Benchmark.bm
นอกจาก Benchmark.measure
, Ruby ให้ Benchmark.bm
ซึ่งสามารถเรียกใช้ตัวอย่างโค้ดหลายรายการและพิมพ์ผลลัพธ์ได้ สำหรับแต่ละตัวอย่าง เราจะเรียก Benchmark#report
ด้วยชื่อและบล็อกที่จะดำเนินการ
require 'benchmark'
input = ("a".."z").map { |letter| [letter, letter] }.to_h
n = 50_000
Benchmark.bm do |benchmark|
benchmark.report("Hash[]") do
n.times do
input.map { |key, value| [key.to_sym, value] }.to_h
end
end
benchmark.report("{}.tap") do
n.times do
{}.tap do |new_hash|
input.each do |key, value|
new_hash[key.to_sym] = value
end
end
end
end
end
ในการวัดประสิทธิภาพนี้ เราจะใช้ Benchmark.bm
เพื่อทดสอบการใช้งานสองครั้งโดยรันแต่ละ 50,000 ครั้ง บล็อกการวัดแรกเหมือนกับตัวอย่างก่อนหน้านี้
ในบล็อกการวัดที่สอง เราใช้การใช้งานที่ยาวขึ้น ซึ่งจะสร้างแฮชใหม่ขึ้นก่อน มันวนรอบแฮชสตริงคีย์และเพิ่มองค์ประกอบลงในแฮชใหม่สำหรับทุกรายการ วิธีนี้ทำให้ไม่ต้องแปลงแฮชเป็นอาร์เรย์ และเปลี่ยนกลับเป็นแฮชเมื่อเสร็จสิ้น
การเรียกใช้การวัดประสิทธิภาพอีกครั้งจะแสดงให้เราเห็นว่าการใช้งานนี้เร็วขึ้นมากกว่า 25% แม้ว่าโค้ดจะยาวกว่า (และฉลาดน้อยกว่าเล็กน้อย) เมื่อเทียบกับบรรทัดเดียวที่เราเคยลองใช้มาก่อน
$ ruby bench.rb
user system total real
Hash[] 0.850000 0.000000 0.850000 ( 0.851106)
{}.tap 0.610000 0.020000 0.630000 ( 0.637070)
การเปรียบเทียบเพิ่มเติม
เมื่อทำงานกับโค้ดที่สำคัญใน codebase ของคุณ การรันการวัดประสิทธิภาพเพื่อเปรียบเทียบการใช้งานที่แตกต่างกันสามารถให้ข้อมูลเชิงลึกมากขึ้นเกี่ยวกับความเร็วในการดำเนินการ การเปรียบเทียบการใช้งานต่างๆ เพื่อทำความเข้าใจผลกระทบต่อประสิทธิภาพจะทำให้คุณหลีกเลี่ยงรูปแบบการต่อต้านและเขียน Ruby ได้เร็วขึ้น
เคล็ดลับ :สำนวนทั่วไปจำนวนมากมีการเปรียบเทียบไว้ล่วงหน้า และผลลัพธ์ของสำนวนเหล่านี้ได้รับการตีพิมพ์เป็น fast-ruby การอ่านตัวอย่างจะช่วยให้คุณประหยัดการเปรียบเทียบได้ในอนาคต
มีตัวเลือกเพิ่มเติมที่คุณสามารถทดสอบได้สำหรับตัวอย่างนี้ และไลบรารีการเปรียบเทียบของ Ruby มีคุณลักษณะที่ซับซ้อนมากขึ้นซึ่งคุณสามารถลองใช้ได้ แต่สิ่งนี้เป็นการแนะนำที่ดีเกี่ยวกับวิธีการทำงานของการเปรียบเทียบใน Ruby หากคุณต้องการทราบข้อมูลเพิ่มเติมเกี่ยวกับการเปรียบเทียบ หรือมีคำถามหรือข้อเสนอแนะ โปรดแจ้งให้เราทราบที่ @AppSignal