เรียนรู้ที่จะเพิ่มประสิทธิภาพด้วยการชุมนุม [ปิด]


21

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

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

ฉันหวังว่าจะมีหนังสือดีๆเล่มหนึ่งออกมาฉันไม่สามารถหา ...


1
สิ่งนี้ไม่ได้ตอบคำถามของคุณโดยตรง แต่เป็นการสำรวจ (เรียกว่าแบบปรับได้) A * ได้รับการตรวจสอบและมีประสิทธิภาพที่ดีจริงๆ (หมายความว่าคุณไม่จำเป็นต้องปรับให้เหมาะสมโดยใช้ ASM) มีลักษณะที่D * Lite
Jonathan Dickinson

คำตอบ:


21

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

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

คำตอบ

นี่เป็นหนึ่งในแหล่งข้อมูลที่ดีที่สุดที่ฉันได้พบสำหรับการเรียนรู้เกี่ยวกับการปรับให้เหมาะสม

http://www.agner.org/optimize/

การคุยโว

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

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

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

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

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

  1. การเพิ่มล่วงหน้าก่อนเว้นแต่ว่าคุณต้องการการเพิ่มภายหลังจริงๆ
  2. การเขียนลูปสำหรับคอนเทนเนอร์โดยใช้ตัวแปรขนาดโลคัลคงที่แทนที่จะเรียกขนาด () บนคอนเทนเนอร์ภายในลูป

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


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

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

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

7
ดังนั้น -1 สำหรับ A: ไม่ตอบคำถามเกี่ยวกับวิธีเขียนรูทีนที่เหมาะที่สุดสำหรับแอสเซมบลี B: การบิดเบือนความจริงในการเรียนรู้วิธีเอาชนะคอมไพเลอร์ในการเขียนรูทีนที่ปรับให้เหมาะสมที่สุด และ C: สนับสนุนให้โปรแกรมเมอร์ดูที่การปรับแต่งระดับแอสเซมบลีก่อนที่จะปรับให้เหมาะสมในระดับอัลกอริทึม แม้แต่ผู้ที่ "ผู้เชี่ยวชาญในอุตสาหกรรม" ที่ได้รับค่าจ้างสูงก็จะบอกคุณว่านั่นคือการเอาเกวียนมาก่อนม้า
Nicol Bolas

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

22

เคล็ดลับแรกที่คุณจะได้รับคือ - ไม่

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

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

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

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


15

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

ต้องบอกว่าฉันเพิ่งเกิดขึ้นกับสิ่งนี้ซึ่งอาจเป็นเส้นทางที่ดีต่อการเรียนรู้ x86 ASM โดยเฉพาะสำหรับนักพัฒนาเกม


ภาคผนวก

มีสองแหล่งที่มาจากส่วนหัวของฉัน:

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

  • การลดการใช้การดำเนินการที่มีค่าใช้จ่ายสูงที่สุด (div, SQRT, trig ops และ conditionals เป็นหลัก);
  • การปรับปรุงประสิทธิภาพแคชผ่านการใช้โครงสร้างข้อมูลที่มีประสิทธิภาพมากขึ้นการจัดตำแหน่งหน่วยความจำและเงื่อนไขที่ลดลง
  • การลดคุณภาพของผลผลิตในพื้นที่ที่ยอมรับได้เพื่อปรับปรุงประสิทธิภาพ;
  • Vectorisation (SIMD);
  • Parallelisation (เธรดรวมถึงการเลื่อนงานออกไปเป็น GPU);
  • และแน่นอน (เพิ่มมากขึ้นเรื่อย ๆ ) แอสเซมบลีที่เข้ารหัสด้วยมือ ขั้นแรกให้ตรวจสอบชุดประกอบ C / C ++ เพื่อดูว่าตัวแปลภาษานั้นสร้างตัวเลือกใดที่ไม่เหมาะสมที่สุด คุณจะพบสิ่งนี้มากขึ้นในเอกสารเก่าจากยุค 80 และยุค 90, IME

การวิจัยการอ่านยังช่วยให้คุณอยู่ในระดับที่ทันสมัยในสาขาของคุณแทนที่จะรอความรู้นั้นในการกรองอุตสาหกรรม


คุณพูดถึงการเพิ่มประสิทธิภาพอัลกอริทึม แต่คุณไม่ได้ให้ข้อมูลหากเราต้องทำตามคำแนะนำของคุณและดูว่าคุณสามารถให้ทิศทางได้หรือไม่
Skeith

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

@NickWiggill คุณมีเอกสารงานวิจัยตามปกติคืออะไร?
kizzx2

3

ฉันคิดว่ามันอาจเร็วเกินไป

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

สำหรับการเริ่มต้นอย่างน้อยมุ่งเน้นที่การเพิ่มประสิทธิภาพที่ปราศจากการชุมนุม Igor Ostrovsky มีบทความดีๆที่แสดงให้เห็นถึงพื้นฐานบางอย่าง: http://igoro.com/archive/fast-and-slow-if-statements-branch-prediction-in-modern-processors/

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

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


2

หนังสือเล่มนี้ดีมากสำหรับหนังสือเรียน แต่มันไม่ได้มุ่งเน้นไปที่การเพิ่มประสิทธิภาพโดยเฉพาะ ภาษาแอสเซมบลีสำหรับโปรเซสเซอร์ x86 รุ่นที่ 6

มันเพิ่มเติมเกี่ยวกับการสอนพื้นฐานของการชุมนุมโดยใช้ MASM จากนั้นในตอนท้ายของหนังสือเล่มนี้มันจะกลายเป็นวิธีการประกอบอินไลน์ด้วย c ++ และรวมเข้ากับโปรแกรมที่ใหญ่กว่า

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

ฉันชอบหนังสือเล่มนี้เพราะเออร์ไวน์สอนวิธีใช้เครื่องมือที่จำเป็นในการเขียนโปรแกรม masm เขาใช้วิธีการใช้ IDE (Visual Studio C ++) และดีบักเกอร์โดยเฉพาะ แต่ละบทมีวิดีโอเพียงไม่กี่รายการที่มุ่งเน้นการแก้ปัญหา ข้อมูลบางส่วนนี้สามารถใช้ได้อย่างอิสระบนเว็บไซต์ที่ระบุไว้


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