גישות שונות להתמודדות עם Concurrency

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

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

cc by flickr, paul.orear

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

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

  • dead-locks: כאשר שני נימים (threads) מנסים לקחת משאב אשר מוחזק אצל הנים האחר, כתוצאה מכך, התוכנית אינה יכולה להתקדם יותר והיא נעצרת.
  • race-conditions: כאשר שני נימים ניגשים למשאב משותף אשר אינו מוגן, וכתוצאה מכך יכולות להגרם בעיות בנכונות התוכנית, בין השאר כתוצאה מכך שכל נים רץ על מעבד אחר. במקרים מסויימים התוכנה עשוייה להתקע.
  • הרעבה (starvation): שני נימים או יותר מתחרים על משאב משותף (ומסונכרן כהלכה). מכיוון שרק אחד מהם יכול לעדכן את המשאב המשותף בו זמנית, שאר הנימים נעצרים ונוצר מצב שבו התוכנה לא יכולה להגיע לקצב שבו היא היתה אמורה לעבוד. לעיתים מגיעים למצב שתוכנית ששוכתבה לרוץ באופן מקבילי על מספר מעבדים מציגה ביצועים נמוכים יותר מאשר התוכנית הסדרתית המקורית.

cc by flickr, Vinttutive

ישנן כמה גישות להתמודדות הדרישה לבו זמניות:

גישת Shared Mutability

זוהי בעצם הגישה הסטנדרטית והמקובלת הסובלת מכל הבעיות שצויונו לעיל. לכן היא נקראת גם גישת סנכרן וסבול (synchronize and suffer). קשה מאוד לכתוב תוכנית מרובת נימים בעלת נתונים משותפים מבלי להיות חשוף לתקלות. ככל שהתוכנה מסובכת יותר זה הופך להיות כמעט בלתי אפשרי. ב-Java ישנה ספריה בשם java.util.concurrent  שנותנת יכולות מאוד חזקות להתמודדות עם Shared Mutability כגון ExecutorService, ReadWriteLock, CompletionService ו- Fork-Join Framework.

גישת Isolated Mutability

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

גישת Pure Immutability

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

בדוגמה שלהלן ישנה רשימה המכילה שלושה איברים.

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

 

Persistent Trie הינו מבנה נתונים מורכב יותר שיותר המזכיר עץ או Hash-Map. בכל שינוי שמבצעים רק חלקים שהשתנו משוכפלים. שאר העץ לא משתנה. בעץ מאוזן בגודל n שינוי אחד ידרוש עד (log(n אובייקטים חדשים. הנתון עצמו וכל מי שמצביע עליו.

Actors

Actors היא שיכלול של גישת Isolated Mutability. כל Actor מחזיק מידע פרטי. כאשר רוצים להעביר מידע בין Actors שולחים הודעה. ההודעות אמורות להיות immutable.  התשתית מבצעת התאמות עבור ה-Actors לנימים כאשר אין צורך שכל Actor יהיה נים בעצמו. כאשר לצורך החישוב אין צורך בהעברת הודעות רבות הגישה הזאת יכולה להיות יעילה.

Software Transactional Memory – STM

בעבודה עם STM, נימים שונים חולקים זיכרון משותף. אבל בגישה שונה מאשר Shared-Mutability. הגישה הזו מאוד דומה לצורה שבה מבצעים optimistic-locking בבסיסי נתונים. כאשר מתוך תכונות ACID של בסיסי נתונים והיא מספקת את תכונות ACI במלואן (Atomicity, Consistency, Isolation כאשר אין משמעות לתכונת Durability שכן הכל נשמר רק בזיכרון הנדיף) כל פעם שאנחנו רוצים לשנות את המצב של הנתונים אנחנו בעצם מתחילים טרנזקציה משנים את כל מה שאנחנו צריכים ובסופו של התהליך מבצעים commit. במידה והיה שינוי בנתונים תוך כדי הטרנזקציה, הטרנזקציה נכשלת והתשתית דואגת לנסות מחדש מספר פעמים ובצורה חכמה.

ספריה מקובלת לעולם של Java לשימוש ב-actors ו-STM נקראת Akka והיא מגיעה במקור משפת Scala.


הפוסט נכתב על ידי מארגני כנס אורקל


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

באירוע, מוצעים 12 מסלולים ולמעלה מ-180 סמינרים, המציעים הזדמנות ייחודית לקבלת מידע עדכני לגבי האתגרים הטכנולוגיים המרתקים של עולם ה-IT בהם Big Data, אבטחת מידע וסייבר, מחשוב ענן וכן מהפכת המובייל ועידן ה-Post-PC, וירטואליזציה, ניתוח מידע מתקדם ועוד. זאת ועוד, בכנס גם תוכלו לקבל כלים באמצעות הדגמות חיות ו best practices מוכחים להתמודדות עם האתגרים העסקיים והארגוניים העומדים בפניכם  כמקצוענים – מפי אנשי המקצוע והמומחים המובילים בישראל .

אנו מזמינים אתכם להיכנס לאתר הכנס ולבחור את הסמינרים המעניינים אתכם. לצפייה במסלולי הכנס ולפרטים נוספים: http://www.oracleweek.com?BannID=1787

כתב אורח

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

הגב

Be the First to Comment!

avatar
Photo and Image Files
 
 
 
Audio and Video Files
 
 
 
Other File Types
 
 
 
wpDiscuz

תגיות לכתבה: