Table of Contents

הבנת יעילות האלגוריתם היא יסוד לפתח תוכנה ביצועים גבוהים ב C ו- C++. בין אם אתה בונה מערכות בזמן אמת, מנועי משחק, יישומים פיננסיים או תוכנה מוטבעת, היכולת לנתח ולייעל אלגוריתמים יכולה להיות ההבדל בין תוכנה העומדת בדרישות ביצועים ותוכנה נופלת קצר.מדריך מקיף זה חוקר את היסודות התיאורטיים של יעילות תוך מתן טכניקות מעשיות ואסטרטגיות בעולם האמיתי עבור אופטימיזציה קוד ב- C ו- C.

מה זה חוסר אחריות ולמה זה משנה?

יעילות Algorithm מודדת כיצד השימוש במשרה או במשאב של אלגוריתם בקנה מידה כמו גודל קלט גדל. ב C ו C++, שבו מפתחים לעתים קרובות לעבוד קרוב לחומרה, יעילות הבנה הופכת אפילו יותר קריטית.שפות אלה מספקות שליטה על זיכרון וביצוע, מה שהופך אותם אידיאליים עבור יישומים קריטיים ביצועים, אבל גם הצבת אחריות גדולה יותר על מפתחים לכתוב קוד יעיל.

החשיבות של יעילות אלגוריתמית משתרעת מעבר לתרגילים אקדמיים.בסביבות הייצור, אלגוריתמים לא יעילים יכולים להוביל עלויות שרת מוגברת, ניסיון משתמש גרוע, ניקוז סוללות על מכשירים ניידים, וחוסר יכולת לעבד נתונים בתוך מגבלות זמן נדרשות.אלגוריתם שנבחר גרוע עשוי לעבוד בסדר עם נתונים קטנים במהלך התפתחות אך נכשל כאשר הוא פרוס עם כרכים נתונים אמיתיים.

יישומים מודרניים לעתים קרובות מעבדים כמויות עצומות של נתונים, מניתוח וידאו הזרמת ל- genomic ריצוף ניתוח שוק פיננסי. אלגוריתם עם מורכבות זמן קוואדרטית עשוי להשלים בתוך מילימטרים עם 100 נקודות נתונים אבל לוקח שעות עם 10,000 נקודות.הבנת המאפיינים הגדלים האלה מאפשר למפתחים לקבל החלטות מושכלות על בחירת אלגוריתם ואסטרטגיות יישום.

מושגי יסוד של אלגוריתאם

יעילות Algorithm כוללת מספר מדדים מרכזיים המסייעים למפתחים להבין ולנבא כיצד הקוד יבצע בתנאים שונים.שני הממדים העיקריים של יעילות הם מורכבות זמן ומורכבות חלל, שניהם משחקים תפקידים מכריעים בפיתוח C ו- C++.

זמן מורכב: מהירות ההוצאה להורג

מורכבות הזמן מתארת כיצד מספר הפעולות שאלגוריתם מבצע גדל יחסית לגודל קלט. במקום למדוד זמן ביצוע בפועל בתוך שניות או מילימטרים, אשר משתנה בהתאם לפרטים חומרה וביצוע, מורכבות הזמן מספקת מידה של יעילות אלגוריתמית.

שיעורי מורכבות הזמן המשותף כוללים זמן קבוע O(1), זמן לונארית O(log n), זמן ליניארי O(n), זמן ליניארי O(n log), זמן quadratic O(n2), וזמן אקספוננציאלי O(2n) כל אחד מייצג התנהגות מדרגת שונה. אלגוריתם O (1) לוקח את אותו הזמן ללא קשר לגודל קלט, בעוד זמן ריצה של O(n2) גדל פיטורים כפולים כמו קלט כפול.

ב C ו- C++, ניתוח מורכבות הזמן חייב לקחת בחשבון פרטים ברמה נמוכה ששפות ברמה גבוהה יותר מופשטות.התנהגות של Cache, תחזית סניף, חיזוי צינור הוראה, ודפוסי גישה לזיכרון משפיעים על זמן ריצה בפועל. אלגוריתם עם מורכבות תיאורטית יכול להופיע גרוע יותר בפועל אם הוא מציג כאב לקוי מקומי או דפוסים בלתי צפויים.

מורכבות חלל: הבנת הזיכרון

מורכבות חלל מודדת כמה זיכרון אלגוריתם דורש יחסית לגודל קלט.זה כולל את החלל הדרוש לאחסון נתוני קלט וכל מרחב עזר הנדרש במהלך ביצועו.בסביבות מאוישות זיכרון כמו מערכות משובצות או בעת עיבוד של נתונים גדולים, מורכבות חלל יכולה להיות חשובה בדיוק כמו מורכבות זמן.

C ו- C++ מפתחים יש שליטה ישירה על הקצאת זיכרון, מה שהופך את שיקולי מורכבות החלל רלוונטיים במיוחד.הקצאת זיכרון דינמי עם קניון או נושא חדש נושאת מעל הראש ויכולה לקלקל את הזיכרון. Stack הקצאה מהירה יותר אך מוגבלת בגודלם.הבנת הבורסות הללו מסייעות למפתחים לבחור אסטרטגיות ניהול זיכרון מתאימות לתרחישים שונים.

אלגוריתמים מסוימים מציעים סחרחורים במשרה בחלל, שבו אתה יכול להפחית את המורכבות של זמן באמצעות זיכרון או להיפך. memoization ו תכנות דינמי exegate את העיקרון הזה, מסחר זיכרון במהירות על ידי צ'נג תוצאות שנמחקו בעבר.ב- C++, מיכלים כמו std: unordered map מאפשר יישום יעיל של טכניקות כאלה.

Big O Notation and Asymptotic Analysis

הסימון הגדול מספק דרך סטנדרטית לבטא מורכבות אלגוריתמית על ידי תיאור הגבול העליון של קצב הצמיחה.כאשר אנו אומרים אלגוריתם הוא O(n), אנו מתכוונים כי זמן הריצה שלה גדל באופן ליניארי ביותר עם גודל קלט, תוך התעלמות מגורמים קבועים ותנאים נמוכים יותר.הפשטה זו מאפשרת השוואה משמעותית בין אלגוריתמים מבלי להתכווץ בפרטים יישום.

מעבר ביג או, מדעני מחשב משתמשים באמגה ( ⁇ ) גדולה כדי לתאר גבולות נמוכים יותר ו-Big Theta ( ⁇ ) לאוטציה של גבולות הדוקים.אלגוריתם שהוא ⁇ (n log n) גדל בדיוק באותו שיעור, לא מהר יותר ולא איטי יותר באופן אמפטורטי.הבנת ההערות האלה עוזר למפתחים לתקשר בדיוק על מאפייני ביצועים.

ניתוח אסימפטוטי מתמקד בהתנהגות כגודל קלט גישות אינסוף, מה שהופך אותו מצוין עבור השוואת אלגוריתמים אבל לפעמים מטעה עבור יישומים מעשיים. אלגוריתם O(n2) עם גורמים קבועים קטנים עלולים לפורש אלגוריתם O(n n) עבור קלטות קטנות.ב C ו- C++ פיתוח, במיוחד עבור מערכות עם מגבלות בגודל קלט ידועות, בהתחשב בגורמים קבועים וביצועים מעשיים כמו מורכבות אמפוטוטית.

ניתוח ביצועים של Algorithm C ו- C++

ניתוח המורכבות הפיזיולוגי מספק בסיס, אך הבנת הביצועים בפועל ב C ו- C++ דורש לבחון כיצד קוד מתורגם להנחיות מכונה ואינטראקציה עם חומרה. מעבדים מודרניים משתמשים בטכניקות אופטימיזציה מתוחכמות שיכולות להשפיע באופן דרמטי על התנהגות בזמן ריצה.

התפקיד של אופטימיזציה Compiler

מודרני C ו C + מדרבנים לבצע אופטימיזציה נרחבים שיכולים לשנות קוד בדרכים מפתיעות.לא לטבול, לתפקד בליירינג, מתקפלים, חיסול קוד מת וקטוריזציה יכולים לשפר באופן משמעותי את הביצועים.הבנת מה אופטימיזציה של מעצבים יכול ולא יכול לבצע עוזר למפתחים לכתוב קוד אשר מאגד קוד יעיל.

רמות אופטימיזציה Compiler, בדרך כלל נשלטות עם דגלים כמו -O0, -O1, -O2, -Os, מייצגים סחרחורת שונים בין זמן איסוף, גודל קוד וביצועי זמן ריצה.פיתוח בונה לעתים קרובות -O0 עבור איסוף מהיר יותר ו debugging קל יותר, בעוד ייצור בונה שימוש -O2 או -O3 לביצועים מקסימליים.

כתיבת קוד ידידותי אופטימיזציה כרוך הבנה של מגבלות מפרש. Compilers נאבקים כדי לייעל קוד עם מפרט נקודה, זרימה שליטה מורכבת, או פונקציות פונקציות שיחות באמצעות נקודות.שימוש בתיקון נקודות, הגבלת נקודות, שמירה על פונקציות קטנות וממוקדות עוזר לספידר לייצר קוד טוב יותר. ב C++, תבנית metaprogramming ו-contexpr מאפשר חישוב בזמן, עבודה מריצה כדי לסלול זמן כדי ליצור זמן.

