Skip to content

Instantly share code, notes, and snippets.

@neon-sunset
Last active December 2, 2023 22:47
Show Gist options
  • Save neon-sunset/e2c23aa76bf4080f92a46633acc8863c to your computer and use it in GitHub Desktop.
Save neon-sunset/e2c23aa76bf4080f92a46633acc8863c to your computer and use it in GitHub Desktop.
// This is an adapted port of Haversine + SIMD implementation by Ash Vardanian
// Reference: https://github.com/ashvardanian/HaversineSimSIMD/blob/main/4.c
using System.Diagnostics;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
var timestamp = Stopwatch.GetTimestamp();
var coords = (Span<(float Lat, float Lon)>)
[
(40.7128f, -74.0060f),
(42.3601f, -71.0589f)
];
var a = 0.0f;
for (var it = 0; it < 1_000_000_000; it++)
{
var (lat1, lon1) = ElementAt(coords, it % 2);
var (lat2, lon2) = ElementAt(coords, (it + 1) % 2);
a = Haversine(lat1, lon1, lat2, lon2);
}
var distance = MathF.Asin(MathF.Sqrt(a)) * 2 * 6371000f;
Console.WriteLine(
$"Distance: {distance} meters\n" +
$"Elapsed: {Stopwatch.GetElapsedTime(timestamp).TotalSeconds}s");
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static float Haversine(float lat1, float lon1, float lat2, float lon2)
{
const float PI = 3.14159265358979323846f;
var data = new Vector4(lat1, lon1, lat2, lon2);
data *= PI / 180.0f;
// Under the hood, Vector4 is pretty much Vector128<float> which
// means it's an immutable primitive stored in a SIMD register.
// Therefore, we must deconstruct it first before mutating,
// otherwise it will be re-spilling on every element change.
var (e0, e1, e2, e3) = (
data[0], data[1], data[2], data[3]);
data[2] = e0 - e2;
data[3] = e1 - e3;
data[0] = e0 + e2;
data *= 0.5f;
static Vector4 One() => Vector4.One;
var xsqr = data * data;
var s = One();
s = One() - (xsqr * s * 0.023809523809523808f);
s = One() - (xsqr * s * 0.05f);
s = One() - (xsqr * s * 0.16666666666666666f);
s *= data;
s *= s;
return s[2] + ((1 - s[2] - s[0]) * s[3]);
}
static ref readonly T ElementAt<T>(Span<T> span, int index) =>
ref Unsafe.Add(ref MemoryMarshal.GetReference(span), index);
@neon-sunset
Copy link
Author

Makefile:

all:
	dotnet publish -o . -p:PublishAot=true -p:IlcInstructionSet=native -p:OptimizationPreference=Speed

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