מימוש Stream reader and writer בעצמי - תכנות - HWzone פורומים
עבור לתוכן
  • צור חשבון

מימוש Stream reader and writer בעצמי


kutomer

Recommended Posts

ערב טוב,

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

מה שמן הסתם מיד מקפיץ את הפחד מ"Out.Of.Memory" באופן עקרוני אני אמור לכתוב לאובייקט באופן קבוע ובמקביל גם לקרוא ממנו.

לכן במקום להשתמש בbinary serialize הפשוט שאני משתמש בו כרגע חשבתי על לממש כמו xmlReader\Writer בעצמי לעץ שלי (בעיקר כי מבחינה לוגית זה נשמע מתאים, מעץ בניגוד להרבה אובייקט אחרים אפשר להפיק נתונים ולהשתמש בו מבלי לקרוא את כולו).

רק שאין לי שמץ איך עושים את זה.

אשמח לעזרתכם בנושא, תודה מראש תומר :)

קישור לתוכן
שתף באתרים אחרים

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

כלומר, יהיה משהו בסגנון הזה:


class Node
{
int nodeId;
bool isLoaded = false;
bool isDirty = false;

List<Node> children;
int data;

void LoadFromDisk()
{
if (isLoaded)
return;

// do whatever operations needed to load the node from the disk...

isLoaded = true;
isDirty = false;
}

void WriteToDisk()
{
if (!isDirty)
return;

// do whatever operations needed to write the node to the disk...

isDirty = false;
}

public int Data
{
get
{
LoadFromDisk();
return data;
}
set
{
LoadFromDisk();
data = value;
isDirty = true;
}
}

public ReadOnlyCollection<Node> Children
{
get
{
LoadFromDisk();
return children.AsReadOnly();
}
}

// some more operations here, such as adding/removing children
}

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

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

באותו אופן, הפונקציה WriteToDisk שומרת את האובייקט לדיסק, רק אם הוא השתנה. יש לדאוג לכך ש-isDirty יודלק כאשר מתבצע שינוי כלשהו באובייקט (לדוגמה, אם אנחנו משנים את ה-Data, או שאנחנו מוסיפים\מורידים ילדים). יש לקרוא לפונקציה הזו כשסיימנו לעבוד עם ה-Node ואנחנו רוצים לשחרר אותו מהזכרון (אם רוצים להיות מאוד פדנטים אפשר להגדיר ש-Node יממש את הממשק IDisposable, ולקרוא ל-WriteToDisk מתוך פונקציית ה-Dispose).

שים לב שאת Children החזרנו כ-ReadOnlyCollection, כי אם היינו מחזירים אותו כ-List אז היה אפשר לשנות אותו מחוץ לאובייקט, וזה לא טוב (כי אז יהיה אפשר לבצע בו שינויים בלי ש-isDirty ידלק).

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

קישור לתוכן
שתף באתרים אחרים

^ רגע אז כל אלמט ידע לשמור\לטעון את עצמו? למה? לא עדיף לתת אינדקס לתוך model או משהו?

אם אחרי כל gett אתה ניגש ישר לדיסק, זה כמו לקנות חלב בשוויץ. אם לא אז יש לך מילא cache

חיצוני איפשהו אז למה כל הבלאגן הזה? לכתוב אחרי כל שינוי, למה? אחי, IO זה חתיכת no no לביצועים!

זאת אחת הסיבות שיש caching במנועי DB, להימנע מכל האיכסה הזאת! בעע! חוץ מזה איפה פה הAPI

שמספק פונקצינליות להכנת קפה! קיצר, זה כל כך 2011 הקוד הזה! ;D

תומר, לדעתי אתה חושב בכיוון הלא נכון. אני הייתי הולך על לבנות interface כלשהו ולעבוד מולו.

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

של MVC, אבל לא בדיוק... זה יותר כמו MV וה controller בתוך ה view. טוב, בערך...

אתה כמובן, לא חייב משהו כלכך מסיבי כמו model view מבוסס טבלאות.

interface פשוט יותר כנראה יעשה אתה העבודה, החלק החשוב פה הוא שאתה קונה את החופש לעשות טעיות.

פלוס יש לך מקום אחד שדרכו הכל עובר, אז ליישם locking הופך למשחק ילדים... וגם caching.

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

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

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

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

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

אתה חופשי לעשות אופטימיזציות.

קישור לתוכן
שתף באתרים אחרים

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

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

קישור לתוכן
שתף באתרים אחרים

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

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

vitali_pe:

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

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

(עברתי על המאמר שצירפת).

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

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

הרשמתם אותי מאוד.

קישור לתוכן
שתף באתרים אחרים

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

אוקי... פשוט כשאני רואה loadFromDisk אני מניח שה load הוא מ ה disk :) + יש את ה dirty bit ...

תחשוב על זה שניה, מה אם אני רוצה 2 עותקים של אותו node ?

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

אפשר? כן, חכם? לא כלכך, לא. שמעת על principle of least astonishment ?

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

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

(עברתי על המאמר שצירפת).

[/r]

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

אבל הנה כמה רעיונות:

- תחשוב על Node כ index, אנ מציע כשתשמור אותם פשוטים וקלים.

- אתה מקבל nodeים מ ה model שלך

- ה model שלך לא חייב להיות טבלה אתה יכול לחשוף interface של עץ ולממש איך שבא לך

- אל תדאג לגבי cache, ברגע שיש לך ארכטקטורה נורמאלית, אתה יכול ליישם מה שמתאים אחרי שאתה מודד ביצועים -> premature optimization is the root of all evil...

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

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

הבנת? או שאתה רוצה דוגמה מהירה בקוד ?

קישור לתוכן
שתף באתרים אחרים

ארכיון

דיון זה הועבר לארכיון ולא ניתן להוסיף בו תגובות חדשות.

×
  • צור חדש...