space-wizards / CannyFastMath

Faster implementations of various common math operations, and harmless placeholders for math operations that just forward to the originals.
MIT License
1 stars 1 forks source link

Broken math functions without SSSE3 #3

Closed PJB3005 closed 4 years ago

PJB3005 commented 4 years ago

Math functions like Abs() are completely broken when SSSE3 is not available (ARM is an example). This caused issues on both SS14 US West and raspberry.

@Tyler-IN I don't even know if you care but you should probably be aware of this. Also if you don't have any intent of maintaining this repo I'm fine with archiving/transferring them.

Tyler-IN commented 4 years ago

Which methods?

Abs implementations all have fallbacks or are pass-thru I think.

    [Pure, JbPure]
    [NonVersionable, TargetedPatchingOptOut("")]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double Abs(double f)
      => System.Math.Abs(f);

    [Pure, JbPure]
    [NonVersionable, TargetedPatchingOptOut("")]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static double Abs(double f)
      => System.Math.Abs(f);

    [Pure, JbPure]
    [NonVersionable, TargetedPatchingOptOut("")]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static float Abs(float f)
      => System.MathF.Abs(f);

    [Pure, JbPure]
    [NonVersionable, TargetedPatchingOptOut("")]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static int Abs(int a)
      => SlowMathIntegerAbs ? Ssse3.IsSupported ? AbsSsse3(a) : AbsNaive(a) : System.Math.Abs(a);

    [Pure, JbPure]
    [NonVersionable, TargetedPatchingOptOut("")]
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static long Abs(long a)
      => SlowMathIntegerAbs ? AbsNaive(a) : System.Math.Abs(a);

None of the naive implementations use intrinsics.

Tyler-IN commented 4 years ago

Narrowed it down to bad impl of AbsNaive (int and long)

private static int AbsNaive(int a)
      => a * (Selector(a < 0) | 1);

^ Fix. Need to see what the performance impact is or if it'd be better to switch to a pass-through like the others.

Tyler-IN commented 4 years ago
using System;
using System.Runtime.CompilerServices;

public class Program
{
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    private static int AbsNaive(int a)
      => a * (Selector(a < 0) | 1);

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static int Selector(bool v)
      => Unsafe.As<bool, sbyte>(ref Unsafe.AsRef(v)) * -1;

    // const compiles well
    public static void X()
    {
        Console.WriteLine(AbsNaive(3));
        Console.WriteLine(AbsNaive(-3));
    }

    // runtime *looks ok*
    public static void X(int m)
    {        
        Console.WriteLine(AbsNaive(m));
    }
}
// const compiles well
Program.X()
    L0000: sub rsp, 0x28
    L0004: mov ecx, 3
    L0009: call System.Console.WriteLine(Int32)
    L000e: mov ecx, 3
    L0013: call System.Console.WriteLine(Int32)
    L0018: nop
    L0019: add rsp, 0x28
    L001d: ret

// runtime *looks ok*
Program.X(Int32)
    L0000: sub rsp, 0x28
    L0004: test ecx, ecx
    L0006: setl al
    L0009: movzx eax, al
    L000c: neg eax
    L000e: or eax, 1
    L0011: imul ecx, eax
    L0014: call System.Console.WriteLine(Int32)
    L0019: nop
    L001a: add rsp, 0x28
    L001e: ret
Tyler-IN commented 4 years ago
CannyFastMath.Tests.PerformanceTests.AbsIntPerf(10000000)

System.Math.Abs processed 10000000 ints in 28ms
CannyFastMath.Math.Abs processed 10000000 ints in 4ms