เมื่อใดควรปรับปรุงซอฟต์แวร์ให้เหมาะสมเพื่อประสิทธิภาพที่ดีขึ้นตั้งแต่เริ่มต้นหรือสิ้นสุดการพัฒนา


19

ฉันเป็นนักพัฒนาซอฟต์แวร์รุ่นเยาว์และฉันสงสัยว่าเมื่อใดจะเป็นเวลาที่ดีที่สุดในการปรับแต่งซอฟต์แวร์เพื่อประสิทธิภาพที่ดีขึ้น (ความเร็ว)

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


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

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

8
ผลที่ตามมา: มันเป็น / หรือการตัดสินใจหรือเป็นสิ่งสำคัญที่จะต้องทำการตัดสินใจเกี่ยวกับประสิทธิภาพก่อนเวลาในขณะที่การชะลอผู้อื่น?
Robert Harvey

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

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

คำตอบ:


52

สิ่งหนึ่งที่ควรอ่านตลอดเวลาและตลอดไป ถ้ามันช้า แต่อ่านได้ฉันสามารถแก้ไขได้ ถ้ามันพัง แต่อ่านได้ฉันสามารถแก้ไขได้ ถ้ามันอ่านไม่ได้ฉันต้องถามคนอื่นว่าสิ่งนี้ควรทำอย่างไร

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

มีเพียงสองสิ่งเท่านั้นที่พาฉันออกจากโหมดนี้:

  1. เมื่อฉันเห็นโอกาสในการพัฒนาO ที่ยิ่งใหญ่เต็มรูปแบบแม้กระทั่งเมื่อnนั้นใหญ่พอที่ทุกคนจะสนใจ
  2. เมื่อฉันมีการทดสอบที่แสดงปัญหาประสิทธิภาพจริง แม้จะมีประสบการณ์หลายสิบปีฉันยังคงไว้วางใจการทดสอบมากกว่าคณิตศาสตร์ของฉัน และฉันเก่งคณิตศาสตร์

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


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

12
@PieterB: มันเป็นอย่างน่าทึ่งเรื่องง่ายที่จะพัฒนาช้าลงโดยกลยุทธ์เช่น"ข้อบกพร่องและการออกแบบที่ไม่ดีสามารถได้รับการแก้ไขในภายหลัง" หมายเหตุด้วยการออกแบบที่ไม่ดีฉันหมายถึงสิ่งต่าง ๆ เช่นโค้ดที่ไม่สามารถอ่านได้ที่ซับซ้อนเช่นเดียวกับโค้ดที่ overengineered
Doc Brown

5
@ Walrat: ฉันคิดว่าตัวอย่างของคุณสามารถเร่งได้ง่ายโดยไม่ต้องเสียสละความสามารถในการอ่านและฉันตีความคำตอบนี้ไม่ใช่ "รหัสที่อ่านได้ไม่มีปัญหาประสิทธิภาพการทำงาน" แต่ปัญหา "perfomance จะไม่ถูกหลีกเลี่ยงโดยอัตโนมัติด้วยการสร้างโค้ด อ่านไม่ได้"
Doc Brown

2
@PieterB: หรือคุณมีลูกค้าที่ต้องการรับเงินคืนเพราะสินค้าที่พวกเขาซื้อนั้นเป็นรถที่ไม่สามารถใช้งานได้
Doc Brown

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

27

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

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

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

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


15

เมื่อใดจะเป็นเวลาที่ดีที่สุดในการปรับแต่งซอฟต์แวร์เพื่อประสิทธิภาพที่ดีขึ้น (ความเร็ว)

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

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

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

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

สมมติว่าซอฟต์แวร์มีขนาดไม่ใหญ่มากและซับซ้อนในการจัดการ

นั่นเป็นสมมติฐานที่แย่มาก

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

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

คุณกำลังนั่งอยู่ที่หน้าว่างและคุณเขียนvoid main() {}คุณเริ่มปรับให้เหมาะสมหรือไม่? ไม่มีอะไรที่จะเพิ่มประสิทธิภาพ! ลำดับที่ถูกต้องคือ:

  • ทำให้มันรวบรวม
  • ทำให้ถูกต้อง
  • ทำให้สง่า
  • ทำให้มันเร็ว

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

แต่มีขั้นตอนที่หายไปมี จริงลำดับที่ถูกต้องคือ

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

"ประสิทธิภาพเป็นสิ่งที่ผู้ใช้เชื่อว่ามีประสิทธิภาพ" - แน่นอนบางครั้งประสบการณ์ผู้ใช้จะดีขึ้นจริงเมื่อสิ่งที่เราคาดหวังว่าจะใช้เวลาต้องใช้เวลา: webdesignerdepot.com/2017/09/when-slower-ux-is-better-ux
svidgen

อาจจะ "เป็นวิทยาศาสตร์" มากกว่า "ใช้วิทยาศาสตร์" :)
สีน้ำเงิน

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

2
@Giorgio: วันนี้ฉัน แต่ฉันจำได้เมื่อฉันได้รับฮาร์ดไดรฟ์ครั้งแรกและฉันจะบันทึกเกมหรือเอกสารและคิดว่ามีบางอย่างผิดพลาดเพราะการดำเนินการใช้เวลาไม่เข้าใจเมื่อเทียบกับการบันทึกแผ่นฟลอปปี้ และแน่นอนตอนนี้สถานะของเกมและเอกสารมีขนาดใหญ่มากจนเรากลับมาประหยัดเวลาได้
Eric Lippert

3

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

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

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

User user = getUser();
int totalAmount;
for (Order o : user.getOrders()) {
  totalAmount += o.getTotalAmount();
} 

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

จำนวนการสืบค้น SQL ที่นี่อาจทำให้คุณประหลาดใจ แน่นอนว่ามันขึ้นอยู่กับโครงสร้างของเอนทิตีของคุณ

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


3

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

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

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

แล้วคุณจะทำอย่างไร? บางสิ่ง

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

  2. ทำซ้ำ ใช้การวัดประสิทธิภาพพื้นฐานเมื่อระบบค่อนข้างใหม่และทำการทดสอบซ้ำเป็นครั้งคราวเพื่อให้แน่ใจว่าโค้ดที่แนะนำใหม่ไม่ได้ลดประสิทธิภาพลงมากเกินไป

  3. อย่า overoptimize เร็วเกินไป คุณไม่เคยรู้ว่าสิ่งใดจะสำคัญและอะไรจะไม่สำคัญ อัลกอริทึมการแยกวิเคราะห์สตริงที่เร็วมากอาจไม่ช่วยถ้าโปรแกรมของคุณกำลังรอ I / O อยู่ตลอดเวลา

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

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

ส่วนใหญ่ของกิจกรรมเหล่านี้ไม่ได้สำหรับจุดเริ่มต้นหรือจุดสิ้นสุดของโครงการ แต่ต้องเข้าร่วมอย่างต่อเนื่อง


1

ฉันเป็นนักพัฒนาซอฟต์แวร์รุ่นเยาว์และฉันสงสัยว่าเมื่อใดจะเป็นเวลาที่ดีที่สุดในการปรับแต่งซอฟต์แวร์เพื่อประสิทธิภาพที่ดีขึ้น (ความเร็ว)

เข้าใจว่ามีสองขั้วที่แตกต่างกันมาก

สุดขีดแรกคือสิ่งที่มีผลต่อการออกแบบส่วนใหญ่เช่นวิธีแบ่งงานออกเป็นกี่โพรเซสและ / หรือเธรดและมีการสื่อสารกันกี่ชิ้น (ซ็อกเก็ต TCP / IP? การเรียกใช้ฟังก์ชันโดยตรงหรือไม่) หรือล่าม "หนึ่ง opcode ในเวลาเดียว" หรือว่าจะวางแผนโครงสร้างข้อมูลเพื่อให้คล้อยตาม SIMD หรือ ... สิ่งเหล่านี้มีแนวโน้มที่จะมีอิทธิพลอย่างมากต่อการใช้งานและกลายเป็นเรื่องยาก / แพงเกินกว่าจะพอดี

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

ในระหว่างสุดขั้วเหล่านี้เป็นพื้นที่สีเทาขนาดใหญ่

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

น่าเสียดายที่เราอาศัยอยู่ในโลกที่มีคนจำนวนมากเกินไปที่คิดว่าการปรับให้เหมาะสมจะรวมเฉพาะสิ่งที่ไม่สำคัญส่วนใหญ่ที่ "ไม่สำคัญ"


1

เป็นการง่ายที่สุดในการเขียนโค้ดที่ไม่มี porformant และไม่สามารถบำรุงรักษาได้ มันยากที่จะเขียนรหัส porformant มันยากที่จะเขียนโค้ดที่สามารถบำรุงรักษาได้ และมันเป็นสิ่งที่ยากที่สุดในการเขียนโค้ดที่สามารถบำรุงรักษาได้และมีประสิทธิภาพ

แต่มันง่ายกว่าที่จะทำให้รหัสที่สามารถบำรุงรักษาได้ดีกว่าที่จะทำให้รหัสที่สามารถบำรุงรักษาได้

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

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

จากนั้นให้มุ่งเน้นไปที่การทำให้รหัสของคุณสามารถบำรุงรักษาได้ โดยสิ่งนี้ฉันหมายถึง:

  1. แยกข้อกังวลออกให้เข้มงวดที่สุดเท่าที่จะทำได้ (ตัวอย่างเช่นอย่าผสมผสานการเข้าถึงข้อมูลกับตรรกะทางธุรกิจ)
  2. การอ้างอิงประเภทนามธรรมแทนประเภทคอนกรีตที่เป็นไปได้
  3. ทำให้รหัสของคุณทดสอบได้

รหัสที่รักษาได้นั้นง่ายต่อการปรับให้เหมาะสมเมื่อสถิติแสดงให้เห็นว่าเป็นสิ่งจำเป็น

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

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

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


1

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

กรณีที่แปลกประหลาด

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

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

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

ดึกมากที่สุด

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

การออกแบบที่เสนอห้องหายใจเพื่อปรับให้เหมาะสมในภายหลัง

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

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

ป้อนคำอธิบายรูปภาพที่นี่

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

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

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

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

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


0

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

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

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


0

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

ประสิทธิภาพยังใช้งานง่าย ผู้ใช้บางคนค่อนข้างมีการกดแป้นพิมพ์ 1 ครั้งใน 10 วินาทีมากกว่าการกดแป้น 2 ครั้งใน 1 วินาที สำหรับสิ่งเช่นนั้นขอให้คุณนำการออกแบบ ฉันไม่ชอบที่จะทำอะไรแบบนี้กับผู้ใช้ แต่เนิ่นๆ ในสุญญากาศพวกเขาอาจบอกว่า X แต่เมื่อพวกเขาทำงานกับ pre-release พวกเขาอาจพูดว่า Y

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

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

เมื่อเลือกประเภทของคอลเล็กชันให้พิจารณาถึงหน้าที่ความเร็วและขนาด

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

คุณวนซ้ำสามครั้งเมื่อคุณวนซ้ำหนึ่งครั้งและทำสามสิ่ง

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

ประสิทธิภาพการทำงานจำนวนมากยังเป็นรหัสที่สะอาด

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

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

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