C# מדוע החלוקה איטית יותר מכפל?

בשפות תכנות ובאופן ספציפי C# ישנן 4 פעולות אריתמטיות שניתן לבצע: חיבור, חיסור, כפל וחילוק.

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

מי מהם איטי יותר אתם עשויים לשאול? חטיבה.

לפי נייר HP זה:

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

כדי לאמת את ההצהרה למעלה החלטתי להריץ בדיקה פשוטה באמצעות הקוד שלהלן:

        //Generate two random numbers
        var rand = new System.Random();
        float a = rand.Next();
        float b = rand.Next();

        Debug.Log("Number a: " + a + " Number b: " + b);

        System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();

        watch.Start();
        //Addition
        for (int i = 1; i < 1000000; i++)
        {
            float tmp = a + b;
        }
        watch.Stop();
        //Output
        Debug.Log("Addition took: " + watch.Elapsed.TotalSeconds.ToString("0.0000") + " seconds");

        watch.Reset();
        watch.Start();
        //Subtraction
        for (int i = 1; i < 1000000; i++)
        {
            float tmp = a - b;
        }
        watch.Stop();
        //Output
        Debug.Log("Subtraction took: " + watch.Elapsed.TotalSeconds.ToString("0.0000") + " seconds");

        watch.Reset();
        watch.Start();
        //Multiplication
        for (int i = 1; i < 1000000; i++)
        {
            float tmp = a * b;
        }
        watch.Stop();
        //Output
        Debug.Log("Multiplication took: " + watch.Elapsed.TotalSeconds.ToString("0.0000") + " seconds");

        watch.Reset();
        watch.Start();
        //Division
        for (int i = 1; i < 1000000; i++)
        {
            float tmp = a / b;
        }
        watch.Stop();
        //Division
        Debug.Log("Division took: " + watch.Elapsed.TotalSeconds.ToString("0.0000") + " seconds");

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

  • הוספה בממוצע ארכה 0.0004 שניות
  • חיסור בממוצע ארך 0.0003 שניות
  • הכפלה בממוצע ארכה 0.0003 שניות
  • החלוקה בממוצע ארכה 0.0044 שניות

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

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