SearchValues<T> מציג סוף סוף יתרון מדיד ב-.NET 10 בעת סריקת באפרים (buffers) גדולים עבור מפרידים (delimiters) דלילים, כאשר SearchValues מסיים ב-3.3 מילישניות לעומת 5.6 מילישניות של IndexOfAny על מטען (payload) של 32 MB.
מדוע SearchValues הופיע
.NET 8 הציגה את SearchValues<T> כדי לחשב מראש קבוצה של תווים (או בתים) ולאפשר לזמן הריצה (runtime) לבחור את אלגוריתם הסריקה המהיר ביותר עבור אותה קבוצה. בונים את האובייקט פעם אחת, ואז משתמשים בו מחדש בכל פעם שצריך לאתר אחד מהערכים בתוך span. ה-runtime עשוי להשתמש בהוראות וקטוריות (vectorized instructions), לולאות ללא הסתעפויות (branch-free loops), או טריקים אחרים ברמה נמוכה שחלופות שנכתבו ידנית לא יכולות להשתוות אליהן ללא מאמץ רב.
מדדים (Benchmarks) שחשובים
הבדיקות שעוררו את הדיון השתמשו בבאפר של 32 MB המכיל חמישה תווים מפרידים. שלוש גישות נמדדו ב-.NET 10:
- IndexOfAny(char[]) – 10.2 ms
- לולאת foreach ידנית על ה-span – 19.4 ms
- SearchValues
– 9.7 ms
SearchValues ניצח את IndexOfAny המובנה בערך בחצי מילישנייה בלבד. התוצאה אינה טענה דרמטית של "מהיר פי חמישה" שרווחת בפורומים; .NET מודרני כבר מבצע אופטימיזציה ל-IndexOfAny עבור קבוצות קטנות, מה שמצמצם את הפער.
כאשר המפרידים נדירים – מופיעים פעם ב-40 KB במקום כל 60 בתים – התמונה משתנה:
- IndexOfAny(char[]) – 5.6 ms
- SearchValues
– 3.3 ms
כעת SearchValues מהיר פי 1.7 מכיוון שהמנוע רץ במהירות דרך רצפים ארוכים של נתונים שאינם תואמים באמצעות צעדים וקטוריים, בעוד ש-IndexOfAny חוזר לנתיב פחות אגרסיבי.
העלות הנסתרת
SearchValues אינו בחינם. בניית האובייקט מקצה זיכרון ובונת טבלאות חיפוש (lookup tables) פנימיות. אם תתקנו אותו (instantiate) בכל קריאה לפונקציה, ה-overhead יגמד כל רווח בזמן הריצה. מדד (benchmark) שקרא לשגרת הסריקה מיליון פעמים על שורות קצרות הראה:
- SearchValues סטטי (משימוש חוזר) – 25.1 ms סה"כ
- SearchValues לכל קריאה – 70.2 ms סה"כ
יצירת האובייקט בכל קריאה הופכת את הפעולה כולה לאיטית פי שלושה מאשר אי-שימוש ב-SearchValues בכלל. שמרו את המופע (instance) בשדה static readonly או השתמשו בו מחדש בין קריאות בדרך אחרת.
מתי כדאי להשתמש ב-SearchValues
- קלט גדול, מעט התאמות – הנתיב הוקטורי בולט כאשר הסורק יכול לדלג על קטעים ארוכים מבלי להיתקל במפריד.
- סריקות חוזרות עם אותה קבוצה – אם מחפשים את אותם תווים פעמים רבות, ההגדרה החד-פעמית משתלמת.
מתי להישאר עם IndexOfAny
- מחרוזות קצרות או הרבה התאמות – עלות ההגדרה הנוספת גוברת על הרווח המזערי במהירות.
- קוד שכבר משתמש ב-IndexOfAny – המימוש של .NET מודרני כבר מכויל היטב עבור קבוצות תווים קטנות, כך שהחלפה ל-
SearchValuesעשויה שלא להשפי
