วิธีจัดทำเอกสารและสอนผู้อื่น“ เพิ่มประสิทธิภาพจนเกินกว่าจะจดจำ” โค้ดที่ต้องใช้การคำนวณอย่างเข้มข้น?


11

บางครั้งมีโค้ด 1% ที่เข้มข้นในการคำนวณเพียงพอที่ต้องการการเพิ่มประสิทธิภาพระดับต่ำที่สุดที่หนักที่สุด ตัวอย่างคือการประมวลผลวิดีโอการประมวลผลภาพและการประมวลผลสัญญาณทุกชนิดโดยทั่วไป

เป้าหมายคือเพื่อจัดทำเอกสารและสอนเทคนิคการปรับให้เหมาะสมเพื่อให้โค้ดไม่สามารถกลายเป็นสิ่งที่ไม่สามารถทำลายได้และมีแนวโน้มที่จะถูกลบออกโดยนักพัฒนาซอฟต์แวร์รุ่นใหม่ (*)

(*) แม้จะมีความเป็นไปได้ว่าการเพิ่มประสิทธิภาพเฉพาะนั้นไม่มีประโยชน์อย่างสมบูรณ์ในซีพียูที่คาดเดาไม่ได้ในอนาคตเช่นนั้นรหัสจะถูกลบต่อไป

พิจารณาว่าข้อเสนอซอฟต์แวร์ (เชิงพาณิชย์หรือโอเพ่นซอร์ส) รักษาความได้เปรียบในการแข่งขันโดยมีรหัสที่เร็วที่สุดและใช้ประโยชน์จากสถาปัตยกรรมซีพียูใหม่ล่าสุดผู้เขียนซอฟต์แวร์มักจะต้องปรับแต่งโค้ดเพื่อให้ทำงานได้เร็วขึ้นในขณะเดียวกัน task, whlist ที่ยอมรับข้อผิดพลาดในการปัดเศษจำนวนเล็กน้อย

โดยทั่วไปผู้เขียนซอฟต์แวร์สามารถเก็บฟังก์ชันหลาย ๆ เวอร์ชันไว้เป็นเอกสารของการเพิ่มประสิทธิภาพ / การเขียนอัลกอริทึมใหม่ที่เกิดขึ้น ใครทำให้รุ่นเหล่านี้พร้อมให้ผู้อื่นศึกษาเทคนิคการปรับให้เหมาะสมได้อย่างไร

ที่เกี่ยวข้อง:


1
คุณสามารถเก็บเวอร์ชันต่าง ๆ ไว้ในโค้ดคอมเม้นท์พร้อมความคิดเห็นมากมายที่บอกผู้อ่านว่าเกิดอะไรขึ้น
Mike Dunlavey

1
และอย่าเพิ่งบอกพวกเขาว่าโค้ดกำลังทำอะไรอยู่ แต่ทำไมมันถึงเร็วขนาดนั้น รวมลิงค์ไปยังอัลกอริธึมหากจำเป็นไม่ว่าจะเป็นวิกิของคุณเองเอกสารหรือทรัพยากรที่มีอยู่บนอินเทอร์เน็ต (โปรดระวัง link-rot ในกรณีนั้นคุณควรคัดลอกลงในระบบ doc ของคุณเองพร้อมลิงค์ไปยังต้นฉบับ .)
Marjan Venema

1
@ MikeDunlavey: โอ้ไม่ได้โปรดออกความเห็น มีฟังก์ชั่นการใช้งานที่เหมือนกันหลายอย่างและเรียกใช้สิ่งที่เร็วที่สุด ด้วยวิธีนี้คุณสามารถเปลี่ยนเป็นรหัสรุ่นอื่นได้อย่างง่ายดายและทำการเปรียบเทียบทั้งหมด
sleske

2
@sleske บางครั้งเพียงแค่มีรหัสไบนารีมากขึ้นอาจทำให้ช้าลง
quant_dev

@quant_dev: ใช่นั่นสามารถเกิดขึ้นได้ ฉันแค่คิดว่ามันเป็นสิ่งสำคัญที่รหัสถูกสร้างขึ้นและเรียกใช้ (นึกคิด) เป็นประจำเพื่อให้ทันสมัย อาจสร้างในโหมดดีบักเท่านั้น
sleske

คำตอบ:


10

คำตอบสั้น ๆ

ทำการออปติไมซ์แบบท้องถิ่นทำให้ชัดเจนจัดทำเอกสารได้ดีและทำให้ง่ายต่อการเปรียบเทียบเวอร์ชันที่ได้รับการปรับปรุงซึ่งกันและกันและกับเวอร์ชันที่ไม่ได้เพิ่มประสิทธิภาพทั้งในแง่ของซอร์สโค้ดและประสิทธิภาพรันไทม์

คำตอบแบบเต็ม

หากการปรับให้เหมาะสมดังกล่าวมีความสำคัญต่อผลิตภัณฑ์ของคุณจริงๆแล้วคุณไม่จำเป็นต้องรู้ว่าทำไมการปรับให้เหมาะสมนั้นมีประโยชน์มาก่อน แต่ยังให้ข้อมูลที่เพียงพอเพื่อช่วยให้นักพัฒนาทราบว่าพวกเขาจะมีประโยชน์ในอนาคตหรือไม่

เป็นการดีที่คุณจะต้องทำการทดสอบประสิทธิภาพในกระบวนการสร้างของคุณดังนั้นคุณจะพบว่าเมื่อใดที่เทคโนโลยีใหม่ ๆ

โปรดจำไว้ว่า:

กฎข้อแรกของการเพิ่มประสิทธิภาพโปรแกรม: อย่าทำ

กฎข้อที่สองของการเพิ่มประสิทธิภาพโปรแกรม (สำหรับผู้เชี่ยวชาญเท่านั้น!): อย่าเพิ่งทำ "

- Michael A. Jackson

เพื่อให้ทราบว่าขณะนี้เป็นเวลาที่ต้องมีการเปรียบเทียบและการทดสอบ

ดังที่คุณกล่าวถึงปัญหาที่ใหญ่ที่สุดของโค้ดที่ได้รับการปรับให้เหมาะสมที่สุดคือมันยากที่จะรักษาไว้ดังนั้นเท่าที่จะทำได้คุณต้องแยกส่วนที่ปรับให้เหมาะสมออกจากส่วนที่ไม่ได้เพิ่มประสิทธิภาพ ไม่ว่าคุณจะทำสิ่งนี้ผ่านการเชื่อมโยงเวลาคอมไพล์การเรียกใช้ฟังก์ชันเสมือนรันไทม์หรือบางสิ่งในระหว่างนั้นไม่ควรสำคัญ สิ่งที่สำคัญคือเมื่อคุณทำการทดสอบคุณต้องการทดสอบกับทุกเวอร์ชันที่คุณสนใจในปัจจุบัน

ฉันอยากจะสร้างระบบในลักษณะที่รหัสการผลิตขั้นพื้นฐานแบบไม่สามารถจับเวลาได้สามารถใช้เพื่อทำความเข้าใจเจตนาของรหัสได้จากนั้นสร้างโมดูลที่ปรับให้เหมาะสมที่สุดควบคู่ไปกับสิ่งนี้ซึ่งประกอบด้วยรุ่นหรือรุ่นที่เหมาะสมที่สุด รุ่นที่ได้รับการปรับให้เหมาะสมที่สุดนั้นแตกต่างจากสายพื้นฐาน เมื่อคุณรันการทดสอบ (หน่วยและการรวม) คุณจะรันในเวอร์ชันที่ไม่ได้เพิ่มประสิทธิภาพและในโมดูลที่ปรับให้เหมาะสมปัจจุบันทั้งหมด

ตัวอย่าง

ตัวอย่างเช่นสมมติว่าคุณมีฟังก์ชั่นการแปลงฟูริเยร์ที่รวดเร็ว บางทีคุณอาจมีพื้นฐานการดำเนินงานในอัลกอริทึมและการทดสอบในfft.cfft_tests.c

แล้วก็ไปตามมา Pentium และคุณตัดสินใจที่จะใช้รุ่นจุดคงที่ในfft_mmx.cการใช้คำแนะนำ MMX ต่อมา Pentium 3 มาพร้อมและคุณตัดสินใจที่จะเพิ่มรุ่นที่ใช้Streaming SIMD ส่วนขยายfft_sse.cใน

