Created
December 14, 2025 09:55
-
-
Save hez2010/289c0b6242d1aca0d6171caa97a2bcd8 to your computer and use it in GitHub Desktop.
benchmarks.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Runtime.CompilerServices; | |
| using System.Runtime.InteropServices; | |
| public class Benchmarks | |
| { | |
| private static unsafe void* Malloc(nuint size, nuint alignment) | |
| { | |
| return NativeMemory.AlignedAlloc(size, alignment); | |
| } | |
| private static unsafe void Free(void* pointer) | |
| { | |
| NativeMemory.AlignedFree(pointer); | |
| } | |
| private interface IJob | |
| { | |
| void Run(); | |
| } | |
| // Fibonacci | |
| public struct FibonacciNET : IJob | |
| { | |
| public uint number; | |
| public uint result; | |
| public void Run() | |
| { | |
| result = Fibonacci(number); | |
| } | |
| private uint Fibonacci(uint number) | |
| { | |
| if (number <= 1) | |
| return 1; | |
| return Fibonacci(number - 1) + Fibonacci(number - 2); | |
| } | |
| } | |
| // Mandelbrot | |
| public struct MandelbrotNET : IJob | |
| { | |
| public uint width; | |
| public uint height; | |
| public uint iterations; | |
| public float result; | |
| public void Run() | |
| { | |
| result = Mandelbrot(width, height, iterations); | |
| } | |
| private float Mandelbrot(uint width, uint height, uint iterations) | |
| { | |
| float data = 0.0f; | |
| for (uint i = 0; i < iterations; i++) | |
| { | |
| float | |
| left = -2.1f, | |
| right = 1.0f, | |
| top = -1.3f, | |
| bottom = 1.3f, | |
| deltaX = (right - left) / width, | |
| deltaY = (bottom - top) / height, | |
| coordinateX = left; | |
| for (uint x = 0; x < width; x++) | |
| { | |
| float coordinateY = top; | |
| for (uint y = 0; y < height; y++) | |
| { | |
| float workX = 0; | |
| float workY = 0; | |
| int counter = 0; | |
| while (counter < 255 && MathF.Sqrt((workX * workX) + (workY * workY)) < 2.0f) | |
| { | |
| counter++; | |
| float newX = (workX * workX) - (workY * workY) + coordinateX; | |
| workY = 2 * workX * workY + coordinateY; | |
| workX = newX; | |
| } | |
| data = workX + workY; | |
| coordinateY += deltaY; | |
| } | |
| coordinateX += deltaX; | |
| } | |
| } | |
| return data; | |
| } | |
| } | |
| // NBody | |
| private struct NBody | |
| { | |
| public double x, y, z, vx, vy, vz, mass; | |
| } | |
| public unsafe struct NBodyNET : IJob | |
| { | |
| public uint advancements; | |
| public double result; | |
| public void Run() | |
| { | |
| result = NBody(advancements); | |
| } | |
| private double NBody(uint advancements) | |
| { | |
| NBody* sun = stackalloc NBody[5]; | |
| NBody* end = sun + 4; | |
| InitializeBodies(sun, end); | |
| Energy(sun, end); | |
| while (--advancements > 0) | |
| { | |
| Advance(sun, end, 0.01); | |
| } | |
| Energy(sun, end); | |
| return sun[0].x + sun[0].y; | |
| } | |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |
| private void InitializeBodies(NBody* sun, NBody* end) | |
| { | |
| const double pi = 3.141592653589793; | |
| const double solarMass = 4 * pi * pi; | |
| const double daysPerYear = 365.24; | |
| unchecked | |
| { | |
| sun[1] = new NBody | |
| { // Jupiter | |
| x = 4.84143144246472090e+00, | |
| y = -1.16032004402742839e+00, | |
| z = -1.03622044471123109e-01, | |
| vx = 1.66007664274403694e-03 * daysPerYear, | |
| vy = 7.69901118419740425e-03 * daysPerYear, | |
| vz = -6.90460016972063023e-05 * daysPerYear, | |
| mass = 9.54791938424326609e-04 * solarMass | |
| }; | |
| sun[2] = new NBody | |
| { // Saturn | |
| x = 8.34336671824457987e+00, | |
| y = 4.12479856412430479e+00, | |
| z = -4.03523417114321381e-01, | |
| vx = -2.76742510726862411e-03 * daysPerYear, | |
| vy = 4.99852801234917238e-03 * daysPerYear, | |
| vz = 2.30417297573763929e-05 * daysPerYear, | |
| mass = 2.85885980666130812e-04 * solarMass | |
| }; | |
| sun[3] = new NBody | |
| { // Uranus | |
| x = 1.28943695621391310e+01, | |
| y = -1.51111514016986312e+01, | |
| z = -2.23307578892655734e-01, | |
| vx = 2.96460137564761618e-03 * daysPerYear, | |
| vy = 2.37847173959480950e-03 * daysPerYear, | |
| vz = -2.96589568540237556e-05 * daysPerYear, | |
| mass = 4.36624404335156298e-05 * solarMass | |
| }; | |
| sun[4] = new NBody | |
| { // Neptune | |
| x = 1.53796971148509165e+01, | |
| y = -2.59193146099879641e+01, | |
| z = 1.79258772950371181e-01, | |
| vx = 2.68067772490389322e-03 * daysPerYear, | |
| vy = 1.62824170038242295e-03 * daysPerYear, | |
| vz = -9.51592254519715870e-05 * daysPerYear, | |
| mass = 5.15138902046611451e-05 * solarMass | |
| }; | |
| double vx = 0, vy = 0, vz = 0; | |
| for (NBody* planet = sun + 1; planet <= end; ++planet) | |
| { | |
| double mass = planet->mass; | |
| vx += planet->vx * mass; | |
| vy += planet->vy * mass; | |
| vz += planet->vz * mass; | |
| } | |
| sun->mass = solarMass; | |
| sun->vx = vx / -solarMass; | |
| sun->vy = vy / -solarMass; | |
| sun->vz = vz / -solarMass; | |
| } | |
| } | |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |
| private void Energy(NBody* sun, NBody* end) | |
| { | |
| unchecked | |
| { | |
| double e = 0.0; | |
| for (NBody* bi = sun; bi <= end; ++bi) | |
| { | |
| double | |
| imass = bi->mass, | |
| ix = bi->x, | |
| iy = bi->y, | |
| iz = bi->z, | |
| ivx = bi->vx, | |
| ivy = bi->vy, | |
| ivz = bi->vz; | |
| e += 0.5 * imass * (ivx * ivx + ivy * ivy + ivz * ivz); | |
| for (NBody* bj = bi + 1; bj <= end; ++bj) | |
| { | |
| double | |
| jmass = bj->mass, | |
| dx = ix - bj->x, | |
| dy = iy - bj->y, | |
| dz = iz - bj->z; | |
| e -= imass * jmass / Math.Sqrt(dx * dx + dy * dy + dz * dz); | |
| } | |
| } | |
| } | |
| } | |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |
| private double GetD2(double dx, double dy, double dz) | |
| { | |
| double d2 = dx * dx + dy * dy + dz * dz; | |
| return d2 * Math.Sqrt(d2); | |
| } | |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |
| private void Advance(NBody* sun, NBody* end, double distance) | |
| { | |
| unchecked | |
| { | |
| for (NBody* bi = sun; bi < end; ++bi) | |
| { | |
| double | |
| ix = bi->x, | |
| iy = bi->y, | |
| iz = bi->z, | |
| ivx = bi->vx, | |
| ivy = bi->vy, | |
| ivz = bi->vz, | |
| imass = bi->mass; | |
| for (NBody* bj = bi + 1; bj <= end; ++bj) | |
| { | |
| double | |
| dx = bj->x - ix, | |
| dy = bj->y - iy, | |
| dz = bj->z - iz, | |
| jmass = bj->mass, | |
| mag = distance / GetD2(dx, dy, dz); | |
| bj->vx = bj->vx - dx * imass * mag; | |
| bj->vy = bj->vy - dy * imass * mag; | |
| bj->vz = bj->vz - dz * imass * mag; | |
| ivx = ivx + dx * jmass * mag; | |
| ivy = ivy + dy * jmass * mag; | |
| ivz = ivz + dz * jmass * mag; | |
| } | |
| bi->vx = ivx; | |
| bi->vy = ivy; | |
| bi->vz = ivz; | |
| bi->x = ix + ivx * distance; | |
| bi->y = iy + ivy * distance; | |
| bi->z = iz + ivz * distance; | |
| } | |
| end->x = end->x + end->vx * distance; | |
| end->y = end->y + end->vy * distance; | |
| end->z = end->z + end->vz * distance; | |
| } | |
| } | |
| } | |
| // Sieve of Eratosthenes | |
| [InlineArray(1024)] | |
| struct Flag { private byte Elem; } | |
| public unsafe struct SieveOfEratosthenesNET : IJob | |
| { | |
| public uint iterations; | |
| public uint result; | |
| public void Run() | |
| { | |
| result = SieveOfEratosthenes(iterations); | |
| } | |
| private uint SieveOfEratosthenes(uint iterations) | |
| { | |
| const int size = 1024; | |
| var flags = new Flag(); | |
| int a, b, c, prime; | |
| uint count = 0; | |
| for (a = 1; a <= iterations; a++) | |
| { | |
| count = 0; | |
| for (b = 0; b < size; b++) | |
| { | |
| flags[b] = 1; // True | |
| } | |
| for (b = 0; b < size; b++) | |
| { | |
| if (flags[b] == 1) | |
| { | |
| prime = b + b + 3; | |
| c = b + prime; | |
| while (c < size) | |
| { | |
| flags[c] = 0; // False | |
| c += prime; | |
| } | |
| count++; | |
| } | |
| } | |
| } | |
| return count; | |
| } | |
| } | |
| // Pixar Raytracer | |
| private struct Vector3 { public float X, Y, Z; } | |
| public enum PixarRayHit | |
| { | |
| None = 0, | |
| Letter = 1, | |
| Wall = 2, | |
| Sun = 3 | |
| } | |
| [InlineArray(2)] | |
| struct Curves { private Vector3 Elem; } | |
| [InlineArray(60)] | |
| struct Letters { private byte Elem; } | |
| public unsafe struct PixarRaytracerNET : IJob | |
| { | |
| public uint width; | |
| public uint height; | |
| public uint samples; | |
| public float result; | |
| public void Run() | |
| { | |
| result = PixarRaytracer(width, height, samples); | |
| } | |
| private uint marsagliaZ, marsagliaW; | |
| private float PixarRaytracer(uint width, uint height, uint samples) | |
| { | |
| marsagliaZ = 666; | |
| marsagliaW = 999; | |
| Vector3 position = new Vector3 { X = -22.0f, Y = 5.0f, Z = 25.0f }; | |
| Vector3 goal = new Vector3 { X = -3.0f, Y = 4.0f, Z = 0.0f }; | |
| goal = Add(Inverse(goal), MultiplyFloat(position, -1.0f)); | |
| Vector3 left = new Vector3 { X = goal.Z, Y = 0, Z = goal.X }; | |
| left = MultiplyFloat(Inverse(left), 1.0f / width); | |
| Vector3 up = Cross(goal, left); | |
| Vector3 color = default(Vector3); | |
| Vector3 adjust = default(Vector3); | |
| for (uint y = height; y > 0; y--) | |
| { | |
| for (uint x = width; x > 0; x--) | |
| { | |
| for (uint p = samples; p > 0; p--) | |
| { | |
| color = Add(color, Trace(position, Add(Inverse(MultiplyFloat(Add(goal, left), x - width / 2 + Random())), MultiplyFloat(up, y - height / 2 + Random())))); | |
| } | |
| color = MultiplyFloat(color, (1.0f / samples) + 14.0f / 241.0f); | |
| adjust = AddFloat(color, 1.0f); | |
| color = new Vector3 | |
| { | |
| X = color.X / adjust.X, | |
| Y = color.Y / adjust.Y, | |
| Z = color.Z / adjust.Z | |
| }; | |
| color = MultiplyFloat(color, 255.0f); | |
| } | |
| } | |
| return color.X + color.Y + color.Z; | |
| } | |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |
| private Vector3 Multiply(Vector3 left, Vector3 right) | |
| { | |
| left.X *= right.X; | |
| left.Y *= right.Y; | |
| left.Z *= right.Z; | |
| return left; | |
| } | |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |
| private Vector3 MultiplyFloat(Vector3 vector, float value) | |
| { | |
| vector.X *= value; | |
| vector.Y *= value; | |
| vector.Z *= value; | |
| return vector; | |
| } | |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |
| private float Modulus(Vector3 left, Vector3 right) | |
| { | |
| return left.X * right.X + left.Y * right.Y + left.Z * right.Z; | |
| } | |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |
| private float ModulusSelf(Vector3 vector) | |
| { | |
| return vector.X * vector.X + vector.Y * vector.Y + vector.Z * vector.Z; | |
| } | |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |
| private Vector3 Inverse(Vector3 vector) | |
| { | |
| return MultiplyFloat(vector, 1 / MathF.Sqrt(ModulusSelf(vector))); | |
| } | |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |
| private Vector3 Add(Vector3 left, Vector3 right) | |
| { | |
| left.X += right.X; | |
| left.Y += right.Y; | |
| left.Z += right.Z; | |
| return left; | |
| } | |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |
| private Vector3 AddFloat(Vector3 vector, float value) | |
| { | |
| vector.X += value; | |
| vector.Y += value; | |
| vector.Z += value; | |
| return vector; | |
| } | |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |
| private Vector3 Cross(Vector3 to, Vector3 from) | |
| { | |
| Vector3 vector = default(Vector3); | |
| vector.X = to.Y * from.Z - to.Z * from.Y; | |
| vector.Y = to.Z * from.X - to.X * from.Z; | |
| vector.Z = to.X * from.Y - to.Y * from.X; | |
| return vector; | |
| } | |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |
| private float Min(float left, float right) | |
| { | |
| return left < right ? left : right; | |
| } | |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |
| private float BoxTest(Vector3 position, Vector3 lowerLeft, Vector3 upperRight) | |
| { | |
| lowerLeft = MultiplyFloat(Add(position, lowerLeft), -1); | |
| upperRight = MultiplyFloat(Add(upperRight, position), -1); | |
| return -Min(Min(Min(lowerLeft.X, upperRight.X), Min(lowerLeft.Y, upperRight.Y)), Min(lowerLeft.Z, upperRight.Z)); | |
| } | |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |
| private float Random() | |
| { | |
| marsagliaZ = 36969 * (marsagliaZ & 65535) + (marsagliaZ >> 16); | |
| marsagliaW = 18000 * (marsagliaW & 65535) + (marsagliaW >> 16); | |
| return ((marsagliaZ << 16) + marsagliaW) * 2.0f / 10000000000.0f; | |
| } | |
| private float Sample(Vector3 position, int* hitType) | |
| { | |
| const int size = 60; | |
| float distance = 1e9f; | |
| Vector3 f = position; | |
| var letters = new Letters(); | |
| // P // I // X // A // R | |
| letters[0] = 53; letters[12] = 65; letters[24] = 73; letters[32] = 85; letters[44] = 97; letters[56] = 99; | |
| letters[1] = 79; letters[13] = 79; letters[25] = 79; letters[33] = 79; letters[45] = 79; letters[57] = 87; | |
| letters[2] = 53; letters[14] = 69; letters[26] = 81; letters[34] = 89; letters[46] = 97; letters[58] = 105; | |
| letters[3] = 95; letters[15] = 79; letters[27] = 95; letters[35] = 95; letters[47] = 95; letters[59] = 79; | |
| letters[4] = 53; letters[16] = 67; letters[28] = 73; letters[36] = 89; letters[48] = 97; | |
| letters[5] = 87; letters[17] = 79; letters[29] = 95; letters[37] = 95; letters[49] = 87; | |
| letters[6] = 57; letters[18] = 67; letters[30] = 81; letters[38] = 93; letters[50] = 101; | |
| letters[7] = 87; letters[19] = 95; letters[31] = 79; letters[39] = 79; letters[51] = 87; | |
| letters[8] = 53; letters[20] = 65; letters[40] = 87; letters[52] = 97; | |
| letters[9] = 95; letters[21] = 95; letters[41] = 87; letters[53] = 95; | |
| letters[10] = 57; letters[22] = 69; letters[42] = 91; letters[54] = 101; | |
| letters[11] = 95; letters[23] = 95; letters[43] = 87; letters[55] = 95; | |
| f.Z = 0.0f; | |
| for (int i = 0; i < size; i += 4) | |
| { | |
| Vector3 begin = MultiplyFloat(new Vector3 { X = letters[i] - 79.0f, Y = letters[i + 1] - 79.0f, Z = 0.0f }, 0.5f); | |
| Vector3 e = Add(MultiplyFloat(new Vector3 { X = letters[i + 2] - 79.0f, Y = letters[i + 3] - 79.0f, Z = 0.0f }, 0.5f), MultiplyFloat(begin, -1.0f)); | |
| Vector3 o = MultiplyFloat(Add(f, MultiplyFloat(Add(begin, e), Min(-Min(Modulus(MultiplyFloat(Add(begin, f), -1.0f), e) / ModulusSelf(e), 0.0f), 1.0f))), -1.0f); | |
| distance = Min(distance, ModulusSelf(o)); | |
| } | |
| distance = MathF.Sqrt(distance); | |
| var curves = new Curves(); | |
| curves[0] = new Vector3 { X = -11.0f, Y = 6.0f, Z = 0.0f }; | |
| curves[1] = new Vector3 { X = 11.0f, Y = 6.0f, Z = 0.0f }; | |
| for (int i = 1; i >= 0; i--) | |
| { | |
| Vector3 o = Add(f, MultiplyFloat(curves[i], -1.0f)); | |
| float m = 0.0f; | |
| if (o.X > 0.0f) | |
| { | |
| m = MathF.Abs(MathF.Sqrt(ModulusSelf(o)) - 2.0f); | |
| } | |
| else | |
| { | |
| if (o.Y > 0.0f) | |
| o.Y += -2.0f; | |
| else | |
| o.Y += 2.0f; | |
| o.Y += MathF.Sqrt(ModulusSelf(o)); | |
| } | |
| distance = Min(distance, m); | |
| } | |
| distance = MathF.Pow(MathF.Pow(distance, 8.0f) + MathF.Pow(position.Z, 8.0f), 0.125f) - 0.5f; | |
| *hitType = (int)PixarRayHit.Letter; | |
| float roomDistance = Min(-Min(BoxTest(position, new Vector3 { X = -30.0f, Y = -0.5f, Z = -30.0f }, new Vector3 { X = 30.0f, Y = 18.0f, Z = 30.0f }), BoxTest(position, new Vector3 { X = -25.0f, Y = -17.5f, Z = -25.0f }, new Vector3 { X = 25.0f, Y = 20.0f, Z = 25.0f })), BoxTest(new Vector3 { X = MathF.Abs(position.X) % 8, Y = position.Y, Z = position.Z }, new Vector3 { X = 1.5f, Y = 18.5f, Z = -25.0f }, new Vector3 { X = 6.5f, Y = 20.0f, Z = 25.0f })); | |
| if (roomDistance < distance) | |
| { | |
| distance = roomDistance; | |
| *hitType = (int)PixarRayHit.Wall; | |
| } | |
| float sun = 19.9f - position.Y; | |
| if (sun < distance) | |
| { | |
| distance = sun; | |
| *hitType = (int)PixarRayHit.Sun; | |
| } | |
| return distance; | |
| } | |
| private int RayMarching(Vector3 origin, Vector3 direction, Vector3* hitPosition, Vector3* hitNormal) | |
| { | |
| int hitType = (int)PixarRayHit.None; | |
| int noHitCount = 0; | |
| float distance = 0.0f; | |
| for (float i = 0; i < 100; i += distance) | |
| { | |
| *hitPosition = MultiplyFloat(Add(origin, direction), i); | |
| distance = Sample(*hitPosition, &hitType); | |
| if (distance < 0.01f || ++noHitCount > 99) | |
| { | |
| *hitNormal = Inverse(new Vector3 { X = Sample(Add(*hitPosition, new Vector3 { X = 0.01f, Y = 0.0f, Z = 0.0f }), &noHitCount) - distance, Y = Sample(Add(*hitPosition, new Vector3 { X = 0.0f, Y = 0.01f, Z = 0.0f }), &noHitCount) - distance, Z = Sample(Add(*hitPosition, new Vector3 { X = 0.0f, Y = 0.0f, Z = 0.01f }), &noHitCount) - distance }); | |
| return hitType; | |
| } | |
| } | |
| return (int)PixarRayHit.None; | |
| } | |
| private Vector3 Trace(Vector3 origin, Vector3 direction) | |
| { | |
| Vector3 | |
| sampledPosition = new Vector3 { X = 1.0f, Y = 1.0f, Z = 1.0f }, | |
| normal = new Vector3 { X = 1.0f, Y = 1.0f, Z = 1.0f }, | |
| color = new Vector3 { X = 1.0f, Y = 1.0f, Z = 1.0f }, | |
| attenuation = new Vector3 { X = 1.0f, Y = 1.0f, Z = 1.0f }, | |
| lightDirection = Inverse(new Vector3 { X = 0.6f, Y = 0.6f, Z = 1.0f }); | |
| for (int bounce = 3; bounce > 0; bounce--) | |
| { | |
| PixarRayHit hitType = (PixarRayHit)RayMarching(origin, direction, &sampledPosition, &normal); | |
| switch (hitType) | |
| { | |
| case PixarRayHit.None: | |
| break; | |
| case PixarRayHit.Letter: | |
| { | |
| direction = MultiplyFloat(Add(direction, normal), Modulus(normal, direction) * -2.0f); | |
| origin = MultiplyFloat(Add(sampledPosition, direction), 0.1f); | |
| attenuation = MultiplyFloat(attenuation, 0.2f); | |
| break; | |
| } | |
| case PixarRayHit.Wall: | |
| { | |
| float | |
| incidence = Modulus(normal, lightDirection), | |
| p = 6.283185f * Random(), | |
| c = Random(), | |
| s = MathF.Sqrt(1.0f - c), | |
| g = normal.Z < 0 ? -1.0f : 1.0f, | |
| u = -1.0f / (g + normal.Z), | |
| v = normal.X * normal.Y * u; | |
| direction = Add(Add(new Vector3 { X = v, Y = g + normal.Y * normal.Y * u, Z = -normal.Y * (MathF.Cos(p) * s) }, new Vector3 { X = 1.0f + g * normal.X * normal.X * u, Y = g * v, Z = -g * normal.X }), MultiplyFloat(normal, MathF.Sqrt(c))); | |
| origin = MultiplyFloat(Add(sampledPosition, direction), 0.1f); | |
| attenuation = MultiplyFloat(attenuation, 0.2f); | |
| if (incidence > 0 && RayMarching(MultiplyFloat(Add(sampledPosition, normal), 0.1f), lightDirection, &sampledPosition, &normal) == (int)PixarRayHit.Sun) | |
| color = MultiplyFloat(Multiply(Add(color, attenuation), new Vector3 { X = 500.0f, Y = 400.0f, Z = 100.0f }), incidence); | |
| break; | |
| } | |
| case PixarRayHit.Sun: | |
| { | |
| color = Multiply(Add(color, attenuation), new Vector3 { X = 50.0f, Y = 80.0f, Z = 100.0f }); | |
| goto escape; | |
| } | |
| } | |
| } | |
| escape: | |
| return color; | |
| } | |
| } | |
| // Fireflies Flocking | |
| private struct Boid | |
| { | |
| public Vector3 position, velocity, acceleration; | |
| } | |
| public unsafe struct FirefliesFlockingNET : IJob | |
| { | |
| public uint boids; | |
| public uint lifetime; | |
| public float result; | |
| public void Run() | |
| { | |
| result = FirefliesFlocking(boids, lifetime); | |
| } | |
| private uint parkMiller; | |
| private float maxSpeed; | |
| private float maxForce; | |
| private float separationDistance; | |
| private float neighbourDistance; | |
| private float FirefliesFlocking(uint boids, uint lifetime) | |
| { | |
| parkMiller = 666; | |
| maxSpeed = 1.0f; | |
| maxForce = 0.03f; | |
| separationDistance = 15.0f; | |
| neighbourDistance = 30.0f; | |
| Boid* fireflies = (Boid*)Malloc((nuint)(boids * sizeof(Boid)), 16); | |
| for (uint i = 0; i < boids; ++i) | |
| { | |
| fireflies[i].position = new Vector3 { X = Random(), Y = Random(), Z = Random() }; | |
| fireflies[i].velocity = new Vector3 { X = Random(), Y = Random(), Z = Random() }; | |
| fireflies[i].acceleration = new Vector3 { X = 0.0f, Y = 0.0f, Z = 0.0f }; | |
| } | |
| for (uint i = 0; i < lifetime; ++i) | |
| { | |
| // Update | |
| for (uint boid = 0; boid < boids; ++boid) | |
| { | |
| Add(&fireflies[boid].velocity, &fireflies[boid].acceleration); | |
| float speed = Length(&fireflies[boid].velocity); | |
| if (speed > maxSpeed) | |
| { | |
| Divide(&fireflies[boid].velocity, speed); | |
| Multiply(&fireflies[boid].velocity, maxSpeed); | |
| } | |
| Add(&fireflies[boid].position, &fireflies[boid].velocity); | |
| Multiply(&fireflies[boid].acceleration, maxSpeed); | |
| } | |
| // Separation | |
| for (uint boid = 0; boid < boids; ++boid) | |
| { | |
| Vector3 separation = default(Vector3); | |
| int count = 0; | |
| for (uint target = 0; target < boids; ++target) | |
| { | |
| Vector3 position = fireflies[boid].position; | |
| Subtract(&position, &fireflies[target].position); | |
| float distance = Length(&position); | |
| if (distance > 0.0f && distance < separationDistance) | |
| { | |
| Normalize(&position); | |
| Divide(&position, distance); | |
| separation = position; | |
| count++; | |
| } | |
| } | |
| if (count > 0) | |
| { | |
| Divide(&separation, (float)count); | |
| Normalize(&separation); | |
| Multiply(&separation, maxSpeed); | |
| Subtract(&separation, &fireflies[boid].velocity); | |
| float force = Length(&separation); | |
| if (force > maxForce) | |
| { | |
| Divide(&separation, force); | |
| Multiply(&separation, maxForce); | |
| } | |
| Multiply(&separation, 1.5f); | |
| Add(&fireflies[boid].acceleration, &separation); | |
| } | |
| } | |
| // Cohesion | |
| for (uint boid = 0; boid < boids; ++boid) | |
| { | |
| Vector3 cohesion = default(Vector3); | |
| int count = 0; | |
| for (uint target = 0; target < boids; ++target) | |
| { | |
| Vector3 position = fireflies[boid].position; | |
| Subtract(&position, &fireflies[target].position); | |
| float distance = Length(&position); | |
| if (distance > 0.0f && distance < neighbourDistance) | |
| { | |
| cohesion = fireflies[boid].position; | |
| count++; | |
| } | |
| } | |
| if (count > 0) | |
| { | |
| Divide(&cohesion, (float)count); | |
| Subtract(&cohesion, &fireflies[boid].position); | |
| Normalize(&cohesion); | |
| Multiply(&cohesion, maxSpeed); | |
| Subtract(&cohesion, &fireflies[boid].velocity); | |
| float force = Length(&cohesion); | |
| if (force > maxForce) | |
| { | |
| Divide(&cohesion, force); | |
| Multiply(&cohesion, maxForce); | |
| } | |
| Add(&fireflies[boid].acceleration, &cohesion); | |
| } | |
| } | |
| } | |
| Free(fireflies); | |
| return (float)parkMiller; | |
| } | |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |
| private void Add(Vector3* left, Vector3* right) | |
| { | |
| left->X += right->X; | |
| left->Y += right->Y; | |
| left->Z += right->Z; | |
| } | |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |
| private void Subtract(Vector3* left, Vector3* right) | |
| { | |
| left->X -= right->X; | |
| left->Y -= right->Y; | |
| left->Z -= right->Z; | |
| } | |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |
| private void Divide(Vector3* vector, float value) | |
| { | |
| vector->X /= value; | |
| vector->Y /= value; | |
| vector->Z /= value; | |
| } | |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |
| private void Multiply(Vector3* vector, float value) | |
| { | |
| vector->X *= value; | |
| vector->Y *= value; | |
| vector->Z *= value; | |
| } | |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |
| private void Normalize(Vector3* vector) | |
| { | |
| float length = MathF.Sqrt(vector->X * vector->X + vector->Y * vector->Y + vector->Z * vector->Z); | |
| vector->X /= length; | |
| vector->Y /= length; | |
| vector->Z /= length; | |
| } | |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |
| private float Length(Vector3* vector) | |
| { | |
| return MathF.Sqrt(vector->X * vector->X + vector->Y * vector->Y + vector->Z * vector->Z); | |
| } | |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |
| private float Random() | |
| { | |
| parkMiller = (uint)(((ulong)parkMiller * 48271u) % 0x7fffffff); | |
| return parkMiller / 10000000.0f; | |
| } | |
| } | |
| // Polynomials | |
| [InlineArray(100)] | |
| struct Poly { private float Elem; } | |
| public unsafe struct PolynomialsNET : IJob | |
| { | |
| public uint iterations; | |
| public float result; | |
| public void Run() | |
| { | |
| result = Polynomials(iterations); | |
| } | |
| private float Polynomials(uint iterations) | |
| { | |
| const float x = 0.2f; | |
| float pu = 0.0f; | |
| var poly = new Poly();; | |
| for (uint i = 0; i < iterations; i++) | |
| { | |
| float mu = 10.0f; | |
| float s; | |
| int j; | |
| for (j = 0; j < 100; j++) | |
| { | |
| poly[j] = mu = (mu + 2.0f) / 2.0f; | |
| } | |
| s = 0.0f; | |
| for (j = 0; j < 100; j++) | |
| { | |
| s = x * s + poly[j]; | |
| } | |
| pu += s; | |
| } | |
| return pu; | |
| } | |
| } | |
| // Particle Kinematics | |
| private struct Particle | |
| { | |
| public float x, y, z, vx, vy, vz; | |
| } | |
| public unsafe struct ParticleKinematicsNET : IJob | |
| { | |
| public uint quantity; | |
| public uint iterations; | |
| public float result; | |
| public void Run() | |
| { | |
| result = ParticleKinematics(quantity, iterations); | |
| } | |
| private float ParticleKinematics(uint quantity, uint iterations) | |
| { | |
| Particle* particles = (Particle*)Malloc((nuint)(quantity * sizeof(Particle)), 16); | |
| for (uint i = 0; i < quantity; ++i) | |
| { | |
| particles[i].x = (float)i; | |
| particles[i].y = (float)(i + 1); | |
| particles[i].z = (float)(i + 2); | |
| particles[i].vx = 1.0f; | |
| particles[i].vy = 2.0f; | |
| particles[i].vz = 3.0f; | |
| } | |
| for (uint a = 0; a < iterations; ++a) | |
| { | |
| for (uint b = 0, c = quantity; b < c; ++b) | |
| { | |
| Particle* p = &particles[b]; | |
| p->x += p->vx; | |
| p->y += p->vy; | |
| p->z += p->vz; | |
| } | |
| } | |
| Particle particle = new Particle { x = particles[0].x, y = particles[0].y, z = particles[0].z }; | |
| Free(particles); | |
| return particle.x + particle.y + particle.z; | |
| } | |
| } | |
| // Arcfour | |
| public unsafe struct ArcfourNET : IJob | |
| { | |
| public uint iterations; | |
| public int result; | |
| public void Run() | |
| { | |
| result = Arcfour(iterations); | |
| } | |
| private int Arcfour(uint iterations) | |
| { | |
| const int keyLength = 5; | |
| const int streamLength = 10; | |
| byte* state = (byte*)Malloc(256, 8); | |
| byte* buffer = (byte*)Malloc(64, 8); | |
| byte* key = stackalloc byte[5]; | |
| byte* stream = stackalloc byte[10]; | |
| key[0] = 0xDB; | |
| key[1] = 0xB7; | |
| key[2] = 0x60; | |
| key[3] = 0xD4; | |
| key[4] = 0x56; | |
| stream[0] = 0xEB; | |
| stream[1] = 0x9F; | |
| stream[2] = 0x77; | |
| stream[3] = 0x81; | |
| stream[4] = 0xB7; | |
| stream[5] = 0x34; | |
| stream[6] = 0xCA; | |
| stream[7] = 0x72; | |
| stream[8] = 0xA7; | |
| stream[9] = 0x19; | |
| int idx = 0; | |
| for (uint i = 0; i < iterations; i++) | |
| { | |
| idx = KeySetup(state, key, keyLength); | |
| idx = GenerateStream(state, buffer, streamLength); | |
| } | |
| Free(state); | |
| Free(buffer); | |
| return idx; | |
| } | |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |
| private int KeySetup(byte* state, byte* key, int length) | |
| { | |
| int i, j; | |
| byte t; | |
| for (i = 0; i < 256; ++i) | |
| { | |
| state[i] = (byte)i; | |
| } | |
| for (i = 0, j = 0; i < 256; ++i) | |
| { | |
| j = (j + state[i] + key[i % length]) % 256; | |
| t = state[i]; | |
| state[i] = state[j]; | |
| state[j] = t; | |
| } | |
| return i; | |
| } | |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |
| private int GenerateStream(byte* state, byte* buffer, int length) | |
| { | |
| int i, j; | |
| int idx; | |
| byte t; | |
| for (idx = 0, i = 0, j = 0; idx < length; ++idx) | |
| { | |
| i = (i + 1) % 256; | |
| j = (j + state[i]) % 256; | |
| t = state[i]; | |
| state[i] = state[j]; | |
| state[j] = t; | |
| buffer[idx] = state[(state[i] + state[j]) % 256]; | |
| } | |
| return i; | |
| } | |
| } | |
| // Seahash | |
| public unsafe struct SeahashNET : IJob | |
| { | |
| public uint iterations; | |
| public ulong result; | |
| public void Run() | |
| { | |
| result = Seahash(iterations); | |
| } | |
| private ulong Seahash(uint iterations) | |
| { | |
| const int bufferLength = 1024 * 128; | |
| byte* buffer = (byte*)Malloc(bufferLength, 8); | |
| for (int i = 0; i < bufferLength; i++) | |
| { | |
| buffer[i] = (byte)(i % 256); | |
| } | |
| ulong hash = 0; | |
| for (uint i = 0; i < iterations; i++) | |
| { | |
| hash = Compute(buffer, bufferLength, 0x16F11FE89B0D677C, 0xB480A793D8E6C86C, 0x6FE2E5AAF078EBC9, 0x14F994A4C5259381); | |
| } | |
| Free(buffer); | |
| return hash; | |
| } | |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |
| private ulong Read(byte* pointer) | |
| { | |
| return *(ulong*)pointer; | |
| } | |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |
| private ulong Diffuse(ulong value) | |
| { | |
| value *= 0x6EED0E9DA4D94A4F; | |
| value ^= ((value >> 32) >> (int)(value >> 60)); | |
| value *= 0x6EED0E9DA4D94A4F; | |
| return value; | |
| } | |
| private ulong Compute(byte* buffer, ulong length, ulong a, ulong b, ulong c, ulong d) | |
| { | |
| const uint blockSize = 32; | |
| ulong end = length & ~(blockSize - 1); | |
| for (uint i = 0; i < end; i += blockSize) | |
| { | |
| a ^= Read(buffer + i); | |
| b ^= Read(buffer + i + 8); | |
| c ^= Read(buffer + i + 16); | |
| d ^= Read(buffer + i + 24); | |
| a = Diffuse(a); | |
| b = Diffuse(b); | |
| c = Diffuse(c); | |
| d = Diffuse(d); | |
| } | |
| ulong excessive = length - end; | |
| byte* bufferEnd = buffer + end; | |
| if (excessive > 0) | |
| { | |
| a ^= Read(bufferEnd); | |
| if (excessive > 8) | |
| { | |
| b ^= Read(bufferEnd); | |
| if (excessive > 16) | |
| { | |
| c ^= Read(bufferEnd); | |
| if (excessive > 24) | |
| { | |
| d ^= Read(bufferEnd); | |
| d = Diffuse(d); | |
| } | |
| c = Diffuse(c); | |
| } | |
| b = Diffuse(b); | |
| } | |
| a = Diffuse(a); | |
| } | |
| a ^= b; | |
| c ^= d; | |
| a ^= c; | |
| a ^= length; | |
| return Diffuse(a); | |
| } | |
| } | |
| // Radix | |
| [InlineArray(10)] | |
| struct Bucket { private int Elem; } | |
| public unsafe struct RadixNET : IJob | |
| { | |
| public uint iterations; | |
| public int result; | |
| public void Run() | |
| { | |
| result = Radix(iterations); | |
| } | |
| private uint classicRandom; | |
| private int Radix(uint iterations) | |
| { | |
| classicRandom = 7525; | |
| const int arrayLength = 128; | |
| int* array = (int*)Malloc(arrayLength * sizeof(int), 16); | |
| for (uint a = 0; a < iterations; a++) | |
| { | |
| for (int b = 0; b < arrayLength; b++) | |
| { | |
| array[b] = Random(); | |
| } | |
| Sort(array, arrayLength); | |
| } | |
| int head = array[0]; | |
| Free(array); | |
| return head; | |
| } | |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |
| private int Random() | |
| { | |
| classicRandom = (6253729 * classicRandom + 4396403); | |
| return (int)(classicRandom % 32767); | |
| } | |
| [MethodImpl(MethodImplOptions.AggressiveInlining)] | |
| private int FindLargest(int* array, int length) | |
| { | |
| int i; | |
| int largest = -1; | |
| for (i = 0; i < length; i++) | |
| { | |
| if (array[i] > largest) | |
| largest = array[i]; | |
| } | |
| return largest; | |
| } | |
| private void Sort(int* array, int length) | |
| { | |
| int i; | |
| int* semiSorted = stackalloc int[length]; | |
| int significantDigit = 1; | |
| int largest = FindLargest(array, length); | |
| while (largest / significantDigit > 0) | |
| { | |
| var bucket = new Bucket(); | |
| for (i = 0; i < length; i++) | |
| { | |
| bucket[(array[i] / significantDigit) % 10]++; | |
| } | |
| for (i = 1; i < 10; i++) | |
| { | |
| bucket[i] += bucket[i - 1]; | |
| } | |
| for (i = length - 1; i >= 0; i--) | |
| { | |
| semiSorted[--bucket[(array[i] / significantDigit) % 10]] = array[i]; | |
| } | |
| for (i = 0; i < length; i++) | |
| { | |
| array[i] = semiSorted[i]; | |
| } | |
| significantDigit *= 10; | |
| } | |
| } | |
| } | |
| [STAThread, MethodImpl(MethodImplOptions.AggressiveOptimization)] | |
| private static void Main() | |
| { | |
| var stopwatch = new System.Diagnostics.Stopwatch(); | |
| double time = 0; | |
| // Options | |
| bool | |
| fibonacciEnabled = true, | |
| mandelbrotEnabled = true, | |
| nbodyEnabled = true, | |
| sieveOfEratosthenesEnabled = true, | |
| pixarRaytracerEnabled = true, | |
| firefliesFlockingEnabled = true, | |
| polynomialsEnabled = true, | |
| particleKinematicsEnabled = true, | |
| arcfourEnabled = true, | |
| seahashEnabled = true, | |
| radixEnabled = true; | |
| uint | |
| fibonacciNumber = 46, | |
| mandelbrotIterations = 8, | |
| nbodyAdvancements = 100000000, | |
| sieveOfEratosthenesIterations = 1000000, | |
| pixarRaytracerSamples = 16, | |
| firefliesFlockingLifetime = 1000, | |
| polynomialsIterations = 10000000, | |
| particleKinematicsIterations = 10000000, | |
| arcfourIterations = 10000000, | |
| seahashIterations = 1000000, | |
| radixIterations = 1000000; | |
| // Benchmarks | |
| if (fibonacciEnabled) | |
| { | |
| var benchmark = new FibonacciNET | |
| { | |
| number = fibonacciNumber | |
| }; | |
| stopwatch.Stop(); | |
| benchmark.Run(); | |
| stopwatch.Restart(); | |
| benchmark.Run(); | |
| time = stopwatch.ElapsedMilliseconds; | |
| Console.WriteLine("Fibonacci: " + time + " ms"); | |
| } | |
| if (mandelbrotEnabled) | |
| { | |
| var benchmark = new MandelbrotNET | |
| { | |
| width = 1920, | |
| height = 1080, | |
| iterations = mandelbrotIterations | |
| }; | |
| stopwatch.Stop(); | |
| benchmark.Run(); | |
| stopwatch.Restart(); | |
| benchmark.Run(); | |
| time = stopwatch.ElapsedMilliseconds; | |
| Console.WriteLine("Mandelbrot: " + time + " ms"); | |
| } | |
| if (nbodyEnabled) | |
| { | |
| var benchmark = new NBodyNET | |
| { | |
| advancements = nbodyAdvancements | |
| }; | |
| stopwatch.Stop(); | |
| benchmark.Run(); | |
| stopwatch.Restart(); | |
| benchmark.Run(); | |
| time = stopwatch.ElapsedMilliseconds; | |
| Console.WriteLine("NBody: " + time + " ms"); | |
| } | |
| if (sieveOfEratosthenesEnabled) | |
| { | |
| var benchmark = new SieveOfEratosthenesNET | |
| { | |
| iterations = sieveOfEratosthenesIterations | |
| }; | |
| stopwatch.Stop(); | |
| benchmark.Run(); | |
| stopwatch.Restart(); | |
| benchmark.Run(); | |
| time = stopwatch.ElapsedMilliseconds; | |
| Console.WriteLine("Sieve of Eratosthenes: " + time + " ms"); | |
| } | |
| if (pixarRaytracerEnabled) | |
| { | |
| var benchmark = new PixarRaytracerNET | |
| { | |
| width = 720, | |
| height = 480, | |
| samples = pixarRaytracerSamples | |
| }; | |
| stopwatch.Stop(); | |
| benchmark.Run(); | |
| stopwatch.Restart(); | |
| benchmark.Run(); | |
| time = stopwatch.ElapsedMilliseconds; | |
| Console.WriteLine("Pixar Raytracer: " + time + " ms"); | |
| } | |
| if (firefliesFlockingEnabled) | |
| { | |
| var benchmark = new FirefliesFlockingNET | |
| { | |
| boids = 1000, | |
| lifetime = firefliesFlockingLifetime | |
| }; | |
| stopwatch.Stop(); | |
| benchmark.Run(); | |
| stopwatch.Restart(); | |
| benchmark.Run(); | |
| time = stopwatch.ElapsedMilliseconds; | |
| Console.WriteLine("Fireflies Flocking: " + time + " ms"); | |
| } | |
| if (polynomialsEnabled) | |
| { | |
| var benchmark = new PolynomialsNET | |
| { | |
| iterations = polynomialsIterations | |
| }; | |
| stopwatch.Stop(); | |
| benchmark.Run(); | |
| stopwatch.Restart(); | |
| benchmark.Run(); | |
| time = stopwatch.ElapsedMilliseconds; | |
| Console.WriteLine("Polynomials: " + time + " ms"); | |
| } | |
| if (particleKinematicsEnabled) | |
| { | |
| var benchmark = new ParticleKinematicsNET | |
| { | |
| quantity = 1000, | |
| iterations = particleKinematicsIterations | |
| }; | |
| stopwatch.Stop(); | |
| benchmark.Run(); | |
| stopwatch.Restart(); | |
| benchmark.Run(); | |
| time = stopwatch.ElapsedMilliseconds; | |
| Console.WriteLine("Particle Kinematics: " + time + " ms"); | |
| } | |
| if (arcfourEnabled) | |
| { | |
| var benchmark = new ArcfourNET | |
| { | |
| iterations = arcfourIterations | |
| }; | |
| stopwatch.Stop(); | |
| benchmark.Run(); | |
| stopwatch.Restart(); | |
| benchmark.Run(); | |
| time = stopwatch.ElapsedMilliseconds; | |
| Console.WriteLine("Arcfour: " + time + " ms"); | |
| } | |
| if (seahashEnabled) | |
| { | |
| var benchmark = new SeahashNET | |
| { | |
| iterations = seahashIterations | |
| }; | |
| stopwatch.Stop(); | |
| benchmark.Run(); | |
| stopwatch.Restart(); | |
| benchmark.Run(); | |
| time = stopwatch.ElapsedMilliseconds; | |
| Console.WriteLine("Seahash: " + time + " ms"); | |
| } | |
| if (radixEnabled) | |
| { | |
| var benchmark = new RadixNET | |
| { | |
| iterations = radixIterations | |
| }; | |
| stopwatch.Stop(); | |
| benchmark.Run(); | |
| stopwatch.Restart(); | |
| benchmark.Run(); | |
| time = stopwatch.ElapsedMilliseconds; | |
| Console.WriteLine("Radix: " + time + " ms"); | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment