แทรกการบรรยายก่อนเวลาอันควรคือรากเหง้าแห่งความชั่วร้ายทั้งหมด
ที่กล่าวว่านี่คือนิสัยบางอย่างที่ฉันได้รับเพื่อหลีกเลี่ยงประสิทธิภาพที่ไม่จำเป็นและในบางกรณีทำให้รหัสของฉันง่ายขึ้นและถูกต้องมากขึ้นเช่นกัน
นี่ไม่ใช่การอภิปรายเกี่ยวกับหลักการทั่วไป แต่มีบางสิ่งที่ต้องระวังเพื่อหลีกเลี่ยงการแนะนำความไม่จำเป็นลงในโค้ด
รู้ว่าใหญ่ของคุณ
สิ่งนี้อาจจะถูกรวมเข้ากับการสนทนาที่มีความยาวด้านบน เป็นเรื่องธรรมดามากที่การวนรอบด้านในของวงวนที่วงด้านในทำการคำนวณซ้ำจะช้าลง ตัวอย่างเช่น:
for (i = 0; i < strlen(str); i++) {
...
}
การทำเช่นนี้จะใช้เวลานานมากหากสตริงมีความยาวจริง ๆ เนื่องจากความยาวจะถูกคำนวณใหม่ในทุกการวนซ้ำของลูป โปรดทราบว่าGCCเพิ่มประสิทธิภาพกรณีนี้จริงเพราะstrlen()
ถูกทำเครื่องหมายเป็นฟังก์ชั่นบริสุทธิ์
เมื่อเรียงลำดับล้านจำนวนเต็ม 32 บิตฟองเรียงลำดับจะเป็นวิธีที่ผิดไป โดยทั่วไปการเรียงลำดับสามารถทำได้ในเวลา O (n * log n) (หรือดีกว่าในกรณีของการจัดเรียง radix) ดังนั้นถ้าคุณรู้ว่าข้อมูลของคุณจะมีขนาดเล็กให้มองหาอัลกอริทึมที่อย่างน้อย O (n * log n)
ในทำนองเดียวกันเมื่อต้องจัดการกับฐานข้อมูลระวังดัชนี หากคุณSELECT * FROM people WHERE age = 20
และคุณไม่มีดัชนีของบุคคล (อายุ) มันจะต้องมีการสแกนตามลำดับ O (n) มากกว่าการสแกนดัชนี O (log n) ที่เร็วกว่ามาก
ลำดับชั้นทางคณิตศาสตร์จำนวนเต็ม
เมื่อเขียนโปรแกรมใน C โปรดทราบว่าการดำเนินการทางคณิตศาสตร์บางอย่างมีราคาแพงกว่าการดำเนินการอื่น สำหรับจำนวนเต็มลำดับชั้นจะเป็นดังนี้ (อย่างน้อยแพงก่อน):
จริงอยู่ที่คอมไพเลอร์จะสิ่งที่มักจะเพิ่มประสิทธิภาพเช่นn / 2
การn >> 1
โดยอัตโนมัติหากคุณกำลังกำหนดเป้าหมายคอมพิวเตอร์หลัก แต่ถ้าคุณกำหนดเป้าหมายอุปกรณ์ฝังตัวที่คุณอาจไม่ได้รับความหรูหราที่
นอกจากนี้% 2
และ& 1
มีความหมายที่แตกต่างกัน การหารและโมดูลัสมักจะปัดเศษเป็นศูนย์ แต่จะมีการกำหนดการใช้งาน ดีเฒ่า>>
และ&
รอบไปทางลบอนันต์ซึ่ง (ในความคิดของฉัน) ทำให้รู้สึกมากขึ้น ตัวอย่างเช่นบนคอมพิวเตอร์ของฉัน:
printf("%d\n", -1 % 2); // -1 (maybe)
printf("%d\n", -1 & 1); // 1
ดังนั้นใช้สิ่งที่เหมาะสม อย่าคิดว่าคุณเป็นเด็กดีโดยใช้เมื่อคุณได้เดิมจะเขียน% 2
& 1
การดำเนินการจุดลอยตัวที่แพง
หลีกเลี่ยงการดำเนินการจุดลอยตัวที่หนักหน่วงเช่นpow()
และlog()
ในรหัสที่ไม่ต้องการโดยเฉพาะอย่างยิ่งเมื่อต้องจัดการกับจำนวนเต็ม ยกตัวอย่างเช่นการอ่านตัวเลข:
int parseInt(const char *str)
{
const char *p;
int digits;
int number;
int position;
// Count the number of digits
for (p = str; isdigit(*p); p++)
{}
digits = p - str;
// Sum the digits, multiplying them by their respective power of 10.
number = 0;
position = digits - 1;
for (p = str; isdigit(*p); p++, position--)
number += (*p - '0') * pow(10, position);
return number;
}
ไม่เพียง แต่การใช้pow()
(และการแปลงint
<-> ที่double
จำเป็นต้องใช้) ค่อนข้างแพง แต่มันสร้างโอกาสในการสูญเสียความแม่นยำ (โดยบังเอิญรหัสข้างต้นไม่มีปัญหาความแม่นยำ) นั่นเป็นเหตุผลที่ฉันสะดุ้งเมื่อฉันเห็นฟังก์ชั่นประเภทนี้ที่ใช้ในบริบทที่ไม่ใช่ทางคณิตศาสตร์
นอกจากนี้ให้สังเกตว่าอัลกอริทึม "ฉลาด" ด้านล่างซึ่งคูณด้วย 10 ในการทำซ้ำแต่ละครั้งนั้นจริง ๆ แล้วรัดกุมกว่ารหัสด้านบน:
int parseInt(const char *str)
{
const char *p;
int number;
number = 0;
for (p = str; isdigit(*p); p++) {
number *= 10;
number += *p - '0';
}
return number;
}