ตอนนี้คุณต้องการเพิ่มCUDAดังนั้นคุณเพิ่มfft_cuda.cแต่พบว่าด้วยชุดข้อมูลทดสอบที่คุณใช้มานานหลายปีรุ่น CUDA จะช้ากว่ารุ่น SSE! คุณทำการวิเคราะห์และจบลงด้วยการเพิ่มชุดข้อมูลที่ใหญ่กว่า 100 เท่าและคุณจะได้ความเร็วที่คุณคาดหวัง แต่ตอนนี้คุณรู้แล้วว่าเวลาตั้งค่าสำหรับการใช้รุ่น CUDA นั้นมีความสำคัญและชุดข้อมูลขนาดเล็กคุณควรใช้ อัลกอริทึมที่ไม่ต้องเสียค่าติดตั้ง

ในแต่ละกรณีเหล่านี้คุณกำลังใช้อัลกอริทึมเดียวกันทั้งหมดควรทำงานในลักษณะเดียวกัน แต่จะทำงานด้วยประสิทธิภาพและความเร็วที่แตกต่างกันในสถาปัตยกรรมที่แตกต่างกัน (หากพวกเขาจะทำงานเลย) จากมุมมองโค้ดคุณสามารถเปรียบเทียบคู่ซอร์สไฟล์ใด ๆ เพื่อค้นหาว่าทำไมอินเทอร์เฟซเดียวกันจึงถูกนำไปใช้งานในรูปแบบที่แตกต่างกันและโดยทั่วไปวิธีที่ง่ายที่สุดคือการอ้างอิงกลับไปยังเวอร์ชันที่ไม่มีการกำหนดเวลาดั้งเดิม

เหมือนกันทั้งหมดสำหรับการใช้งาน OOP ที่คลาสพื้นฐานซึ่งใช้อัลกอริทึมที่ไม่ได้เพิ่มประสิทธิภาพและคลาสที่ได้รับจะใช้การเพิ่มประสิทธิภาพที่แตกต่างกัน

สิ่งที่สำคัญคือเพื่อให้สิ่งเดียวที่เหมือนกันเพื่อให้ความแตกต่างที่เห็นได้ชัด


7

โดยเฉพาะอย่างยิ่งเนื่องจากคุณได้รับตัวอย่างของการประมวลผลวิดีโอและภาพหนึ่งสามารถเก็บรหัสเป็นส่วนหนึ่งของรุ่นเดียวกัน แต่ใช้งานหรือไม่ใช้งานขึ้นอยู่กับบริบท

ในขณะที่คุณไม่ได้พูดถึงฉันกำลังสันนิษฐานCที่นี่

วิธีที่ง่ายที่สุดในการเขียนCโค้ดก็คือการเพิ่มประสิทธิภาพ (และยังใช้เมื่อพยายามทำให้อุปกรณ์พกพา) คือการรักษา

 
#ifdef OPTIMIZATION_XYZ_ENABLE 
   // your optimzied code here... 
#else  
   // your basic code here...

เมื่อคุณเปิดใช้งาน#define OPTIMIZATION_XYZ_ENABLEระหว่างการคอมไพล์ใน Makefile ทุกอย่างจะทำงานอย่างสอดคล้องกัน

โดยปกติแล้วการตัดโค้ดสองสามบรรทัดที่อยู่ตรงกลางฟังก์ชั่นอาจกลายเป็นความยุ่งเหยิงเมื่อมีฟังก์ชั่นมากเกินไปที่ได้รับการปรับให้เหมาะสม ดังนั้นในกรณีนี้หนึ่งกำหนดตัวชี้ฟังก์ชั่นที่แตกต่างกันเพื่อดำเนินการฟังก์ชั่นที่เฉพาะเจาะจง

รหัสหลักจะดำเนินการผ่านตัวชี้ฟังก์ชั่นเช่น


   codec->computed_idct(blocks); 

แต่พอยน์เตอร์ของฟังก์ชั่นจะถูกกำหนดขึ้นอยู่กับประเภทของตัวอย่าง (เช่นที่นี่ฟังก์ชั่น idct ถูกปรับให้เหมาะสมกับสถาปัตยกรรม CPU ที่แตกต่างกัน



if(OPTIMIZE_X86) {
  codec->computed_idct = compute_idct_x86; 
}
else if(OPTIMZE_ARM) {
  codec->computed_idct = compute_idct_ARM;
}
else {
  codec->computed_idct = compute_idct_C; 
}

คุณควรเห็นรหัสlibjpegและรหัสlibmpeg2และอาจเป็นffmpegสำหรับเทคนิคดังกล่าว


6

ในฐานะนักวิจัยฉันจบการเขียนโค้ด "คอขวด" สักหน่อย อย่างไรก็ตามเมื่อนำมาผลิตจริงแล้วความรับผิดชอบในการรวมเข้ากับผลิตภัณฑ์และการให้การสนับสนุนที่ตามมาจะตกอยู่กับผู้พัฒนา อย่างที่คุณสามารถจินตนาการได้การสื่อสารอย่างชัดเจนว่าอะไรคือสิ่งที่โปรแกรมควรจะทำงานมีความสำคัญสูงสุด

ฉันพบว่ามีสามส่วนผสมที่สำคัญในการทำขั้นตอนนี้ให้สำเร็จ

  1. อัลกอริทึมที่ใช้ต้องชัดเจนอย่างยิ่ง
  2. วัตถุประสงค์ของทุกสายงานต้องชัดเจน
  3. การเบี่ยงเบนจากผลลัพธ์ที่คาดหวังจะต้องระบุโดยเร็วที่สุด

สำหรับขั้นตอนแรกฉันมักจะเขียนสมุดปกขาวสั้น ๆซึ่งเป็นเอกสารของอัลกอริทึม จุดมุ่งหมายที่นี่คือการเขียนขึ้นจริงเพื่อให้บุคคลอื่นสามารถนำไปใช้ตั้งแต่เริ่มต้นโดยใช้เฉพาะกระดาษขาว ถ้ามันเป็นอัลกอริธึมที่รู้จักกันดีเผยแพร่ก็เพียงพอที่จะให้การอ้างอิงและทำซ้ำสมการ หากเป็นงานดั้งเดิมคุณจะต้องมีความชัดเจนมากขึ้น นี้จะบอกคุณสิ่งรหัสควรจะทำอย่างไร

การใช้งานจริงที่ส่งมอบให้กับการพัฒนาจะต้องจัดทำเป็นเอกสารในลักษณะที่แสดงรายละเอียดปลีกย่อยทั้งหมดอย่างชัดเจน หากคุณได้รับการล็อคตามลำดับเฉพาะเพื่อหลีกเลี่ยงการหยุดชะงักให้เพิ่มความคิดเห็น หากคุณวนซ้ำคอลัมน์แทนที่จะเป็นแถวของเมทริกซ์เนื่องจากปัญหาการเชื่อมโยงกันของแคชให้เพิ่มความคิดเห็น หากคุณทำอะไรที่ฉลาดแม้แต่น้อยก็แสดงความคิดเห็น หากคุณสามารถรับประกันกระดาษขาวและรหัสจะไม่ถูกแยกออก (ผ่าน VCS หรือระบบที่คล้ายกัน) คุณสามารถอ้างอิงกลับไปที่กระดาษแข็งสีขาว ผลลัพธ์สามารถแสดงความคิดเห็นได้มากกว่า 50% ไม่เป็นไร. นี่จะบอกคุณว่าทำไมรหัสทำในสิ่งที่มันทำ

ในที่สุดคุณจะต้องสามารถรับประกันความถูกต้องเมื่อเผชิญกับการเปลี่ยนแปลง โชคดีที่เราเป็นเครื่องมือที่มีประโยชน์ในการทดสอบอัตโนมัติและแพลตฟอร์มการรวมอย่างต่อเนื่อง สิ่งเหล่านี้จะบอกคุณว่ารหัสกำลังทำอะไรอยู่

คำแนะนำที่น่ายินดีที่สุดของฉันคือจะไม่ทำตามขั้นตอนใด ๆ คุณจะต้องการพวกเขาในภายหลัง;)


ขอบคุณสำหรับคำตอบที่ครอบคลุมของคุณ ฉันเห็นด้วยกับคะแนนทั้งหมดของคุณ ในแง่ของการทดสอบอัตโนมัติฉันพบว่าการครอบคลุมช่วงตัวเลขของเลขคณิตจุดคงที่และรหัส SIMD อย่างเพียงพอนั้นเป็นเรื่องยาก เงื่อนไขที่ระบุไว้ในความคิดเห็นเท่านั้น (โดยไม่ต้องมีรหัสเพื่อเสริมกำลัง) ไม่ได้พบกันเสมอ

เหตุผลที่ฉันยังไม่ยอมรับคำตอบของคุณคือเพราะฉันต้องการคำแนะนำเพิ่มเติมเกี่ยวกับความหมายของ "กระดาษสีขาวสั้น ๆ " และความพยายามในการสร้างมันขึ้นมา สำหรับบางอุตสาหกรรมนี่เป็นส่วนหนึ่งของสายธุรกิจหลัก แต่ในอุตสาหกรรมอื่น ๆ จะต้องพิจารณาค่าใช้จ่ายและควรใช้ทางลัดที่ถูกต้องตามกฎหมาย

ก่อนอื่นฉันรู้สึกถึงความเจ็บปวดของคุณเกี่ยวกับการทดสอบอัตโนมัติเลขทศนิยมและเลขขนาน ฉันเกรงว่าจะไม่มีวิธีแก้ปัญหาที่ใช้ได้สำหรับทุกกรณี โดยปกติแล้วฉันจะทำงานร่วมกับการยอมรับอย่างเป็นธรรม แต่ในอุตสาหกรรมของคุณที่อาจไม่สามารถทำได้
drxzcl

2
ในทางปฏิบัติ whitepaper มักจะดูเหมือนร่างแรกของบทความทางวิทยาศาสตร์โดยไม่มีส่วน "ปุย" (ไม่มีการแนะนำที่มีความหมายไม่มีนามธรรมสรุป / อภิปรายน้อยที่สุดและมีเพียงการอ้างอิงที่จำเป็นต้องเข้าใจเท่านั้น) ฉันเห็นการเขียนรายงานเป็นส่วนหนึ่งของการพัฒนาอัลกอริทึมและ / หรือการเลือกอัลกอริทึม คุณเลือกที่จะใช้อัลกอริทึมนี้ (พูดสเปกตรัม FFT) มันคืออะไรกันแน่? ทำไมคุณถึงเลือกอันนี้มากกว่าตัวอื่น? อะไรคือลักษณะของการขนาน ความพยายามควรเป็นสัดส่วนกับงานคัดเลือก / พัฒนา
drxzcl

5

ฉันเชื่อว่าสิ่งนี้จะได้รับการแก้ไขที่ดีที่สุดผ่านการแสดงความคิดเห็นที่ครอบคลุมของรหัสจนถึงจุดที่แต่ละบล็อกที่สำคัญของรหัสได้อธิบายการแสดงความคิดเห็นล่วงหน้า

ความคิดเห็นควรรวมการอ้างอิงถึงข้อกำหนดหรือวัสดุอ้างอิงฮาร์ดแวร์

ใช้คำศัพท์และอัลกอริธึมทั่วทั้งอุตสาหกรรมตามความเหมาะสม - เช่น 'สถาปัตยกรรม X สร้างกับดัก CPU สำหรับการอ่านที่ไม่ได้จัดแนวดังนั้นอุปกรณ์ของดัฟฟ์นี้จะเต็มไปยังขอบเขตการจัดตำแหน่งถัดไป'

ฉันจะใช้การตั้งชื่อตัวแปรในหน้าของคุณเพื่อให้แน่ใจว่าไม่มีความเข้าใจผิดว่าเกิดอะไรขึ้น ไม่ใช่ฮังการี แต่เป็น 'ก้าวย่าง' เพื่ออธิบายระยะห่างระหว่างพิกเซลแนวตั้งสองพิกเซล

ฉันจะเสริมด้วยเอกสารสั้น ๆ ที่มนุษย์สามารถอ่านได้ซึ่งมีไดอะแกรมระดับสูงและการออกแบบบล็อก


1
การใช้คำศัพท์ที่สอดคล้องกันเพียงข้อเดียวสำหรับสิ่งเดียว (เช่นการใช้ "ก้าวย่าง" เหนือข้อกำหนดที่มีความหมายคล้ายกันเช่น "ขั้นตอน", "การจัดตำแหน่ง") ในโครงการเดียวกันจะช่วยได้ สิ่งนี้ค่อนข้างยากเมื่อรวมฐานรหัสของหลายโครงการเข้ากับโครงการเดียว
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.