Skip to content

Instantly share code, notes, and snippets.

@stolnikov
Created December 15, 2023 07:39
Show Gist options
  • Save stolnikov/201677aa431c2b0ca6f41727adc169b3 to your computer and use it in GitHub Desktop.
Save stolnikov/201677aa431c2b0ca6f41727adc169b3 to your computer and use it in GitHub Desktop.
Odin vs C#
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
namespace CSharpLib;
public class CSharpBench
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int[] CSharp_Bench2(int[] a, int[] b, int[] result, int size)
{
int vectorSize = Vector256<int>.Count;
var spanA = a.AsSpan();
var spanB = b.AsSpan();
var spanResult = MemoryMarshal.Cast<int, Vector256<int>>(result);
var vectorsA = MemoryMarshal.Cast<int, Vector256<int>>(spanA);
var vectorsB = MemoryMarshal.Cast<int, Vector256<int>>(spanB);
for (var i = 0; i < vectorsA.Length; i++)
{
var va = vectorsA[i];
var vb = vectorsB[i];
var mask = Vector256.LessThan(va, vb);
var vsum = Vector256.Add(va, Vector256.Multiply(vb, va));
var vdiff = Vector256.Subtract(Vector256.Multiply(va, vb), vb);
var vresult = Vector256.ConditionalSelect(mask, vsum, vdiff);
spanResult[i] = vresult;
}
var remaining = size - spanA.Length * vectorSize;
for (int i = size - remaining; i < size; i++)
{
if (a[i] < b[i])
{
result[i] = a[i] + b[i];
}
else
{
result[i] = a[i] - b[i];
}
}
return result;
}
}
@stolnikov
Copy link
Author

open System
open System.Runtime.InteropServices
open BenchmarkDotNet.Running
open FSharp.Core
open BenchmarkDotNet.Attributes
open FSharp.NativeInterop

#nowarn "9"

module Odin =

    type Slice<'a when 'a: unmanaged> =
        struct
            val Pointer: nativeptr<'a>
            val Length: nativeint

            new(ptr, l) = { Pointer = ptr; Length = nativeint l }

            member s.Item
                with get (i: int) = NativePtr.get s.Pointer i
                and set i v = NativePtr.set s.Pointer i v
        end

    module Slice =

        let create<'a when 'a: unmanaged> (arr: 'a[]) =
            use ptr = fixed arr
            Slice(ptr, arr.Length)

    [<Literal>]
    let DLLName = "Odin"

    type IntPtr = nativeptr<int>

    [<DllImport(DLLName, CallingConvention = CallingConvention.Cdecl)>]
    extern void bench1(IntPtr result, IntPtr a, IntPtr b, int32 length)

    [<DllImport(DLLName, CallingConvention = CallingConvention.Cdecl)>]
    extern void bench2(IntPtr result, IntPtr a, IntPtr b, int32 length)

    [<DllImport(DLLName, CallingConvention = CallingConvention.Cdecl)>]
    extern void bench3(IntPtr result, IntPtr a, IntPtr b, int32 length)

    type IntSlice = Slice<int>

    [<DllImport(DLLName, CallingConvention = CallingConvention.Cdecl)>]
    extern void min(IntSlice a)


type Benchmarks() =

    let rng = Random 123
    let size = 100_000
    let a = Array.init size (fun _ -> rng.Next())
    let b = Array.init size (fun _ -> rng.Next())
    let result = Array.zeroCreate size
    let oResult = Array.zeroCreate size
    let cResult = Array.zeroCreate size

    [<Benchmark>]
    member _.FSharp_Bench2() =

        (a, b)
        ||> Array.iteri2 (fun i aValue bValue ->
            if aValue < bValue then
                result[i] <- aValue + bValue * aValue
            else
                result[i] <- aValue * bValue - bValue)

        result

    [<Benchmark>]
    member _.CSharp_Bench2() =
        CSharpLib.CSharpBench.CSharp_Bench2(a, b, cResult, size)


    [<Benchmark>]
    member _.Odin_Bench2() =

        use aPtr = fixed a
        use bPtr = fixed b
        use resultPtr = fixed oResult

        Odin.bench2 (resultPtr, aPtr, bPtr, a.Length)
        oResult


let test () =

    let b = Benchmarks()
    let fsharpResult = b.FSharp_Bench2()
    let odinResult = b.Odin_Bench2()
    let csharpResult = b.CSharp_Bench2()
    printfn $"{fsharpResult = odinResult}, {odinResult = csharpResult}"

// test ()
let _ = BenchmarkRunner.Run<Benchmarks>()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment