Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

How to edit Mathf algorithms and calculations?

Discussion in 'Scripting' started by Virenz, Oct 9, 2016.

  1. Virenz

    Virenz

    Joined:
    Mar 15, 2015
    Posts:
    17
    I was wondering if its possible to change Mathf algorithms. If not, how can i get the Mathf.Sqrt code (to make an external method and change the external method) ? The reason i want it is because right now, the Mathf.Sqrt code calculates the square root to 5 decimal places of accuracy, but i only need 3 decimal places of accuracy, so alot of processing power is wasted.
     
  2. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,376
    First an foremost, it's technically not 5 decimal places of accuracy. It's that a 'float' is returned, which can hold roughly 6 to 9 sig values (fractional or whole) depending the number (it's a binary value so it's ranges don't map well to decimal). You usually end up with about 7, 6 guaranteed. If you're calculating the square root of say 10, you get about 3.16227... the float no longer can really hold values pass that as it doesn't have the bit depth to do it (the double which is actually doing the calculation is much larger).


    So lets look at the source...


    You can always decompile the UnityEngine.dll with something like dotpeek to get a look at the general code behind the methods. Though one problem with this is that usually it's calling through to Unity itself (the C++ side, which is not source accessible), or is just wrapping around .Net/Mono methods.

    After decompiling Mathf we find that the Sqrt method is a wrapper around .Net/Mono:
    Code (csharp):
    1.  
    2.     /// <summary>
    3.     ///
    4.     /// <para>
    5.     /// Returns square root of f.
    6.     /// </para>
    7.     ///
    8.     /// </summary>
    9.     /// <param name="f"/>
    10.     public static float Sqrt(float f)
    11.     {
    12.       return (float) Math.Sqrt((double) f);
    13.     }
    14.  
    Note how actually it's using the System.Math method, which calculates on doubles. A double has even greater bit depth, so really, it's calculating way more than 5 digits of accuracy, it's calculating like 16 digits, which than get truncated down to 5 or 6.

    So it's calling through to Mono, which the source code is out there at the mono-project:
    https://github.com/mono/mono
    (technically this is the latest source, and Unity uses a slightly modified out-dated version... but we can get a general idea, I highly doubt Unity reimplemented Sqrt)

    System.Math is here:
    https://github.com/mono/mono/blob/master/mcs/class/referencesource/mscorlib/system/math.cs

    But again from the source:
    Code (csharp):
    1.  
    2. [System.Security.SecuritySafeCritical] // auto-generated
    3. [ResourceExposure(ResourceScope.None)]
    4. [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
    5. [MethodImplAttribute(MethodImplOptions.InternalCall)]
    6. public static extern double Sqrt(double d);
    7.  
    It's making a call to the internal implementation in the mono runtime, found here:
    https://github.com/mono/mono/blob/master/mono/metadata/sysmath.c

    Code (csharp):
    1.  
    2. gdouble
    3. ves_icall_System_Math_Sqrt (gdouble x)
    4. {
    5. if (x < 0)
    6. return NaN.d;
    7. return sqrt (x);
    8. }
    9.  
    Which really just calls the cmath implementation (note the include <math.h> at the top):
    http://www.cplusplus.com/reference/cmath/

    So we're actually calling a fairly robust algorithm for it.

    You're probably not going to get more efficient than this.

    Though of course, there's a good deal of overhead in the Mathf.Sqrt implementation because it:
    1) converts to double
    2) calls another method (new stack frame)
    3) calls another method (new stack frame)
    5) calculates sqrt
    6) returns back both stack frames
    7) converts to float
    8) finally returns closing last stack frame

    You're not going to overload this method either, you can't change the underlying implementation.

    BUT, you can write your own separately and just call that one. Call it CustomMath.Sqrt. You can then implement some fast but inaccurate algorithm you prefer... but I don't suspect it will really give you any major performance increase that you'd really notice.

    But maybe, at least now, you can see how you can research and dig through source code to find the answer to your original question about how it's implemented.
     
    JoeStrout, KelsoMRK, Fab4 and 8 others like this.
  3. Tzan

    Tzan

    Joined:
    Apr 5, 2009
    Posts:
    733
    Awesome answer!
     
    Virenz likes this.
  4. Virenz

    Virenz

    Joined:
    Mar 15, 2015
    Posts:
    17
    Id have to agree. Amazing answer, thanks so much! Considering i am using Sqrt millions of times per second, it is pretty important ;) Also, in the Math.h source, its implemented here:

    00361 inline double sqrt( double fValue )
    00362 {
    00363 return double( ::sqrt( fValue ) );
    00364 }

    http://ggt.sourceforge.net/html/Math_8h-source.html
     
    Last edited: Oct 9, 2016
  5. steego

    steego

    Joined:
    Jul 15, 2010
    Posts:
    967
    If you are doing a lot of square roots and it's causing performance problems, it would make a lot more sense to try to modify your algorithm to work with squares instead.
     
    Dave-Carlile, Virenz and Kiwasi like this.
  6. Virenz

    Virenz

    Joined:
    Mar 15, 2015
    Posts:
    17
    I dont want to create my own algorithm, because i have already tried it, and its actually slower than Mathf.Sqrt.

    public static float Sqrt2(float z)
    {
    if (z == 0) return 0;
    FloatIntUnion u;
    u.tmp = 0;
    float xhalf = 0.5f * z;
    u.f = z;
    u.tmp = 0x5f375a86 - (u.tmp >> 1);
    u.f = u.f * (1.5f - xhalf * u.f * u.f);
    return u.f * z;
    }

    [StructLayout(LayoutKind.Explicit)]
    private struct FloatIntUnion
    {
    [FieldOffset(0)]
    public float f;

    [FieldOffset(0)]
    public int tmp;
    }

    and then "using System.Runtime.InterlopServices". I know i could add a lookup table or something, but i am not trying to compete with the Mathf.Sqrt algorithm, because it is extremely efficient when working out square root to 16 digits of precision. All i want to do is reduce the amount of iterations of Mathf.Sqrt, because its just wasted performance. If i am working out the square root to 16 digits of precision, and then just truncating it to 7 digits of precision , what it the point of all those extra iterations? And i dont even need 7 digits of precision, i only need three! I have looked at the source for Math.h, and it looks like it points to this: https://sourceware.org/git/?p=glibc...4a2bb6324acc6be7a9c20b6521aed84193c64;hb=HEAD Which looks like it doesnt point to anything else
     
    Last edited: Oct 9, 2016
  7. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    This would be the sane path. Do you really need millions of square roots every single frame? Why not spread them out a bit with a coroutine or a thread. Or work in square space. Or just drop the overall precision of the algorithm using the roots.
     
    Virenz likes this.
  8. steego

    steego

    Joined:
    Jul 15, 2010
    Posts:
    967
    Maybe it is, but why do you care? There is a lot of wasted performance everywhere in your average game, it rarely matters at all. This sort of micro-optimization is just a waste of time unless you have finished your game and have identified this as a major performance bottleneck. Unless you see it as an academic exercise of course, then by all means have at it.

    Come to think of it, sqrt is actually an FPU instruction, so the math.c implementation is probably only used as a fallback where it's not supported. If I'm not mistaken, you are going to have a hard time beating that no matter how few digits you use.
     
    JoeStrout and Virenz like this.
  9. Virenz

    Virenz

    Joined:
    Mar 15, 2015
    Posts:
    17
    Ok then... Thanks for the replies :)
     
  10. LeftyRighty

    LeftyRighty

    Joined:
    Nov 2, 2012
    Posts:
    5,148