ฉันทำงานกับ OO MATLAB มาระยะหนึ่งแล้วก็พบปัญหาด้านประสิทธิภาพที่คล้ายกัน
คำตอบสั้น ๆ คือ: ใช่ OOP ของ MATLAB ค่อนข้างช้า มีวิธีการเรียกใช้อย่างมีนัยสำคัญสูงกว่าภาษา OO หลักและไม่มีอะไรที่คุณสามารถทำได้ เหตุผลส่วนหนึ่งอาจเป็นไปได้ว่าสำนวน MATLAB ใช้รหัส "vectorized" เพื่อลดจำนวนการเรียกใช้เมธอดและค่าใช้จ่ายต่อการโทรไม่สำคัญ
ฉันเปรียบเทียบประสิทธิภาพโดยการเขียนฟังก์ชั่น "nop" แบบไม่มีอะไรเป็นฟังก์ชันและวิธีการต่าง ๆ นี่คือผลการทั่วไปบางส่วน
>> call_nops
คอมพิวเตอร์: PCWIN เปิดตัว: 2009b
เรียกแต่ละฟังก์ชัน / วิธี 100,000 ครั้ง
ฟังก์ชั่น nop (): 0.02261 วินาที 0.23 usec ต่อการโทรหนึ่งครั้ง
ฟังก์ชั่น nop1-5 (): 0.02182 วินาที 0.22 usec ต่อการโทรหนึ่งครั้ง
ฟังก์ชั่นย่อย nop (): 0.02244 วินาที 0.22 usec ต่อการโทรหนึ่งครั้ง
@ () [] ฟังก์ชั่นไม่ระบุชื่อ: 0.08461 วินาที 0.85 usec ต่อการโทรหนึ่งครั้ง
วิธี nop (obj): 0.24664 วินาที 2.47 usec ต่อการโทร
เมธอด nop1-5 (obj): 0.23469 วินาที 2.35 usec ต่อการโทรหนึ่งครั้ง
nop () ฟังก์ชั่นส่วนตัว: 0.02197 วินาที 0.22 usec ต่อการโทรหนึ่งครั้ง
classdef nop (obj): 0.90547 วินาที 9.05 usec ต่อการโทรหนึ่งครั้ง
classdef obj.nop (): 1.75522 วินาที 17.55 usec ต่อการโทรหนึ่งครั้ง
classdef private_nop (obj): 0.84738 วินาที 8.47 usec ต่อการโทรหนึ่งครั้ง
classdef nop (obj) (m-file): 0.90560 วินาที 9.06 usec ต่อการโทรหนึ่งครั้ง
classdef class.staticnop (): 1.16361 วินาที 11.64 usec ต่อการโทรหนึ่งครั้ง
Java nop (): 2.43035 วินาที 24.30 usec ต่อการโทร
Java static_nop (): 0.87682 วินาที 8.77 usec ต่อการโทรหนึ่งครั้ง
Java nop () จาก Java: 0.00014 วินาที 0.00 usec ต่อการโทร
MEX mexnop (): 0.11409 วินาที 1.14 usec ต่อการโทรหนึ่งครั้ง
C nop (): 0.00001 วินาที 0.00 usec ต่อการโทรหนึ่งครั้ง
ผลลัพธ์ที่คล้ายกันใน R2008a ถึง R2009b นี่คือใน Windows XP x64 ที่ใช้งาน MATLAB แบบ 32 บิต
"Java nop ()" เป็นวิธี Java ที่ไม่ต้องทำอะไรเรียกจากภายในลูป M-code และรวมถึงโอเวอร์เฮดการส่ง MATLAB-to-Java ด้วยการโทรแต่ละครั้ง "Java nop () จาก Java" เป็นสิ่งเดียวกันกับที่เรียกในการวนซ้ำ Java () สำหรับและไม่ต้องเสียค่าปรับขอบเขต ใช้เวลา Java และ C กับเม็ดเกลือ คอมไพเลอร์ฉลาดสามารถเพิ่มประสิทธิภาพการโทรออกไปอย่างสมบูรณ์
กลไกการกำหนดขอบเขตแพ็กเกจเป็นของใหม่เปิดตัวในเวลาเดียวกันกับคลาส classdef พฤติกรรมของมันอาจจะเกี่ยวข้อง
ข้อสรุปเบื้องต้นบางประการ:
- วิธีการช้ากว่าฟังก์ชั่น
- ใหม่สไตล์ (classdef) วิธีการช้ากว่าวิธีแบบเก่า
obj.nop()
ไวยากรณ์ใหม่ช้ากว่าnop(obj)
ไวยากรณ์แม้สำหรับวิธีการเดียวกันกับวัตถุ classdef เหมือนกันสำหรับวัตถุ Java (ไม่แสดง) nop(obj)
หากคุณต้องการที่จะไปอย่างรวดเร็วโทร
- เมธอดโอเวอร์เฮดการเรียกใช้สูงกว่า (ประมาณ 2x) ใน MATLAB แบบ 64 บิตบน Windows (ไม่แสดง)
- การส่งเมธอด MATLAB ช้ากว่าภาษาอื่น
การพูดว่าทำไมจึงเป็นเช่นนั้นเพียงแค่การเก็งกำไรในส่วนของฉัน internals OO ของโปรแกรม MATLAB ไม่ใช่แบบสาธารณะ มันไม่ได้เป็นปัญหาที่แปลแล้วรวบรวมต่อ se - MATLAB มี JIT - แต่การพิมพ์และไวยากรณ์ของ MATLAB อาจทำให้การทำงานมากขึ้นในเวลาทำงาน (เช่นคุณไม่สามารถบอกได้จากไวยากรณ์เพียงอย่างเดียวว่า "f (x)" เป็นการเรียกใช้ฟังก์ชันหรือดัชนีไปยังอาร์เรย์ แต่ขึ้นอยู่กับสถานะของพื้นที่ทำงาน ณ เวลาทำงาน) อาจเป็นเพราะนิยามคลาสของ MATLAB ถูกผูกไว้ ไปยังสถานะระบบไฟล์ในลักษณะที่ภาษาอื่น ๆ ไม่ได้
แล้วจะทำอย่างไรดี?
MATLAB ที่ใช้สำนวนวิธีนี้คือ "vectorize" โค้ดของคุณโดยการกำหนดโครงสร้างคลาสของคุณเพื่อให้อินสแตนซ์ของวัตถุล้อมรอบอาร์เรย์ นั่นคือแต่ละเขตข้อมูลของมันมีอาร์เรย์แบบขนาน (เรียกว่า "planar" องค์กรในเอกสาร MATLAB) แทนที่จะมีอาร์เรย์ของวัตถุแต่ละฟิลด์มีค่าสเกลาร์กำหนดวัตถุซึ่งเป็นอาร์เรย์เองและให้วิธีการใช้อาร์เรย์เป็นอินพุตและทำการโทรแบบเวกเตอร์บนฟิลด์และอินพุต สิ่งนี้ช่วยลดจำนวนของการเรียกเมธอดที่ทำหวังว่าเพียงพอที่ค่าใช้จ่ายการจัดส่งไม่ใช่คอขวด
การเลียนแบบคลาส C ++ หรือ Java ใน MATLAB อาจไม่เหมาะสม โดยทั่วไปแล้วคลาส Java / C ++ จะถูกสร้างขึ้นเพื่อให้วัตถุเป็นหน่วยการสร้างที่เล็กที่สุดตามที่คุณสามารถทำได้ (นั่นคือคลาสที่แตกต่างกันจำนวนมาก) และคุณเขียนไว้ในอาร์เรย์อาร์เรย์วัตถุคอลเลกชัน ฯลฯ และวนซ้ำกับลูป ในการสร้างคลาส MATLAB อย่างรวดเร็วให้เปลี่ยนวิธีการด้านในออก มีคลาสที่ใหญ่กว่าซึ่งมีเขตข้อมูลเป็นอาร์เรย์และเรียกใช้วิธีการ vectorized บนอาร์เรย์เหล่านั้น
จุดคือการจัดเรียงรหัสของคุณเพื่อเล่นจุดแข็งของภาษา - การจัดการอาร์เรย์คณิตศาสตร์เวกเตอร์ - และหลีกเลี่ยงจุดอ่อน
แก้ไข: ตั้งแต่โพสต์ต้นฉบับ R2010b และ R2011a ออกมาแล้ว ภาพรวมเป็นเช่นเดียวกับ MCOs เรียกรับบิตเร็วขึ้นและ Java และวิธีการแบบเก่าโทรได้รับช้า
แก้ไข: ฉันเคยมีบันทึกย่อบางส่วนที่นี่ใน "ความไวเส้นทาง" พร้อมตารางเพิ่มเติมของการกำหนดเวลาการเรียกใช้ฟังก์ชันซึ่งเวลาการทำงานได้รับผลกระทบจากวิธีการกำหนดค่าเส้นทาง Matlab แต่ดูเหมือนจะเป็นความผิดปกติของการตั้งค่าเครือข่ายเฉพาะของฉัน เวลา. แผนภูมิข้างต้นสะท้อนให้เห็นถึงเวลาตามแบบฉบับของความเหนือกว่าของการทดสอบของฉันในช่วงเวลา
อัปเดต: R2011b
แก้ไข (2/13/2012): R2011b ไม่ทำงานและภาพการทำงานมีการเปลี่ยนแปลงมากพอที่จะอัปเดต
Arch: PCWIN เปิดตัว: 2011b
เครื่อง: R2011b, Windows XP, 8x คอร์ i7-2600 @ 3.40GHz, 3 GB RAM, NVIDIA NVS 300
ทำแต่ละการดำเนินงาน 100,000 ครั้ง
รูปแบบรวม µsec ต่อการโทร
ฟังก์ชัน nop (): 0.01578 0.16
nop (), 10x loop unroll: 0.01477 0.15
nop (), 100x loop unroll: 0.01518 0.15
ฟังก์ชันย่อย nop (): 0.01559 0.16
@ () [] ฟังก์ชั่นไม่ระบุชื่อ: 0.06400 0.64
วิธี nop (obj): 0.28482 2.85
ฟังก์ชั่นส่วนตัว nop (): 0.01505 0.15
classdef nop (obj): 0.43323 4.33
classdef obj.nop (): 0.81087 8.11
classdef private_nop (obj): 0.32272 3.23
classdef class.staticnop (): 0.88959 8.90
classdef คงที่: 1.51890 15.19
คุณสมบัติ classdef: 0.12992 1.30
คุณสมบัติ classdef พร้อมทะลุ: 1.39912 13.99
+ pkg.nop () ฟังก์ชัน: 0.87345 8.73
+ pkg.nop () จากภายใน + pkg: 0.80501 8.05
Java obj.nop (): 1.86378 18.64
Java nop (obj): 0.22645 2.26
Java feval ('nop', obj): 0.52544 5.25
Java Klass.static_nop (): 0.35357 3.54
Java obj.nop () จาก Java: 0.00010 0.00
MEX mexnop (): 0.08709 0.87
C nop (): 0.00001 0.00
j () (builtin): 0.00251 0.03
ฉันคิดว่าผลที่สุดของเรื่องนี้คือ:
- วิธี MCOS / classdef เร็วขึ้น ราคาใกล้เคียงกับคลาสสไตล์เก่าตราบใดที่คุณใช้
foo(obj)
ไวยากรณ์ ดังนั้นความเร็วของเมธอดจึงไม่ใช่เหตุผลที่จะใช้คลาสคลาสสิกในกรณีส่วนใหญ่อีกต่อไป (คำชื่นชม, MathWorks!)
- วางฟังก์ชั่นใน namespaces ทำให้พวกเขาช้า (ไม่ใช่ใหม่ใน R2011b เพียงใหม่ในการทดสอบของฉัน)
อัปเดต: R2014a
ฉันสร้างรหัสการเปรียบเทียบและเรียกใช้บน R2014a ใหม่
Matlab R2014a บน PCWIN64
Matlab 8.3.0.532 (R2014a) / Java 1.7.0_11 บน PCWIN64 Windows 7 6.1 (eilonwy-win7)
เครื่อง: Core i7-3615QM CPU @ 2.30GHz, RAM 4 GB (แพลตฟอร์ม VMware เสมือน)
nIters = 100000
เวลาทำการ (µsec)
ฟังก์ชั่น nop (): 0.14
ฟังก์ชั่นย่อย nop (): 0.14
@ () [] ฟังก์ชั่นไม่ระบุชื่อ: 0.69
วิธี nop (obj): 3.28
nop () ไพรเวต fcn บน @class: 0.14
classdef nop (obj): 5.30
classdef obj.nop (): 10.78
classdef pivate_nop (obj): 4.88
classdef class.static_nop (): 11.81
classdef คงที่: 4.18
คุณสมบัติ classdef: 1.18
คุณสมบัติ classdef พร้อม getter: 19.26
+ pkg.nop () ฟังก์ชั่น: 4.03
+ pkg.nop () จากด้านใน + pkg: 4.16
feval ('nop'): 2.31
feval (@nop): 0.22
eval ('nop'): 59.46
Java obj.nop (): 26.07
Java nop (obj): 3.72
Java feval ('nop', obj): 9.25
Java Klass.staticNop (): 10.54
Java obj.nop () จาก Java: 0.01
MEX mexnop (): 0.91
builtin j (): 0.02
การเข้าถึงฟิลด์ struct s.foo: 0.14
isempty (ถาวร): 0.00
อัพเดท: R2015b: วัตถุเร็วขึ้น!
นี่คือผลลัพธ์ R2015b ของโปรดได้รับจาก @Shaked นี่เป็นการเปลี่ยนแปลงครั้งใหญ่ : OOP เร็วขึ้นอย่างมากและขณะนี้obj.method()
ไวยากรณ์เร็วเท่าmethod(obj)
และเร็วกว่าวัตถุ OOP รุ่นเก่า
Matlab R2015b บน PCWIN64
Matlab 8.6.0.267246 (R2015b) / Java 1.7.0_60 บน PCWIN64 Windows 8 6.2 (nanit-shaked)
เครื่อง: Core i7-4720HQ CPU @ 2.60GHz, RAM 16 GB (20378)
nIters = 100000
เวลาทำการ (µsec)
ฟังก์ชั่น nop (): 0.04
ฟังก์ชันย่อย nop (): 0.08
@ () [] ฟังก์ชั่นไม่ระบุชื่อ: 1.83
วิธี nop (obj): 3.15
nop () ไพรเวต fcn บน @class: 0.04
classdef nop (obj): 0.28
classdef obj.nop (): 0.31
classdef pivate_nop (obj): 0.34
classdef class.static_nop (): 0.05
classdef คงที่: 0.25
คุณสมบัติ classdef: 0.25
คุณสมบัติ classdef พร้อม getter: 0.64
+ pkg.nop () ฟังก์ชัน: 0.04
+ pkg.nop () จากภายใน + pkg: 0.04
feval ('nop'): 8.26
feval (@nop): 0.63
eval ('nop'): 21.22
Java obj.nop (): 14.15
Java nop (obj): 2.50
Java feval ('nop', obj): 10.30
Java Klass.staticNop (): 24.48
Java obj.nop () จาก Java: 0.01
MEX mexnop (): 0.33
builtin j (): 0.15
struct s.foo การเข้าถึงข้อมูล: 0.25
isempty (ถาวร): 0.13
อัปเดต: R2018a
นี่คือผลลัพธ์ของ R2018a มันไม่ใช่การกระโดดครั้งใหญ่ที่เราเห็นเมื่อมีการเปิดตัวเอ็นจิ้นการประมวลผลใหม่ใน R2015b แต่ก็ยังคงเป็นปีที่ดีกว่าปีที่ผ่านมา สะดุดตาฟังก์ชั่นที่ไม่ระบุชื่อได้เร็วขึ้นทาง
Matlab R2018a บน MACI64
Matlab 9.4.0.813654 (R2018a) / Java 1.8.0_144 บน MACI64 Mac OS X 10.13.5 (eilonwy)
เครื่อง: Core i7-3615QM CPU @ 2.30GHz, RAM 16 GB
nIters = 100000
เวลาทำการ (µsec)
ฟังก์ชั่น nop (): 0.03
ฟังก์ชันย่อย nop (): 0.04
@ () [] ฟังก์ชั่นไม่ระบุชื่อ: 0.16
classdef nop (obj): 0.16
classdef obj.nop (): 0.17
classdef pivate_nop (obj): 0.16
classdef class.static_nop (): 0.03
classdef คงที่: 0.16
คุณสมบัติ classdef: 0.13
คุณสมบัติ classdef พร้อม getter: 0.39
+ pkg.nop () ฟังก์ชัน: 0.02
+ pkg.nop () จากด้านใน + pkg: 0.02
feval ('nop'): 15.62
feval (@nop): 0.43
eval ('nop'): 32.08
Java obj.nop (): 28.77
Java nop (obj): 8.02
Java feval ('nop', obj): 21.85
Java Klass.staticNop (): 45.49
Java obj.nop () จาก Java: 0.03
MEX mexnop (): 3.54
builtin j (): 0.10
struct s.foo การเข้าถึงข้อมูล: 0.16
isempty (ถาวร): 0.07
อัปเดต: R2018b และ R2019a: ไม่มีการเปลี่ยนแปลง
ไม่มีการเปลี่ยนแปลงที่สำคัญ ฉันไม่ได้กังวลที่จะรวมผลการทดสอบ
รหัสที่มาสำหรับการวัดประสิทธิภาพ
ฉันได้วางซอร์สโค้ดสำหรับการวัดประสิทธิภาพเหล่านี้บน GitHub ซึ่งเผยแพร่ภายใต้ใบอนุญาต MIT https://github.com/apjanke/matlab-bench