ניהול כלים ואמצעי ביצועים

כלים של ייעוץ מספקים נתונים אמפיריים על איפה תוכניות לבלות זמן וצורכים משאבים. במקום לנחש אילו קטעי קוד זקוקים אופטימיזציה, פרופיל מזהה צווארי בקבוק בפועל המבוססים על ביצוע אמיתי. גישה זו מונעת מאמץ מבוזבז אופטימיזציה קוד שיש לו השפעה מינימלית על הביצועים הכוללים.

פרופיל gprof, זמין במערכות דמויי יוניקס, מספק פרופיל ברמת תפקוד מראה אילו פונקציות לצרוך את הזמן ביותר וכמה פעמים הם נקראים. Compiling עם דגל -pg מאפשר מכשיר פרופיל, וריצה התוכנית מייצרת קובץ gmon.out כי gpro מנתח כדי לייצר דוחות מפורטים.זה עוזר לזהות כתמים אופטימיזציה חמים שבו יש את ההשפעה הגדולה ביותר.

Valgrind מציעה חבילה של כלים לניתוח ביצועים ו debugging. הכלי Callgrind מספק פרופיל טלגרף שיחה מפורט, בעוד Cachegrind מדמה התנהגות cache לזהות מפספסים cache. פרופילים מפרשים שימוש זיכרון לאורך זמן, עוזר לזהות דליפות זיכרון והקצאות יתר.

פרופילים מודרניים כמו perf על לינוקס ומכשירים על macOS מספקים פרופיל מבוסס-על נמוך שיכול לנתח עומסי ייצור ללא השפעה משמעותית ביצועים. כלים אלה משלבים עם ניגודי ביצועים חומרה כדי למדוד מפספסי cache, הטרדות סניף, ואירועים מיקרו-אסטיים אחרים המשפיעים על הביצועים.

Benchmarking Best Practices

מתודולוגיה מדויקת דורשת מתודולוגיה זהירה כדי למנוע תוצאות מטעות.תזמון הוצאה להורג יחיד יכול להיות לא אמין בגלל לוח זמנים מערכת הפעלה, מצב כאב, וגורמים סביבתיים אחרים. הפעלת מספר רב של תחזיות וסטטיסטיקות מחשוב כמו סטיות מדיה וסטנדרט מספק מדידות אמינות יותר.

מיקרובנצ'נסינג, מדידת הביצועים של קטעי קוד קטנים בבידוד, דורש טיפול מיוחד. Compilers עשוי לייעל קוד שנראה שאין לו השפעה, או התחממות cache עלולה להפוך את ההסרות מהר יותר מאשר אלה הראשוניים. Libraries כמו Google Benchmark עבור C++ לספק תשתיות עבור microbenchmarking אמין, טיפול במכשולים נפוצים באופן אוטומטי.

כאשר השוואת אלגוריתמים, בדיקות עם נתונים מציאותיים חשובים מאוד.מסוגים לעומת נתונים אקראיים, נתונים עם הרבה לשכפלות לעומת כל הערכים הייחודיים, ונתונים שמתאימים ל- cache לעומת נתונים שאינם יכולים לייצר מאפיינים שונים באופן דרמטי.

מבנה נתונים משותף והשלכותיהם

בחירת מבנה הנתונים הנכון היא אחת ההחלטות המשפיעות ביותר עבור יעילות אלגוריתמית.כל מבנה נתונים מציע תכונות ביצועים שונות עבור פעולות שונות, והבנה של אלה עסקאות מאפשר החלטות עיצוב מושכלות.

אריות וקטורים: אחסון זיכרון מתמשך

Arrays לספק את מבנה הנתונים הפשוט והמהיר ביותר, אחסון אלמנטים במקומות זיכרון מקיפים.גישה אקראית היא O(1) כי חישוב כתובת של אלמנט דורש רק ריבוי אחד ותוספת. הפריסה ידידותית זו משמעה גישה לאלמנטים הסמוכים היא מהירה מאוד, שכן הם כבר נמצאים ב- cache.

מערךים בסגנון C נקבעו בזמן הרכיב או בזמן ההקצאה, מה שהופך אותם גמישים אך יעילים. C++ std:vector מספק מערך דינמי שגדל באופן אוטומטי, שילוב ביצועים מערך עם גמישות. Vectors לשמור על יכולת נפרדת מהגודל, ומאפשר כניסה מחדש של O(1) בסוף על ידי הקצאת שטח נוסף ורק לעתים ריאלי.

המגבלה העיקרית של המערך היא כי שילוב או מחיקה באמצע דורש שינוי כל האלמנטים הבאים, מה שהופך את הפעולות הללו O(n) עבור עומסי עבודה נשלטים על ידי גישה אקראית עם שינויים בלתי צפויים, ערכים מצטיינים. עבור עומסי עבודה הדורשים תוספות תכופות וסטיות, מבנים נתונים אחרים עשויים להיות מתאימים יותר.

מקומיות Cache הופכת את המערך יעיל במיוחד על מעבדים מודרניים.כאשר אתה ניגש לרכיב אחד, המעבד לטעון קו שלם של קשת המכיל אלמנטים סמוכים. Sequential מערך traversal משיג ביצועים מצוינים כי כל קו cache מספק מספר אלמנטים שימושיים. זה יעילות ברמת החומרה לעתים קרובות עושה מנגנונים מהירים יותר בפועל מאשר מבנים נתונים עם מורכבות טובה יותר תיאורטית.

רשימות קשורות: Dynamic Sequential Storage

רשימות מקושרות מאחסנות אלמנטים בצומת מפוזרים לאורך כל הזיכרון, עם כל צומת המכיל נתונים ונקודת מפנה לצומת הבא.מבנה זה מאפשר כניסה ומחיקה של O(1) כאשר יש לך מצביע על נקודת ההכנסה, שכן אתה רק צריך לעדכן כמה נקודות במקום לשנות אלמנטים.

ה- Tradingoff הוא כי גישה אקראית הופכת ל- O(n) כי השגת האלמנט nth דורשת לאחר נקודות מן הראש.בנוסף, כל צומת דורש זיכרון נוסף עבור נקודות, הגדלת שטח מעל C++, std:list מיישום רשימה כפולה מקושרת עם נקודות לשני הצדדים הבאים וקדמוניים, המאפשרת מסלולים דו-צדדיים עלות של זיכרון נוסף.

מקומי של שפם מסכן הוא החיסרון המעשי הגדול ביותר של רשימות.מכיוון שצומת מפוזרים בזיכרון, גישה לגורם הבא כמעט תמיד דורשת מפספסת מטמון.זה הופך את הרשימה המקושרת לאט יותר מאשר מסלול ממערך בפועל, למרות ששנים הם תיאורטית O(n) עבור רוב היישומים, האופי ידידותי של המערך עולה על היתרונות התיאורטיים של רשימות קשורות.

רשימות מקושרות מאירות בתרחישים ספציפיים כגון יישום תורים שבהם אתה רק מוסיף לסיומו אחד ולהסיר מן האחר, או כאשר אתה צריך לעתים קרובות להתפרץ יחד או לפרק רצפים נפרדים.

שולחן האש: מהיר מפתח-ויל

שולחנות האש מספקים ממוצע של O(1), השקיה, והמחיקה על ידי שימוש בפונקציה של hash כדי למפות מפתחות כדי לקבוע אינדיקציות. ביצועים יוצאי דופן אלה הופכים את טבלאות hash לזמין עבור יישומים הדורשים גישה מבוססת מפתח מהיר, מאינדקס מסד נתונים כדי לדרג טבלאות סמלים כדי לעקוף מערכות כיס.

הפונקציה hash מצמידת a integer מן המפתח, אשר ממפה לאינדקס מערך, בדרך כלל באמצעות Modulo ⁇ . Good hash פונקציות להפיץ מפתחות באופן אחיד על פני המערך, מיני התנגשות שבו מפתחות שונים ישh לאותה אסטרטגיות החלטה קולית כוללים שרשרת, שבו כל מרווח מכיל רשימה מקושרת של אלמנטים התנגשות, ופותח, שבו בדיקות התנגשות עבור חלופות עבור חריצים.

C++ מספק std: Unordered map ו std:: Unordered set as hash Table יישוםs. מיכלים אלה מציעים ביצועים מצוינים תיק בינוני אבל הגרוע ביותר O(n) פעולות אם רבים קולטים.גורם העומס, היחס של אלמנטים לגודל מערך, משפיע באופן משמעותי על הביצועים.

ביצועי שולחן האש תלויים באופן ביקורתי באיכות הפונקציה hash עני, אשר מייצרת התנגשויות רבות יכול להפיג את הביצועים של O(n) אפילו עם גורם עומס נמוך. עבור סוגים מותאמים אישית, יישום פונקציה ישה טובה דורש הבנה של ההפצה של הנתונים ולהבטיח ערכים שונים לייצר חיתולים שונים עם הסתברות גבוהה. C++11 של ,hash מספק ברירת מחדל עבור סוגים מובנה יכול להיות מיוחד עבור סוגים מיוחדים עבור סוגים.

עץ חיפוש בינארי: סדר נתונים דינמיים

עצי חיפוש בינאריים שומרים על אלמנטים בסדר מיון תוך תמיכה בהכנסה יעילה, מחיקה, ופעולות חיפוש.כל צומת יש ברוב שני הילדים, עם כל האלמנטים בצוללת השמאלית פחות מהצומת וכל האלמנטים בצוללת הנכונה גדול יותר.

