interoptopus_csharp 0.16.3

The C# backend for Interoptopus.
Documentation

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;

namespace Interoptopus
{
    public delegate void Run();

    public class MeasureResult
    {
        private static long _calibrationTicks = 0;

        long _n;
        long _totalTicks;

        public double MicroPer1000()
        {
            var n = (double) _n;
            var ticks_for_all_n = (double) _totalTicks;

            // ns/call == µs/1000 calls numerically; use Stopwatch.Frequency to be platform-independent
            var ns_per_call = ticks_for_all_n / n * 1_000_000_000.0 / Stopwatch.Frequency;
            return ns_per_call;
        }

        public MeasureResult(long n, long totalTicks)
        {
            _n = n;
            _totalTicks = totalTicks;
        }

        public static void Calibrate(uint n, Run r)
        {
            var result = Measure(n, r);
            _calibrationTicks = result._totalTicks;
        }

        public static MeasureResult Measure(uint n, Run r)
        {

            for (var i = 0; i < n; i++)
            {
                r.Invoke();
            }

            var stopwatch = new Stopwatch();
            stopwatch.Start();
            for (var i = 0; i < n; i++)
            {
                r.Invoke();
            }
            stopwatch.Stop();

            return new MeasureResult( n, stopwatch.ElapsedTicks - _calibrationTicks);
        }

    }

    class Entry
    {
        public string Name;
        public MeasureResult Result;
    }

    public class MarkdownTableWriter
    {
        private List<Entry> Entries = new List<Entry>();

        public void Add(string name, MeasureResult result)
        {
            Console.WriteLine($"{name}: {result.MicroPer1000():F0}");
            Entries.Add(new Entry()
            {
                Name = name,
                Result = result
            });
        }

        public void Write(string file)
        {
            var header = @"
# FFI Call Overheads

The numbers below are to help FFI design decisions by giving order-of-magnitude estimates how
expensive certain constructs are.

## Notes

- Times were determined by running the given construct 100k times, taking the elapsed time in ticks,
and computing the cost per 1k invocations.

- The time of the called function is included.

- However, the reference project was written so that each function is _minimal_, i.e., any similar
function you wrote, would have to at least as expensive operations if it were to do anything sensible with
the given type.

- The list is ad-hoc, PRs adding more tests to `Benchmark.cs` are welcome.

- Bindings were generated with the C# `use_unsafe` config, which dramatically (between 2x and 150x(!)) speeds
  up slice access and copies in .NET and Unity, [see the FAQ for details](https://github.com/ralfbiedert/interoptopus/blob/master/FAQ.md#existing-backends).

## System

The following system was used:

```
System: AMD Ryzen 9 7950X3D, 64 GB RAM; Windows 11
rustc: stable (i.e., 1.85 or later)
profile: --release
.NET: v9.0
```

## Results

| Construct | ns per call |
| --- | --- |
";

            using StreamWriter sw = File.CreateText(file);
            sw.Write(header);
            foreach (var entry in Entries)
            {
                sw.WriteLine($"| `{entry.Name}` | {(long) entry.Result.MicroPer1000():F0} |");
            }
        }
    }
}