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

การเปรียบเทียบรหัสทับทิม

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