המלכוד הוא שעצים בסיסיים של חיפוש בינארי יכולים להפוך לא מאוזנים, מידרדרים להופעה של O(n במקרה הגרוע ביותר.אם אתה מוסיף נתונים ממיין לתוך BST בסיסי, זה הופך לרשימה מקושרת עם כל הצומת יש רק ילדים צודקים.עצים דמויי עצמי כמו עצי AVL ועצים שחורים אדומים לשמור על איזון באמצעות סיבובים במהלך הכנסה ו deletion, ערבות ביצועים גרועים יותר של Olog( ).

C++ std:map ו std: ראשית, בדרך כלל ליישם עצים שחורים אדומים, מתן ביצועים לוגיסטיים מובטחים עבור כל הפעולות. מיכלים אלה לשמור אלמנטים סדר מיון, המאפשר שאילתות טווח יעילות וצוינו אותו. כאשר אתה צריך גם חיפוש מהיר הזמנה מכוונן, עצי חיפוש מאוזנים מציעים פתרון מצוין.

עצי B + B מרחיבים את מושג עץ החיפוש בינארי לבלוטות עם ילדים רבים, צמצום גובה העץ ושיפור ביצועי ה- cache. מבנים אלה חשובים במיוחד עבור מערכות מסד נתונים ומערכות קבצים שבו נתונים שוכנים על דיסק ו minimizing גישה דיסק הוא קריטי.כל אחד לאד מכיל מפתחות מרובים וילדים, ודיסק יחיד קורא להביא כל צומת שלם, מה שהופך שימוש טוב יותר של כל פעולה I / O יקר.

המונחים: Priority Queue Implementation

הערימה היא עצים בינאריים ששומרים על רכוש הערימה: כל הורה אינו גדול יותר או שווה לילדיו בערימה מקסית, או פחות או שווה בערימה של מניין.מבנה זה מאפשר ל-O(1) גישה לגורם המקסימלי או המינימום ו- O(log n) להוסיף ו deletion, מה שהופך את הערימה האידיאלית ליישום תורים.

heaps בינארי בדרך כלל מיושם באמצעות מערך, עם מערכת היחסים של ההורה-ילד המוגדר על ידי index ⁇ .עבור צומת באינדקס i, הילדים שלה הם אינדיקציות 2i+1 ו 2i+2, והורה שלה הוא באינדקס (i-1) , יישום מבוסס מערך זה מספק מקומי מטמון מעולה תוך שמירה על מבנה העץ באופן לא סביר.

C++ std:priority queue מספק יישום תור עדיפות מבוסס הערימה.הכול שומר באופן אוטומטי את סדר הערימה כמו אלמנטים מוכנסים ומחקו. heaps הם הכרחי עבור אלגוריתמים כמו הדרך הקצרה ביותר של Dijkstra וסוג heap, ולכל יישום הדורש גישה יעילה אלמנט העליון או הנמוך ביותר.

גרפים: ייצוג יחסים

Graphs מייצגים יחסים בין גופים, עם אותנטיות המייצגת ישויות ונקודות המייצגות מערכות יחסים. Graph ייצוג משפיע באופן משמעותי על יעילות האלגוריתם. Adjacency matrices להשתמש במערך 2D שבו matrix [i] מציין אם קיים מ- vertex i to vertex j, מתן O(1) קצה מראה אך מורכבות שטח O(V2).

רשימות של Adjacency מאחסנים לכל vertex רשימה של שכנותיה, באמצעות שטח O(V + E) שבו V הוא אותנטיות ו- E הוא הקצוות. ייצוג זה הוא יותר יעיל עבור גרפים ספאריים שבו E הוא הרבה פחות מ V2. Edge נראה הופך O( מעלות) שבו תואר הוא מספר השכנים, אבל זה על פני כל הקצוות הוא יעיל.

בחירת בין ייצוגים תלויה בצפיפות גרפית ומבצעים הנדרשים. גרף Dense עם הרבה קצוות ליהנות מהופעות חד-משמעיות של מזחלות קצה קצה קצה קצה קצה קצה קצה קצה קצה קצה קצה קצה קצה קצה קצה קצה קצה קצה קצה קצה קצה קצה קצה קצה קצה קצה קצה קצה קצה קצה קצה קצה קצה קצה. גרפים ספורד ליהנות יעילות החלל של רשימות דבקות.

טכניקות אופטימיזציה ל-C ו- C++

מעבר לבחירת אלגוריתמים יעילים ומבנים נתונים, טכניקות אופטימיזציה מעשיות רבות יכולות לשפר באופן משמעותי את ביצועי תכנית C ו- C++. טכניקות אלה נעות מניהול זיכרון ברמה נמוכה ועד להחלטות אדריכליות ברמה גבוהה.

מינימום זיכרון אל-מיקום

הקצאת זיכרון דינמי עם קניון, calloc, או חדש הוא יקר יחסית, כולל שיחות מערכת וניהול זיכרון מעל ראש. הקצאה תדירות והתמודדות יכול לקלקל זיכרון וביצועי מטמון. מינימיזציה לעתים קרובות מספק שיפורים משמעותיים ביצועים.

אובייקטים המאגדים מחדשים שהוקצו אובייקטים ולא שוב ושוב מסלקים אותם ומשחררים אותם. לשמור על מאגר של אובייקטים שהוגדרו מראש ומחזר אותם כנדרש.טכניקה זו יעילה במיוחד עבור אובייקטים עם תקופות חיים קצרות שנוצרו ונהרסו לעתים קרובות, כגון חלקיקים במנוע משחק או באפיינים זמניים בשרת רשת.

הקצאת ארנה או ניהול זיכרון מבוסס אזור מקצה בלוקים גדולים של זיכרון ומפיצה הקצאות קטנות יותר מהבלוקים האלה.כאשר אתה נעשה עם כל ההקצאות מהזירה, לשחרר את כל הזירה בבת אחת.גישה זו היא מהירה מאוד ומבטלת פיצול, אם כי זה דורש ניהול חיים זהיר כדי להימנע באגים ללא שימוש.

הקצאת Stack היא הרבה יותר מהירה מאשר הקצאת ה- heap, כי היא רק דורשת התאמה של נקודת הערימה. השתמש בהקצאת ערימה עבור אובייקטים קטנים בגודל קבוע עם תקופות חיים מוגדרות היטב. C99 התמחויות באורך משתנה ו- C++ std::array מאפשר הקצאה עם גדלים שנקבעו בריצה או בהקמה של זמן בהתאמה. להיות זהיר של ערימה עם הקצאות גדולות, כמו שטח מוגבל.

אופטימיזציה של Cache Performance

מעבדים מודרניים מהירים יותר דרמטי מזיכרון, מה שהופך את ביצועי ה-Cache קריטי. פספסת מטמון יכולה לעלות מאות מחזורים, בעוד שכאב פגע בעלויות רק כמה.כתיבה קוד ידידותי ל- cache יכול לשפר את הביצועים באמצעות הזמנות של גודל עבור יישומים רגישים לזיכרון.

מבנה הנתונים משפיע באופן משמעותי על ביצועי ה- cache.מבנה של מערך (SoA) מאחסן כל שדה במערך נפרד, שיפור ניצולי ה- cache כאשר אתה רק ניגש לתחומים מסוימים. Array of Structures (AoS) מאחסן אובייקטים מלאים במערך, טוב יותר כאשר אתה ניגש לכל התחומים יחד.בחירת הפריסה הנכונה תלויה בדפוסי גישה.

לולאה סדר עניינים עבור מערכי רב-ממדיים. ב C ו- C++, מחסנים נשמרים בסדר-מג'ר, כלומר אלמנטים רצופים בממד האחרון נמצאים בסמוך לזיכרון.התחילה עם המדד האחרון בלולאה הפנימית ביותר ממקסימה את להיטי ה-Cache. עבור מערך 2D, זה יותר מערכי [ij] עם J בלולאה פנימית, לא מערך [J].

Prefetching במפורש לטעון נתונים לתוך cache לפני שזה נחוץ, מסתירה את הגמישות זיכרון. מעבדים מודרניים לבצע prefetching אוטומטי עבור דפוסי גישה צפויים כגון מסלול מערך quential. עבור דפוסי גישה לא סדירים, prefetching ידנית עם מברק intrinsicsicsics כמו Buildin prefetch יכול לעזור, למרות שהוא דורש כוונון זהיר כדי למנוע prefetching מוקדם מדי או מאוחר מדי.

המונחים: overhead

שיחות פונקציונליות כרוכות בהצלת רישומים, פרמטרים חולפים, קופצים לתפקוד, וחזרה.עבור פונקציות קטנות הנקראות לעתים קרובות, ראש זה יכול לשלוט בזמן ביצוע.כמה טכניקות להפחית את השיחה לתפקוד יתר.

תוך התעלמות מפעולת שיחה עם הגוף של הפונקציה, ביטול שיחות מעל הראש. Compilers באופן אוטומטי ליזום פונקציות קטנות, במיוחד כאשר מוגדר בראש או מסומן עם מילת המפתח באינטרנט.עם זאת, עלייה מופרזת בגודל קוד, שעלול לפגוע בביצועי זיכרון הוראה.פי המדפים מודרניים לקבל החלטות מתוחכמות על בסיס תפקוד ותדירות קריאה.

ב C++, פונקציות תבנית ופונקציות עצירות מאפשרות חישוב ואופטימיזציה של זמן רב ואופטימיזציה. תבניות מאפשרות ל-Matr ליצור קוד מיוחד עבור כל סוג, המאפשר אופטימיזציה בלתי אפשרית עם פונקציות פולימורפיזם במשרה מלאה. Constexpr יכול לבצע בעת ביצוע בעת ביצוע כאשר נתון טיעונים קבועים, העברת חישוב משעות ריצה כדי ליצור זמן שלם.

פונקציות וירטואליות ב C++ כרוכות בעקיפין דרך ה- vtable, מניעת הכחשה והוספת מעל לראש. כאשר פולימורפיזם אינו נחוץ, מעדיף פונקציות לא וירטואליות.כאשר פולימורפיזם הוא הכרחי, לשקול חלופות כמו std:חלות או עיצוב מבוסס מדיניות המאפשרים פולימורפיזם של זמן ייצור ללא זמן רב על פני ראש.

מינוף SIMD ו- Vectorization

הוראות הוראה אחת מרובות נתונים (SIMD) מעבדות מספר אלמנטים נתונים עם הוראה אחת, ומספקות שיפורים משמעותיים בביצועים עבור פעולות דומות לנתונים.מעבדים מודרניים תומכים ב- SIMD הוראות כגון SSE, AVX, ו- NEON שפועלים על 128 סיביות, 256 סיביות, או 512-bit וקטורים.

אופטימיזציה אוטומטית מאפשרת לגולשים ליצור באופן אוטומטי קוד SIMD מקוד קלאבלר.לאות פשוטות המבצעות את אותו פעולה על אלמנטים מערך הם מועמדים טובים עבור עריכת אוטומטי.עזור לרכיבה וקטורז כרוך בכתיבת לולאות פשוטות, הימנעות זרימה שליטה מורכבת, ולהבטיח היערכות נתונים. ptree-vectorize ועדכוני אופטימיזציה לעזור לזהות הזדמנויות וקטוריזציה.

וקטוריזציה של אינטגרטיביים באמצעות אינטרינוטיקה או הרחבות וקטור מספק שליטה רבה יותר מאשר אינקטוריזציה אוטומטי. Intrinsics הם C פונקציות שממפה ישירות להנחיות SIMD, ומאפשרות קוד SIMD מותש יד בעת שנותר C/C++ Libraries כמו FLT:0IntelLFLT:1 לספק אופטימיזציה גבוהה של פעולות נפוצות.

היערכות נתונים חיונית לביצועים של SIMD. הוראות SIMD רבות דורשות נתונים המיובאים ל- 16-byte או 32-byte. גישה בלתי מזוינת יכולה לגרום לתאונות על כמה ארכיטקטורות או עונשים משמעותיים בביצועים על אחרים. השתמש בפונקציות הקצאה תואמים כמו תואמים alloc או תכונות מדגמים כמו תואמים כדי להבטיח היערכות נאותה.

קוד משותף

Compilers יכול לייעל קוד ביעילות רבה יותר כאשר הוא עוקב אחר דפוסים מסוימים.הבנת מה מעצבנים יכולים ולא יכול לייעל את המפתחים לכתוב קוד אשר מאגד קוד מכונה יעיל.

נכונות קונסטנטית מסייעת לפייס את האופטימיזציה על ידי המציין אילו נתונים לא משתנים.מארק נקודות והתייחסויות עצירות מאפשרת אופטימיזציה כי יהיה לא בטוח אם הנתונים עשויים להיות שונו. מילת המפתח המוגבלת ב-C מעידה כי מצביע היא הדרך היחידה לגשת לנתונים שנראו, המאפשר אופטימיזציה כי יהיה לא בטוח עם נקודות ציון.

הימנעות מענפים בלולאות חמות יכול לשפר את הביצועים על ידי מניעת תקלות של הענף.טכניקות כמו תכנות ללא סניף שימוש בקידוד ופעולות bitwise במקום הצהרות מותניות.לדוגמה, מחשוב המינימום של שני פולשים כמו b ((a ↑) & -(a < b) נמנע ענף, אם כי מדגמים מודרניים מבצעים לעתים קרובות אופטימיזציה זו באופן אוטומטי.

שינויים בלולאה כמו לולאה, לולאה היתוך, וחילופי לולאה יכולים לשפר באופן משמעותי את הביצועים. Compilers לבצע רבים אלה באופן אוטומטי, אבל ההבנה שהם עוזרים למפתחים לכתוב לולאות שקל יותר לייעל.

פורמטים עיצוב Algorithm ו Paradigms

גישות אלגוריתמיות מסוימות ודפוסי עיצוב מופיעים שוב ושוב בעיצוב אלגוריתם יעיל.הבנת פרדיגמות אלה מספקת ערכת כלים לפתרון בעיות מגוונות ביעילות.

פיצול וכיבוש

אלגוריתמים מפוצלים וכבוש פורצים בעיות לסובייקטים קטנים יותר, פותרים אותם באופן חוזר, ומשלבים את התוצאות. גישה זו מניבה לעתים קרובות אלגוריתמים יעילים עם מורכבות לוגיסטית או ליניארית. mge ומהירות מדגימה את הפיצות והכבוש, השגת O(n di n) ממיין על ידי חלוקה מחדש של המערך.

יעילות הפיצול והכיבוש תלויה בשאלה כיצד הבעיה מתחלקת וכיצד ביעילות ניתן לשלב תוצאות.חיפוש בינארי משיג חיפוש O(log n) על ידי חלוקת מרחב החיפוש בחצי כל הרהרציה.המשפט מספק מסגרת לניתוח הפרדה וכיבוש ההישנות, עוזר לחזות מורכבות אלגוריתמית.

ב C ו- C++, יישום דיבידנד וכיבוש דורש תשומת לב זהירה לצעדת עומק כדי להימנע מערימות יתר על פני השטח.עבור סיור עמוק, לשקול יישום זהרטיבי או הגדלת גודל ערימה. Tail retour אופטימיזציה יכול לחסל את צמיחת הערימה עבור דפוסים חוזרים מסוימים, אם כי C ו C + מעבדים לא להבטיח אופטימיזציה זו.

דינמי תכנות

תכנות דינמי פותר בעיות על ידי שבירה אותם לתוך תת-בעיות חפיפה ותוצאות צ'ינג כדי להימנע חישובים מאוזנים.טכניקה זו הופכת אלגוריתמים אקספוננציאלית לפולינומיים בזמן המסחר בחלל לזמן.

רצף הפיבונצ'י מדגים את העוצמה של תכנות דינמי.יש יישום חוזר נאיבי יש מורכבות אקספונציאלית כי הוא מחדש מאמת את אותם ערכים שוב ושוב. Caching computed ערכים במערך מפחית מורכבות ל- O(n) עם O(n) אופטימיזציה נוספת באמצעות רק שני משתנים מפחיתים את החלל ל- O(1).

בעיות תכנות דינמיות מציגות מבנה תת-מבנה אופטימלי, שבו פתרונות אופטימליים מכילים פתרונות אופטימליים כדי subproblems.זיהוי מבנה זה הוא מפתח ליישום תכנות דינמי.קלאסי דוגמאות כוללות תת-השוויון המשותף הארוך ביותר, מרחק עריכה ובעיות knapsack, אשר מופיעים ביישומים בעולם האמיתי מביו-informatics להקצאת משאבים.

תכנות דינמי עם memoization משתמש טיולים ו caches תוצאות בטבלה hash או מערך.תחת תכנות דינמי העליון , תוך כדי ייצור פתרונות מ subproblems קטנים ביותר לבעיה הסופית. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

גנדי אלגורית

אלגוריתמים אפורים עושים בחירות אופטימליות בכל שלב, בתקווה למצוא את האופטימום העולמי, בעוד אלגוריתמים חמדנים לא תמיד מייצרים פתרונות אופטימליים, כשהם עושים, הם לעתים קרובות פשוטים ויעילים יותר מאשר גישות אחרות.

אלגוריתם הנתיב הקצר ביותר של דייקסטרה מדגים גישה מוצלחת של חמדנות, תמיד מרחיב את ה-Huffman הקרוב ביותר ל-Huffman הקידוד של דחיסת נתונים בונה קוד ללא תיקון אופטימלי על ידי שילוב שוב ושוב של שני הסמלים הנפוצים ביותר.

בהנחה שאלגוריתם חמדני מייצר תוצאות אופטימליות דורש להפגין את רכוש הבחירה הנאות והמבנה הסגול ביותר ללא הוכחה, אלגוריתמים חמדניים עשויים לייצר תוצאות תת-אופטימיות.לדוגמה, גישה חמדנית לבעיה 0/1 לא מבטיחה אופטימליות, בעוד זה עושה לבעיה של knapsack שברירית.

גם כאשר אלגוריתמים חמדניים אינם מבטיחים את אופטימליות, הם לעתים קרובות מספקים תחזיות טובות ביעילות.עבור בעיות NP-Hard שבו פתרונות אופטימליים הם בלתי סבירים, היירויים חמדניים יכולים לייצר פתרונות מקובלים במהירות.

ריצוף ושרשרת-and-Bound

מעקב אחר מסלול באופן שיטתי חוקר את מרחב הפתרון על ידי בניית מועמדים באופן מצטבר ונטישה מועמדים שאינם יכולים להוביל לפתרונות תקפים.גישה זו פותרת בעיות שביעות רצון מעצימות כמו סודוקו, N-queens, צבע גרפי.

מעקב לאחור מספק אסטרטגיות טובות לריצה כדי להימנע מבדיקת סניפים שאינם מתקדמים. Constraint propagation מבטל ערכים שאינם יכולים להשתתף בכל פתרון, צמצום מרחב החיפוש.בחירתו המשתנה כדי להקצות הבא ומה כדי לנסות ערכים משפיעים באופן משמעותי על הביצועים.

הזרוע-ומרכזית מרחיבה את בעיות אופטימיזציה על ידי שמירה על גבולות על ערך הפתרון האופטימלי.כאשר חקר ענף, אם גבולותיה מצביעים על כך שהוא לא יכול לשפר את הפתרון הטוב ביותר שנמצא עד כה, לנכון כי טכניקה זו יעילה במיוחד עבור שילוב בעיות אופטימיזציה למערכות כגון מכירות נסיעות ותזמון עבודה.

מיון וחיפוש אלגוריתמים

מיון וחיפוש הם פעולות בסיסיות המופיעות באינספור יישומים.הבנת המאפיינים של אלגוריתמים שונים מאפשרת לבחור את הגישה הנכונה לכל מצב.

המונחים: Based sorting

אלגוריתמים מבוססי השוואה יש גבול נמוך תיאורטי של O(n log n) למורכבות הגרועה ביותר. Quicksort, ממזגים סוג, ו- heap להזין את הגבול הזה, אם כי עם מאפיינים שונים של ביצועים מעשיים.

Quicksort מחיצת את המערך סביב אלמנט pivot, recursive ממיין את המחיצות. עם בחירת pivot טובה, מהירsort להשיג O(n log n) ביצועים ממוצעים ומיקום cache מעולה.עם זאת, ביצועים הגרועים ביותר הוא O(n2) עם מבחר pivot גרוע.

מארג' מחלק את המערך בחצי, חוזר כל חצי, ומאחד את המורכבות המנוכרת של הלווינים.זה מבטיח O(n log n) ביצועים הגרועים ביותר והוא יציב, שמירה על הסדר היחסי של אלמנטים שווים.החלופה העיקרית היא O(n) מורכבות שטח עבור פעולת המיזוג, אם כי בגרסאות במקומות קיימים עם יישום מורכב יותר.

סוג ה-Heap בונה ערימה מהמערך ומוציא שוב ושוב את האלמנט המקסימלי.הוא משיג ביצועים גרועים של O(n di n) עם מורכבות שטח O(1), מה שהופך אותו אטרקטיבי כאשר הזיכרון מוגבל.עם זאת, אזורי שפם עניים הופכים את הערימה לאט יותר מאשר מהירות או מיזוג לסוג עבור רוב הקלטים.

C מספק qsort עבור עריכת מנגנונים, בעוד C++ מספק std:sort ו std::stable sort. אלה יישום הספרייה להשתמש אלגוריתמים היברידיים מתוחכמים, בדרך כלל introsort עבור std:sort, אשר משלב מהיר, heap, והוספת כדי להשיג ביצועים מצוינים וגרועים ביותר.

המונחים: non-Comparison

אלגוריתמים שאינם שותפים לסינון יכולים לעלות על ה- O(n log n) נמוך יותר על ידי ניצול התכונות של הנתונים. Counting, קרינת רנטגן, ודלי להשיג מורכבות זמן ליניארית בתנאים מסוימים.

ספירת יצירות מסוג כאשר אלמנטים הם integers בטווח ידוע.זה נחשב להתרחשות של כל ערך ומשתמש בספירות אלה כדי להציב אלמנטים בסדר ממונן, השגת מורכבות O(n + k) שבו k הוא טווח הערכים.כאשר k הוא O(n), ספירה פועל בזמן ליניארי.האלגוריתם יציב ומשמש לעתים קרובות כ subroutine בקרינה.

רדינקס מעבד אלמנטים ספרות על ידי ספרות, באמצעות סוג יציב כמו ספירה עבור כל ספרה. עבור integers עם d ספרות, קורנקס סוג משיג מורכבות O(d-n) כאשר d הוא קבוע, זה זמן ליניארי. רדינקס עובד עבור מיתרים וסוגים נתונים אחרים שניתן לפסל לתוך ספרות או דמויות.

סוג באקקט מחלק אלמנטים לתוך דליים, סוגים של דלי, ומאגד את התוצאות.כאשר אלמנטים מחולקים אחיד, דלי משיג מורכבות של O(n) ממוצע מזוודה. ביצועי האלגוריתם תלויים במידה רבה בחלוקת קלט, מה שהופך אותו יעיל עבור דפוסי נתונים ספציפיים אבל לא אמין עבור קלטות שרירותיות.

חיפוש אלגוריתמים

חיפוש בינארי מוצא אלמנטים במערךים ממוינים בזמן O(log n) על ידי חלוקה שוב ושוב של מרחב החיפוש בחצי.אלגוריתם פשוט זה יעיל להפליא, צמצום חיפוש של מיליון-הההה ל- 20 ההשוואה. C מספק bsearch for Binary Search, בעוד C++ מספק std:binary search, std:lower bound, ו std:upp reveed: for for absearch for acre bound for acbound for acre bound for aquid.

חיפוש האינטרפולציה משתפר על חיפוש בינארי בנתונים מבוזרים אחידים על ידי הערכת המיקום של האלמנט מבוסס על הערך שלו.זה יכול להשיג מורכבות של O(לוג n) ממוצע, אם כי הגרוע ביותר נשאר O(n). חיפוש אינטרפולציה עובד טוב עבור נתונים כגון מילים או מספרים מבוזרים אחידים.

חיפוש מבוסס האש באמצעות טבלאות hash מספק O(1) ממוצע צפי, מה שהופך אותו מהר יותר מאשר חיפוש בינארי עבור נתונים גדולים.המסחר הוא שטח נוסף עבור שולחן היש והיעדר ההזמנה. כאשר אתה צריך גם חיפוש מהיר וצוה השקיה, שילוב של שולחן יש עבור לחפש עם מבנה נפרד עבור זה יכול להיות יעיל.

גפרף אלגורית'ים ומורכבותם

אלגוריתמים של Graph פותרים בעיות הקשורות ליחסים בין גופים, מניתוח רשת חברתית ועד לתכנון נתיב תכנון מעגלים.הבנת מורכבות אלגוריתם גרף חיונית לעבודה עם נתונים ברשת.

גרף טראוותר אלגורית

חיפוש ראשון בלחם (BFS) חוקר רמה של גרף ברמה, ביקור בכל שכנפיים של vertex לפני המעבר לשלב הבא. BFS מוצא מסלולים קצרים ביותר בגרפים לא במשקל, רץ ב O(V + E) בזמן שימוש תור כדי לעקוב אחר אותנטיות לבקר.האלגוריתם הוא בסיסי לבעיות גרף רבות, ממציאת רכיבים מחוברים לבדיקות bipartite.

חיפוש עומק ראשון (DFS) חוקר ככל האפשר לאורך כל ענף לפני מעקב לאחור. DFS פועל גם בזמן O(V + E) ויכול להיות מיושם באופן חוזר או זהיר עם ערימה. DFS הוא שימושי עבור מיון טופולוגי, זיהוי מחזורים, ומציאת רכיבים מחוברים בגרפים מכוונים.

גם BFS ו- DFS מבקרים כל vertex ו- Edge פעם, מה שהופך אותם לינאריים בגודל גרף.הבחירה ביניהם תלויה במבנה הבעיה. BFS מוצא מסלולים קצרים ביותר ולחקור אותנטיות קרובה, בעוד DFS משתמש פחות זיכרון עבור גרפים רחבים מטפל באופן טבעי מבנים של בעיות.

הדרך הקצרה ביותר אלגוריתמים

האלגוריתם של דייקסטרה מוצא מסלולים קצרים יותר ממקור אל כל שאר האותנטיות בגרפים עם משקולות שאינן שליליות.שימוש בתור עדיפות, הוא משיג מורכבות O(V + E) עם a binary heap או O(V log + E) עם Abonacci heap.

האלגוריתם בלמן-פורד מטפל בגרפים עם משקולות שליליות, גילוי מחזורים שליליים ונתיבים קצרים ביותר ב O(VE) בזמן איטי יותר מאשר האלגוריתם של דייקסטרה, יכולתו של בלמן-פורד להתמודד עם משקולות שליליות הופכת אותו חיוני עבור יישומים מסוימים כמו זיהוי מטבע.

אלגוריתם Floyd-Warshall מציב מסלולים קצרים ביותר בין כל זוגות של אותנטיות בזמן O(V3). עבור גרפים צפופים שבו אתה צריך את כל הנתיבים הקצרים ביותר, Floyd-Warshall הוא לעתים קרובות מעשי יותר מאשר הפעלת אלגוריתם V פעמים V של Dijkstra.הפשטות של האלגוריתם ותבנית גישה ידידותית- cache להפוך אותו יעיל בפועל עבור גרפים בינוניים.

חיפוש* מרחיב את האלגוריתם של דייקסטרה עם פונקציה תיירותית המוערך כי הוא מעריך מרחק אל המטרה.עם היוריסטי בלתי עביר כי לעולם לא overestimates מרחק אמיתי, A * מוצא מסלולים אופטימליים תוך פחות אותנטיות מאשר אלגוריתם של Dijkstra.A* יעיל במיוחד עבור מציאת משחקים ורובוטיקה שבה הוא עתידנים טובים זמינים.

עץ גילוח מינימלי אלגורית

מינימום המשתרע על פני העצים מחבר את כל האותנטיות בגרף במשקל משקל משקל עם משקל קצה מינימלי. אלגוריתם של קרוסקאל מונים על ידי משקל ומוסיף אותם לעץ המתפרש אם הם לא יוצרים מחזור, באמצעות מבנה נתונים של האיחוד למציאת מחזור עבור זיהוי מחזור.האלגוריתם פועל בזמן O(E log E) נשלט על ידי מיון.

האלגוריתם של פרימי גדל העץ הממושך מ-retex החל, שוב ושוב מוסיף את קצה משקל מינימלי המחבר בין עץ vertex ל- un-tree-tex. עם heap בינארי, האלגוריתם של פריים משיג O(V + E) לוגב מורכבות, בדומה לאלגוריתם של Dijkstra. for גרפים צפופים, אלגוריתם של פרימי יכול להיות יעיל יותר מאשר Croal'ssk.

שני האלגוריתמים מייצרים מינימום אופטימלי של עצים, עם הבחירה בהתאם לצפיפות הגרף ולנוחות היישום. אלגוריתם של קרוסקל עובד טוב עבור גרגרי ספאאר וקל יותר ליישם, בעוד האלגוריתם של פרימי עדיף עבור גרפים צפופים וכאשר אתה רוצה לבנות את העץ באופן מצטבר.

מחרוזת Algorithms ו- Pattern Matching

עיבוד סטרינג הוא ubiquitous במחשוב, מעורכי טקסט ועד ביונופורמטיקה לחיפוש באינטרנט. אלגוריתמים מיתרים Efficient יכולים לשפר באופן דרמטי את הביצועים עבור יישומים של טקסט-כבד.

« « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « « «

הגישה הנאיבית למציאת דפוס בטקסט בודק כל עמדה, השוואת האופי על ידי הדמות.זה משיג מורכבות O(nm) שבה n הוא אורך טקסט ו m הוא אורך דפוס. בעוד פשוט ליישם, התאמה תמימה אינה יעילה עבור טקסטים גדולים או דפוסים.

C מספק סטריסטר לחיפוש חתרני, בעוד C++ מספק סטד: סטרלינג:: מציאת פונקציות הספרייה האלה בדרך כלל להשתמש אלגוריתמים אופטימיזציה כי התאמות תמימות רפורמטיבית, מה שהופך אותם מועדפים לשימוש כללי.הבנת אלגוריתמים מתוחכמים יותר עוזר כאשר פונקציות הספרייה לא לענות על דרישות ביצועים.

ניות-מוריריס-פראט אלגוריתאם

אלגוריתם KMP מעבד את התבנית כדי לבנות תפקוד כשל המציין כמה רחוק לעבור לאחר תקלה.זה מבטל השוואות מקודמות, השגת מורכבות O(n + m) KMP לעולם לא נסוג טקסט, מה שהופך אותו יעיל עבור זרימת נתונים שבו אתה לא יכול לשחזר עמדות קודמות.

חישוב תפקוד הכשל הוא המפתח ליעילותו של KMP.עבור כל עמדה בדפוס, הוא מצמיד את אורך הרצף המתאים הארוך ביותר שהוא גם suffix. מידע זה מנחה את האלגוריתם כאשר מתרחש אי התאמה, ומאפשר לו לדלג על עמדות שלא יכולות להתאים.

בויאר-מור אלגוריתאם

בויאר-מור מחפש מימין לשמאל בדפוס, תוך שימוש בשני היסטרים כדי לדלג על עמדות.החוק של הדמות הרעה משתנה בהתבסס על עמדתו של הדמות הלא-מתאמת בתבנית.השינוי הטוב של ה-Suffix מבוסס על התאמת suffixes. אלה היואיריסטים לעתים קרובות לאפשר לדלג על חלקים גדולים של טקסט, השגת ביצועים תת-ליניים ממוצע.

בויאר-Moore יעיל במיוחד עבור אלפביתים גדולים ודפוסי זמן ארוכים, שבו הירויים מאפשרים לדלג גדול. הרבה יישומי חיפוש מחרוזת מעשיים, כולל אלה בעורכי טקסט וכלים חיפוש, להשתמש בוייר-Moore או גרסאות בגלל הביצועים הקנוניים המצוין שלו.

רבין-קארפ אלגוריתאם

רבין-Karp משתמש בהתחרחשות כדי למצוא התאמות דפוס.זה מעדכן את הישבן של התבנית ומשווה אותה ל- ישויות של קטעי טקסט.שימוש ב- hash מתגלגל, הוא מעדכן את היש עבור כל עמדה ב- O (1) זמן, השגת מורכבות של O(n + m) ממוצע.כאשר יש התאמה, זה מאמת את האופי על ידי אופי כדי למנוע התנגשויות חיוביות.

רבין-Karp מצטיין במציאת דפוסים מרובים בו-זמנית על ידי מחשוב ישים לכל הדפוסים ובדיקת כל מיקום טקסט כנגד כל התבניות.זה הופך את זה שימושי לזיהוי plagiarism, סריקה ויישומים אחרים הדורשים התאמה דפוסית מרובות.

עיצוב מקביל ו-Concurrent Algorithm Design

למעבדים מודרניים יש ליבות מרובות, מה שהופך את עיצוב אלגוריתם מקביל יותר ויותר חשוב.המקבילה יעילה יכולה לספק שיפורים ביצועים דרמטיים, אבל דורש שיקול זהיר של סינכרון, איזון עומס ודפוסי גישה לזיכרון.

מקביל אלגוריתאם

מקבילות נתונים מחלק נתונים בין חוטים, עם כל חוט המבצע את אותו פעולה על חלקו.תבנית זו עובדת היטב עבור פעולות כגון עיבוד מערך, סינון תמונות, חישוב מספרי.האתגר המרכזי הוא להבטיח חוטים לא להפריע אחד לשני באמצעות גישה משותפת לזיכרון.

מקבילות משימות מתחלקות לעבוד למשימות עצמאיות שיכולות לבצע במקביל.המקבילות מבוססת משימות יעילה כאשר פעולות הן heterogeneous או כאשר כמות העבודה עבור רכיב נתונים משתנה באופן משמעותי.

מקבילות פיפירית מחלק עיבוד לשלבים, עם חוטים שונים העוסקים בשלבים שונים.הנתונים זורם דרך הצינור, עם כל פריטי עיבוד שלב במקביל.תבנית זו יעילה לעיבוד נתונים זורם כל פריט באמצעות שלבים מרובים עיבוד.

סינכרון ו-Shale Safety

פרימיטיביזציה כמו mutexes, סמפורים, ומשתנים מצב לתאם גישה חוט משאבים משותפים. עם זאת, סינכרוניזציה מציגה מעל פני השטח ויכולה להפוך צוואר בקבוק אם חוטים לעתים קרובות לטעון עבור מנעולים. minimizing המדינה המשותפת והסנכרון הוא מפתח לביצועים מקבילים מדרג.

מבנים ללא תשלום נתונים משתמשים בפעולות אטומיות כדי לתאם גישה ללא מנעולים, הימנעות מתוכן ומסלק מתים. פעולות השוואתיות אטומיות ונפיחות מאפשרות יישום מחסניות ללא מנעולים, תורים ומבנים אחרים. בעוד מורכב יותר ליישם נכון, מבנים ללא מנעול יכולים לספק יכולת טובה יותר מאשר חלופות מבוססות מנעול.

C11 ו- C++11 מספקים תמיכה סטנדרטית של חוטים עם std:thread, std:mutex, std:atomic, ומתקנים קשורים. אלה מופשטים לספק חוט נייד תוך מתן יישום יעיל על פלטפורמות שונות.הבנת הפרימיטיביות האלה ואת המאפיינים הביצועים שלהם חיוני לתכנות מקבילה יעילה.

המונחים: Algorithm Complexity

מורכבות אלגוריתם מקבילים דורשת התחשבות הן בעבודה (פעולות אלטיות) והן לאורך (שרשרת תלותיות ארוכה ביותר) מהירות האלגוריתם המקבילה מוגבלת על ידי חוק אמהל, אשר מהווה חלקות מקבילות זמינות במבנה האלגוריתם.

החוק של Amdahl קובע כי אם חלק שבריר של עבודה חייב להיות סייטרלי, מהירות מקסימלית עם מעבדי p הוא 1 / (f + (1-f) / P) זה אומר אפילו חלקים קטנים של קיבולת מוגבלת. עיצוב אלגוריתמים למזער עבודה סיעתית הוא חיוני להשגת מהירות מקבילים טובה.

קוהרנטיות של כאב יכול להגביל את הביצועים מקבילים כאשר חוטים לעתים קרובות לגשת לנתונים משותפים.לכל הליבה יש את השפם שלה, ולשמור על כיבים עקביים דורש תקשורת. שיתוף כוזב מתרחשת כאשר חוטים גישה למשתנים שונים החולקים קו מטמון, מה שגורם תנועה קו הכפפה מיותר.

ניהול זיכרון ואלגוריגים הם יעילות

ניהול זיכרון משפיע באופן משמעותי על ביצועי האלגוריתם ב C ו- C++, הבנת ההיררכיה של הזיכרון, אסטרטגיות ההקצאה ותבניות הגישה מאפשרות כתיבת אלגוריתמים המשתמשים בזיכרון ביעילות.

הבנה של זיכרון ההיררכיה

מחשבים מודרניים יש היררכיה זיכרון עם רישומים, רמות מרובות של כאב, זיכרון הראשי, אחסון דיסק.כל רמה היא גדולה יותר אך איטי יותר מזו הקודמת. רשם לספק גישה תת-שנתי, L1 cache לוקח כמה ננו שניות, L2 cache עשרות ננו השניות, זיכרון עיקרי של ננו השניות, ו- דיסק מעבורות.

אלגוריתמים Cache-aware מתייחסים במפורש לגודל השבר והמבנה בעיצוב שלהם.אלגוריתמים חיצוניים ממזערים את הדיסק I / O על ידי עיבוד נתונים בלוקים המתאימים בזיכרון.הבנת ההיררכיה הזיכרון עוזרת למפתחים לתכנן אלגוריתמים שעובדים ביעילות בכל רמה.

מקומיות טמפלית פירושה גישה לאותו נתונים שוב ושוב בחלון זמן קצר.מקומיות ספאטי פירושה גישה לנתונים סמוכים. Algorithms עם מקומי טוב לשמור לעתים קרובות גישה לנתונים ב- cache, שיפור דרמטי בביצועים. Array traversal תערוכות מקומיות מצוינות, בעוד מצביע רודף רשימות מקושרות מציג מקומי לקוי.

המונחים: memory allocators

מכלאונים מתאימים יכולים לשפר באופן משמעותי את הביצועים עבור דפוסי הקצאה ספציפיים.פולנים פולווקרים לפני הקצאה קבועה, מתן הקצאה מהירה ועסקה ללא פיצול. Stack הקצאה מ- buffer in LIFO, המאפשר הקצאה מהירה מאוד עם סמן פשוט.

C++ מאפשר לציין את כלocators מותאם אישית עבור מכולות סטנדרטיות באמצעות פרמטרים תבנית.זה מאפשר להשתמש בכלאודורטורים מיוחדים עבור מכולות קריטיות ביצועים תוך שמירה על ממשקי מכולה סטנדרטיים.ספריית הזיכרון הפולמורפי (PMR) ב C++ מספק ממשק allocator של כלול זמני-polymorphic למשך אפילו יותר גמישות.

מיפוי זיכרון עם מ"מap מאפשר טיפול בקבצים כזיכרון, ומאפשר למערכת ההפעלה להתמודד עם עריכת דין.זה יעיל לעיבוד קבצים גדולים שאינם מתאימים לזיכרון, שכן מערכת ההפעלה באופן אוטומטי נטען חלקים הדרושים.

דפוסי הגישה של Memory Access

דפוסי גישה אפשריים ממקסימים את יעילות ה- cache על ידי טעינה קווי שפיחה אשר ישמשו במלואם.תבניות גישה אקראיות לגרום להפרעות כאבי ראש תכופות, צמצום דרמטי בביצועים.כאשר גישה אקראית היא הכרחית, טכניקות כגון חסימת או הטיירה יכולות לשפר את המקומיות על ידי עיבוד נתונים ב- cache-sizes.

דפוסי גישה מבוקשים, שבהם אתה ניגש לכל אלמנט nth, יכולים לגרום לסכסוכים מטמון ולניצול גרוע.כאשר צעדים הם כוחות של שניים, הם עשויים למפות את אותם קבוצות מטמון, מה שגורם פינויים מופרזים.

הצגת נתונים לפני שזה נחוץ יכול להסתיר את הגמישות של הזיכרון.תוכנה לפני שטף עם אינטרינואידים או חומרה prefetching עבור דפוסים צפויים הן לעזור.עם זאת, עודף פסולת פסולת רוחב פס זיכרון יכול לשחרר נתונים שימושיים מ- cache, כך זה דורש כוונון זהיר.

שיקולים של ביצועים אמיתיים

ניתוח אלגוריתם תיאורטי מספק בסיס, אבל ביצועים בעולם האמיתי תלויים גורמים רבים מעבר למורכבות אסימפטוטית.הבנת שיקולים פרקטיים אלה מסייעת לגשר על הפער בין תיאוריה ופרקטיקה.

גורמים קבועים ועלויות נסתרות

הסימון הגדול מתעלם מגורמים קבועים, אך בפועל, קבועים אלה חשובים מאוד.אלגוריתם O(n2) עם קבוע זעיר עשוי לזרז אלגוריתם O(n log n) עם קבועים גדולים עבור גדלים קלט מציאותיים.

עלויות נסתרות כמו הקצאת זיכרון, מפספסים מטמון, וטענות של ענף יכולות לשלוט בזמן ביצוע.אלגוריתם הממזער את העלויות הללו עשוי לפרט אחד עם מורכבות תיאורטית טובה יותר, הבנת מודל העלות המלא, לא רק ספירת פעולה, חיוני אופטימיזציה מעשית.

מאפיינים אינput משפיעים באופן דרמטי על הביצועים.סוגים לעומת נתונים אקראיים, נתונים עם הרבה לשכפלות לעומת כל הערכים הייחודיים, וגודל הנתונים ביחס לגודל ה- cache כל ההשפעה אשר האלגוריתם מבצע בצורה הטובה ביותר. אלגוריתמים הסתגלותיים שמתאימים את ההתנהגות בהתבסס על תכונות קלט יכולים לספק ביצועים חזקים על פני קלטות מגוונות.

אופטימיזציה ותחזוקתיות

אופטימיזציה מוקדמת מבזבזים מאמץ קוד שאינו משפיע על הביצועים הכלליים.פרופיל ראשון לזהות צווארי בקבוק אמיתיים, ואז לייעל את האזורים הספציפיים האלה.רוב הקוד אינו זקוק לאופטימיזציה אגרסיבית, וברור, קוד פשוט קל יותר לשמור ולעתים קרובות לבצע ביצועים נאותים.

כאשר אופטימיזציה היא הכרחית, מסמך מדוע וכיצד קוד מותאם.קוד אופטימיזציה הוא לעתים קרובות פחות קריא, ומשמרות עתידיות צריך להבין את ההיגיון כדי למנוע פריצות אופטימיזציה.הערות להסביר חלקים קריטיים ביצועים ואת רציונלי עבור טכניקות ספציפיות לעזור לשמור אופטימיזציה במהלך תחזוקה.

תיאור וביצועים לפעמים קונפליקטים. פונקציות וירטואליות, טיפול יוצא דופן, ותכונות אחרות ברמה גבוהה להוסיף מעל לראש, עם זאת, הם גם לשפר את ארגון הקוד ואת יכולת המשיכה.מציאת האיזון הנכון דורש הבנה הן עלויות הביצוע והן את היתרונות של יכולת שמירה של גישות שונות.

אופטימיזציה של פלטפורמה-Specific Optimizations

למעבדים שונים יש תכונות ביצועים שונות.מעבדים ARM יש מערכות הוראה שונות והיררכיה של כאבי צוואר מאשר מעבדי x86.קוד מותאם לפלטפורמה אחת לא יכול להופיע טוב על קוד נייד אחר.

הבדלים Compiler משפיעים על הביצועים באופן משמעותי. GCC, Clang, ו- MSVC אופטימיזציה שונה ולתמוך בתוספים שונים.בדיקה עם מספר רב של מדגמים מסייע להבטיח ביצועים חזקים ויכולים לחשוף הזדמנויות אופטימיזציה ספציפיים Compiler ותכונות מאפשרות אופטימיזציה לכוונון עדין עבור מדגמים ספציפיים בעת הצורך.

ההבדלים במערכת ההפעלה משפיעים על ניהול זיכרון, חוט ו- I/O ביצועים.לינוקס, Windows ו-macOS יש אונקטורים זיכרון שונים, לוח הזמנים, ומערכת להתקשר מעל ליישומים של Cross-platform חייבים לקחת בחשבון את ההבדלים הללו כדי להשיג ביצועים עקביים.

נושאים מתקדמים ב Algorithm Efficiency

מעבר למושגים הבסיסיים, מספר נושאים מתקדמים מספקים תובנות עמוקות יותר ליעילות האלגוריתמית ומאפשרים לפתור אתגרים מורכבים יותר.

ניתוח מודע

ניתוח מוקרן רואה את העלות הממוצעת של פעולות על רצף ולא עלות הגרועה ביותר של פעולות בודדות. ⁇ דינמיים מדגימים זאת: יישום רכיב בדרך כלל לוקח זמן O(1), אבל לפעמים דורש O(n) זמן כדי לשנות את גודלו. ניתוח מוקרן מראה כי העלות הממוצעת לתוספת הוא O(1) כי עלייה יקר מתרחשת באופן בלתי צפוי.

שיטת החשבונאות מקצה עלויות שונות לפעולות כגון כי העלות הכוללת שהוקצה מכסה את העלות בפועל.השיטה הפוטנציאלית מגדירה פונקציה פוטנציאלית שעולה כאשר מבצעים זולים מתרחשים וירידה כאשר מבצעים יקרים מתרחשים.

הבנת המורכבות המתוקנת מסייעת להעריך מבנים נתונים כמו מערך דינמי, עצי משחק, ואת פיבונצ'י כי יש פעולות אישיות יקרות, אבל ביצועים גבוהים ממוצע. בפועל, גבולות מאומתים לעתים קרובות טוב יותר לשקף ביצועים בפועל מאשר בעיות הגרועות ביותר.

⁇ ⁇ ⁇

אלגוריתמים Cache-Oblivious משיגים ביצועים אופטימליים של כאב מבלי לדעת פרמטרים כגון גודל או אורך קו. אלגוריתמים אלה פועלים ביעילות על פני כל היררכיה הזיכרון, מ-L1 cache לדיסק, תוך שימוש במבנים מפוצצים, ו-conquer שמתאימים באופן טבעי לגודלי מטמון שונים.

האלגוריתם של matrix-oblivious Matrix multiplication מתפצל באופן רציני לתוך quadants, עיבוד submatrices כי בסופו של דבר מתאים cache.זה משיג מורכבות מטמון אופטימלית מבלי לחסום במפורש את הגדלים המסומנים ספציפיים. אלגוריתמים Cache-oblivious מספקים ביצועים חזקים על פני תצורה חומרה שונה.

בעוד אלגוריתמים מכאובים הם אלגנטיים תיאורטית, אלגוריתמים מטמון-מודע מכוונים לגודלי שפם ספציפיים לפעמים להשיג ביצועים מעשיים טובים יותר.בחירה תלויה בשאלה אם אתה צריך ביצועים חזקים על פני חומרה מגוונת או ביצועים מקסימליים על חומרה מסוימת.

המונחים: Algorithms

בעיות חשובות רבות הן NP-Hard, כלומר לא אלגוריתם ידוע של פולינומי בזמן מציאת פתרונות אופטימליים. אלגוריתמים של Approximation מוצאים פתרונות קרובים-אופטימיים ביעילות, ומספקים גבולות אפשריים על איכות הפתרון. A 2-approximation מבטיחה פתרונות בתוך גורם של 2 של אופטימלי.

הבעיה מכסה vertex מבקש את המינימום של אותנטיות המכסה את כל הקצוות בגרף. אלגוריתם פשוט 2-approximation שוב ושוב בוחר קצה וכולל שתי נקודות קצה בכיסוי.זה פועל בזמן פולינומי ומבטיח פתרון ברוב כפול של הגודל האופטימלי.

עבור בעיות מעשיות רבות, פתרונות משוערים מספיקים.דרך שארכה 10% יותר מאופטימלית עשויה להיות מקובלת אם היא נצמדה תוך שניות ולא שעות.הבנת הקונוף בין איכות הפתרון וזמן חישובי מאפשר קבלת החלטות מושכלות לגבי כאשר אלגוריתמים של האפליקציות מתאימים.

אלגורית אלגו

אלגוריתמים אקראיים משתמשים במספרים אקראיים כדי לקבל החלטות, לעתים קרובות להשיג ביצועים טובים יותר של תיק מאשר אלגוריתמים ⁇ . Quicksort עם בחירת פיוט אקראי משיגה O(n log n) הצפויה זמן ללא קלט, הימנעות המקרה הגרוע ביותר O(n2) המתרחש עם מבחר pivot גרוע על קלט מתואם.

אלגוריתמים מונטה קרלו עשויים לייצר תוצאות לא נכונות עם הסתברות קטנה אבל לרוץ במהירות. אלגוריתמים בלאס וגאס תמיד מייצרים תוצאות נכונות אבל יש להם זמן ריצה אקראית.

אלגוריתמים אקראיים לעתים קרובות לפשט את היישום תוך מתן ביצועים צפויים מצוינים.טבלאות האש עם פונקציות hash אקראיות, מהירות אקראיות, ובדיקת פרימיות אקראית כל להפגין את הכוח של אקראיזציה.

כלים ומשאבים לניתוח Algorithm

כלים ומשאבים רבים עוזרים למפתחים לנתח ולייעל אלגוריתמים ב- C ו- C++.מינוף המשאבים האלה מאיץ את הפיתוח ומשפר את איכות הקוד.

שיטות ניתוח וחקירה

מעבר ל gprof ו Valgrind, כלים מיוחדים רבים מספקים תובנות בביצוע התוכנית. Intel VTune פרופילr מציע ניתוח מיקרוסקופי מפורט, מראה מפספסי שפם, תקלות סניף, ואירועים ביצועים נמוכים אחרים. AMD uProf מספק יכולות דומות עבור מעבדי AMD. כלים אלה מסייעים אופטימיזציה עבור ארכיטקטורות מעבדים ספציפיים.

כלי ניתוח סטטי כגון Clang Static Analyzer וכיסוי לזהות בעיות ביצועים פוטנציאליות באגים ללא ביצוע קוד. כלים אלה לזהות בעיות כמו לולאות לא יעילות, עותקים מיותרים, ודלפות זיכרון במהלך הפיתוח, לפני שהם משפיעים על ביצועי הייצור.

דוחות אופטימיזציה של Compiler מראים אילו אופטימיזציה היו מוחלים ואשר נחסמו. GCC -fopt-info ו- Clang's - דגלי Rpass מספקים מידע אופטימיזציה מפורט.הבנת מדוע משווקים לא יכולים לייעל קוד מסוים עוזר למפתחים לכתוב קוד ידידותי יותר אופטימיזציה.

המונחים:

(FLT:0) Google BenchmarkFLT:1 מספק מסגרת מקיפה עבור C++ microbenchmarking.It מטפל במכשולים נפוצים כמו אופטימיזציה של תוצאות לא בשימוש, מספק ניתוח סטטיסטי של תוצאות, ותומכת ביישומים שונים.

לכידת2 ו-Google Test, בעוד בעיקר בדיקות מסגרות, תמיכה בבדיקת מבחנים ביצועים בחבילת הבדיקה שלך מסייעת לתפוס את התוקפנות של הביצועים במהלך הפיתוח.מערכות שילוב רציף יכולות להפעיל השוואות באופן אוטומטי ולהזהיר את המפתחים להידרדרות בביצוע.

למידה משאבים

ספרי אלגוריתם קלאסיים כמו "החדירה אלגוריתמים" מאת קורימן, ליסרסון, ריבסט ושטיין מספקים כיסוי מקיף של תורת האלגוריתם. "אמנות תכנות מחשב" מאת דונלד קונות מציע תובנות עמוקות בניתוח אלגוריתם ויישום.טקסטים בסיסיים אלה נשארים עשורים רלוונטיים לאחר פרסום.

ספרים ממוקדים ביצועים כמו "Computer Systems: A Programmer's Perspective" מאת בריאןט ו- O'Hallaron מסבירים כיצד חומרה משפיעה על ביצועי תוכנה. "Optimizing Software in C++" על ידי Agner Fog מספק הדרכה מפורטת על טכניקות אופטימיזציה ברמה נמוכה.משאבים אלה לגשר על הפער בין תורת האלגוריתם וביצועים מעשיים.

משאבים מקוונים כמו FLT:0 ccpreference.comearph.comearph 1 ; מסמך C++ מורכבות הספרייה סטנדרטית מבטיחה הבנה של המאפיינים של קונטיינרים סטנדרטיים ואלגוריתמים מסייעת למפתחים להשתמש בהם ביעילות.

מסקנה: Mastering Algorithm Efficiency in C ו- C++

יעילות אלגואטריים ב C ו- C++ דורשת איזון הבנה תיאורטית עם שיקולים מעשיים.ניתוח מורכבות אסימפטוטיים מספק בסיס להשוואה של אלגוריתמים, אבל ביצועים בעולם האמיתי תלויים בגורמים קבועים, התנהגות מטמון, בדפוסי גישה לזיכרון, ומאפיינים של אופטימיזציה מוצלחת דורש פרופיל לזהות צווארי בקבוק, הבנה כיצד קוד מתורגם להנחיות מכונה, ובחירת אלגוריתמים מתאימים ומבנים לבעיות ספציפיות.

המסע לשלוט ביעילות אלגוריתמית הוא מתמשך.מעבדים מתפתחים, המציגים תכונות ביצועים חדשות והזדמנויות אופטימיזציה. שפות תכנות ופיתחים לשפר, המאפשר טכניקות אופטימיזציה חדשות.בעיות שינוי, ומציג אתגרים חדשים הדורשים גישות אלגוריתמיות חדשניות.

התחל עם קוד ברור, נכון, ולאחר מכן אופטימיזציה המבוססת על נתונים פרופיל. להבין את המורכבות התיאורטית של אלגוריתמים ואת המאפיינים המעשיים שלהם ביצועים. Leverage היטב-optimized הספר כאשר זמין, אבל להבין את האלגוריתמים הבסיסית כדי לקבל החלטות מושכלות.מאזן ביצועים עם שמירה, אופטימיזציה אגרסיבית רק כאשר פרופיל מראה את זה משנה. על ידי שילוב ידע תיאורטי עם ניסיון מעשי מדידה קפדנית, יכול ליצור ביצועים גבוהים ותוכנות C++ דרישות דורשות לעמוד בדרישות חזקות ודרישות חזקות ודרישה לעמוד בדרישות.