Compare commits
2
Commits
3a2b123478
...
0cef5aa517
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0cef5aa517 | ||
|
|
172f64c6f9 |
@@ -1,14 +1,12 @@
|
||||
# Nitload
|
||||

|
||||
|
||||

|
||||
# nFramework
|
||||
|
||||
Nitload (formerly StgSharp) is an experimental .NET framework for graphics, numerical computing, language analysis, and other performance-sensitive applications. It began as the foundation of a next-generation STG (shoot 'em up) engine, but the current repository focuses on reusable low-level infrastructure rather than a complete, ready-to-use game engine.
|
||||
|
||||
The managed libraries now use the `Nitload.*` namespace and project prefix. Repository URLs, the logo filename, and some legacy test artifacts still retain the StgSharp name.
|
||||
nFramework (formerly StgSharp, now also nitload.Framework) is an experimental framework built on .NET for graphics, numerical computing, language analysis, and other performance-sensitive applications. It combines managed C# libraries with a C native runtime, targets .NET 8, and provides Windows and Linux native implementations.
|
||||
|
||||
## Overview
|
||||
|
||||
The framework combines managed C# APIs with a C native library. Its current work includes OpenGL bindings and rendering infrastructure, generic matrix and vector types, SIMD-accelerated native kernels, custom allocators and collections, regular-language analysis, and state-oriented UI infrastructure. Nitload targets .NET 8; platform and architecture coverage varies by module, with Windows and Linux as the principal native targets.
|
||||
nFramework combines managed C# APIs with a C native library. Its current work includes OpenGL bindings and rendering infrastructure, generic matrix and vector types, SIMD-accelerated native kernels, custom allocators and collections, regular-language analysis, and state-oriented UI infrastructure. nFramework targets .NET 8; platform and architecture coverage varies by module, with Windows and Linux as the principal native targets.
|
||||
|
||||
## Core Modules
|
||||
|
||||
@@ -139,7 +137,7 @@ The benchmark reports normalized time per allocation/free operation. Each iterat
|
||||
|
||||
## Installation
|
||||
|
||||
Nitload is currently in development and requires compilation from source. **NuGet packages are not yet available** - you must build the project from source code.
|
||||
nFramework is currently in development and requires compilation from source. **NuGet packages are not yet available** - you must build the project from source code.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
@@ -245,7 +243,7 @@ allocator.Free(handle);
|
||||
|
||||
## Contributing
|
||||
|
||||
Nitload is developed by Nitload Space. Contributions are welcome, particularly in:
|
||||
nFramework is developed by Nitload Space under the Nitload brand. Contributions are welcome, particularly in:
|
||||
|
||||
- Performance optimizations
|
||||
- Cross-platform compatibility
|
||||
@@ -254,7 +252,7 @@ Nitload is developed by Nitload Space. Contributions are welcome, particularly i
|
||||
|
||||
## License
|
||||
|
||||
Nitload is licensed under the MIT License. See [LICENSE](LICENSE) for details.
|
||||
nFramework is licensed under the MIT License. See [LICENSE](LICENSE) for details.
|
||||
|
||||
## Future Plans
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="616" height="104" viewBox="0 0 616 104" role="img" aria-labelledby="title desc">
|
||||
<title id="title">nFramework</title>
|
||||
<desc id="desc">Horizontal nFramework logo with the upright punctuation icon and an IBM Plex Mono Medium wordmark.</desc>
|
||||
<g fill="#59B2DA" transform="translate(13.6 13.6) scale(0.8)"><polygon points="8,8 31,8 31,20 20,20 20,76 31,76 31,88 8,88" /><polygon points="97,8 120,8 120,88 97,88 97,76 108,76 108,20 97,20" /><polygon points="40,16 53,16 53,45 40,45" /><circle cx="80" cy="76" r="8" /></g>
|
||||
<g fill="#E9EEF5"><path d="M146.384 78.524V39.308H154.668V45.996H155.048Q155.656 44.476 156.606 43.07Q157.556 41.664 158.924 40.638Q160.292 39.612 162.116 39.004Q163.94 38.396 166.372 38.396Q172.3 38.396 175.948 42.272Q179.596 46.148 179.596 53.292V78.524H171.312V54.508Q171.312 45.16 163.18 45.16Q161.584 45.16 160.064 45.578Q158.544 45.996 157.328 46.832Q156.112 47.668 155.39 48.96Q154.668 50.252 154.668 51.924V78.524Z"/><path d="M192.136 78.524V25.476H226.26V32.62H200.648V48.048H223.904V55.192H200.648V78.524Z"/><path d="M236.976 71.836H247.388V45.996H236.976V39.308H255.672V49.72H256.128Q256.66 47.668 257.648 45.768Q258.636 43.868 260.156 42.462Q261.676 41.056 263.88 40.182Q266.084 39.308 269.048 39.308H273.76V47.212H266.008Q261.448 47.212 258.56 49.91Q255.672 52.608 255.672 57.168V71.836H269.884V78.524H236.976Z"/><path d="M314.192 78.524Q310.62 78.524 308.834 76.7Q307.048 74.876 306.668 71.912H306.288Q305.148 75.484 302.146 77.46Q299.144 79.436 294.508 79.436Q288.504 79.436 284.932 76.282Q281.36 73.128 281.36 67.58Q281.36 61.88 285.578 58.878Q289.796 55.876 298.536 55.876H306.06V52.836Q306.06 44.856 297.624 44.856Q293.824 44.856 291.468 46.338Q289.112 47.82 287.516 50.252L282.576 46.224Q284.248 43.032 288.2 40.714Q292.152 38.396 298.384 38.396Q305.832 38.396 310.088 41.968Q314.344 45.54 314.344 52.304V71.988H319.284V78.524ZM296.864 73.356Q300.892 73.356 303.476 71.532Q306.06 69.708 306.06 66.82V61.12H298.688Q289.72 61.12 289.72 66.516V68.036Q289.72 70.696 291.62 72.026Q293.52 73.356 296.864 73.356Z"/><path d="M325.744 78.524V39.308H332.812V44.4H333.192Q334.104 41.892 335.814 40.144Q337.524 38.396 340.716 38.396Q343.832 38.396 345.542 40.106Q347.252 41.816 347.708 44.78H348.012Q349.076 42.12 350.976 40.258Q352.876 38.396 356.448 38.396Q361.008 38.396 362.794 41.588Q364.58 44.78 364.58 50.936V78.524H357.512V52Q357.512 47.896 356.524 46.338Q355.536 44.78 353.332 44.78Q351.356 44.78 350.026 46.11Q348.696 47.44 348.696 50.1V78.524H341.628V52Q341.628 47.896 340.678 46.338Q339.728 44.78 337.524 44.78Q335.472 44.78 334.142 46.11Q332.812 47.44 332.812 50.1V78.524Z"/><path d="M391.636 79.436Q387.152 79.436 383.58 77.992Q380.008 76.548 377.576 73.888Q375.144 71.228 373.852 67.466Q372.56 63.704 372.56 58.992Q372.56 54.28 373.89 50.48Q375.22 46.68 377.614 43.982Q380.008 41.284 383.428 39.84Q386.848 38.396 391.028 38.396Q395.132 38.396 398.476 39.802Q401.82 41.208 404.176 43.792Q406.532 46.376 407.786 49.948Q409.04 53.52 409.04 57.852V60.968H380.92V62.26Q380.92 66.896 383.808 69.822Q386.696 72.748 391.788 72.748Q395.588 72.748 398.286 71.114Q400.984 69.48 402.656 66.744L407.824 71.304Q405.848 74.724 401.782 77.08Q397.716 79.436 391.636 79.436ZM391.028 44.704Q388.824 44.704 386.962 45.464Q385.1 46.224 383.77 47.63Q382.44 49.036 381.68 50.936Q380.92 52.836 380.92 55.116V55.648H400.604V54.888Q400.604 50.252 397.982 47.478Q395.36 44.704 391.028 44.704Z"/><path d="M415.12 39.308H422.188L425.988 72.292H426.672L432.524 39.308H440.58L446.356 72.292H447.04L450.916 39.308H457.68L452.208 78.524H442.024L436.78 48.124H436.096L430.776 78.524H420.592Z"/><path d="M482 79.436Q477.668 79.436 474.21 77.992Q470.752 76.548 468.358 73.888Q465.964 71.228 464.672 67.428Q463.38 63.628 463.38 58.916Q463.38 54.204 464.672 50.404Q465.964 46.604 468.358 43.944Q470.752 41.284 474.21 39.84Q477.668 38.396 482 38.396Q486.332 38.396 489.79 39.84Q493.248 41.284 495.642 43.944Q498.036 46.604 499.328 50.404Q500.62 54.204 500.62 58.916Q500.62 63.628 499.328 67.428Q498.036 71.228 495.642 73.888Q493.248 76.548 489.79 77.992Q486.332 79.436 482 79.436ZM482 72.9Q486.56 72.9 489.258 70.126Q491.956 67.352 491.956 61.88V55.952Q491.956 50.48 489.258 47.706Q486.56 44.932 482 44.932Q477.44 44.932 474.742 47.706Q472.044 50.48 472.044 55.952V61.88Q472.044 67.352 474.742 70.126Q477.44 72.9 482 72.9Z"/><path d="M510.576 71.836H520.988V45.996H510.576V39.308H529.272V49.72H529.728Q530.26 47.668 531.248 45.768Q532.236 43.868 533.756 42.462Q535.276 41.056 537.48 40.182Q539.684 39.308 542.648 39.308H547.36V47.212H539.608Q535.048 47.212 532.16 49.91Q529.272 52.608 529.272 57.168V71.836H543.484V78.524H510.576Z"/><path d="M557.088 22.284H565.372V56.256H565.752L571.908 49.948L582.928 39.308H592.732L576.848 54.736L594.48 78.524H584.524L570.844 59.448L565.372 64.54V78.524H557.088Z"/></g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.8 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="616" height="104" viewBox="0 0 616 104" role="img" aria-labelledby="title desc">
|
||||
<title id="title">nFramework</title>
|
||||
<desc id="desc">Horizontal nFramework logo with the upright punctuation icon and an IBM Plex Mono Medium wordmark.</desc>
|
||||
<g fill="#59B2DA" transform="translate(13.6 13.6) scale(0.8)"><polygon points="8,8 31,8 31,20 20,20 20,76 31,76 31,88 8,88" /><polygon points="97,8 120,8 120,88 97,88 97,76 108,76 108,20 97,20" /><polygon points="40,16 53,16 53,45 40,45" /><circle cx="80" cy="76" r="8" /></g>
|
||||
<g fill="#14202D"><path d="M146.384 78.524V39.308H154.668V45.996H155.048Q155.656 44.476 156.606 43.07Q157.556 41.664 158.924 40.638Q160.292 39.612 162.116 39.004Q163.94 38.396 166.372 38.396Q172.3 38.396 175.948 42.272Q179.596 46.148 179.596 53.292V78.524H171.312V54.508Q171.312 45.16 163.18 45.16Q161.584 45.16 160.064 45.578Q158.544 45.996 157.328 46.832Q156.112 47.668 155.39 48.96Q154.668 50.252 154.668 51.924V78.524Z"/><path d="M192.136 78.524V25.476H226.26V32.62H200.648V48.048H223.904V55.192H200.648V78.524Z"/><path d="M236.976 71.836H247.388V45.996H236.976V39.308H255.672V49.72H256.128Q256.66 47.668 257.648 45.768Q258.636 43.868 260.156 42.462Q261.676 41.056 263.88 40.182Q266.084 39.308 269.048 39.308H273.76V47.212H266.008Q261.448 47.212 258.56 49.91Q255.672 52.608 255.672 57.168V71.836H269.884V78.524H236.976Z"/><path d="M314.192 78.524Q310.62 78.524 308.834 76.7Q307.048 74.876 306.668 71.912H306.288Q305.148 75.484 302.146 77.46Q299.144 79.436 294.508 79.436Q288.504 79.436 284.932 76.282Q281.36 73.128 281.36 67.58Q281.36 61.88 285.578 58.878Q289.796 55.876 298.536 55.876H306.06V52.836Q306.06 44.856 297.624 44.856Q293.824 44.856 291.468 46.338Q289.112 47.82 287.516 50.252L282.576 46.224Q284.248 43.032 288.2 40.714Q292.152 38.396 298.384 38.396Q305.832 38.396 310.088 41.968Q314.344 45.54 314.344 52.304V71.988H319.284V78.524ZM296.864 73.356Q300.892 73.356 303.476 71.532Q306.06 69.708 306.06 66.82V61.12H298.688Q289.72 61.12 289.72 66.516V68.036Q289.72 70.696 291.62 72.026Q293.52 73.356 296.864 73.356Z"/><path d="M325.744 78.524V39.308H332.812V44.4H333.192Q334.104 41.892 335.814 40.144Q337.524 38.396 340.716 38.396Q343.832 38.396 345.542 40.106Q347.252 41.816 347.708 44.78H348.012Q349.076 42.12 350.976 40.258Q352.876 38.396 356.448 38.396Q361.008 38.396 362.794 41.588Q364.58 44.78 364.58 50.936V78.524H357.512V52Q357.512 47.896 356.524 46.338Q355.536 44.78 353.332 44.78Q351.356 44.78 350.026 46.11Q348.696 47.44 348.696 50.1V78.524H341.628V52Q341.628 47.896 340.678 46.338Q339.728 44.78 337.524 44.78Q335.472 44.78 334.142 46.11Q332.812 47.44 332.812 50.1V78.524Z"/><path d="M391.636 79.436Q387.152 79.436 383.58 77.992Q380.008 76.548 377.576 73.888Q375.144 71.228 373.852 67.466Q372.56 63.704 372.56 58.992Q372.56 54.28 373.89 50.48Q375.22 46.68 377.614 43.982Q380.008 41.284 383.428 39.84Q386.848 38.396 391.028 38.396Q395.132 38.396 398.476 39.802Q401.82 41.208 404.176 43.792Q406.532 46.376 407.786 49.948Q409.04 53.52 409.04 57.852V60.968H380.92V62.26Q380.92 66.896 383.808 69.822Q386.696 72.748 391.788 72.748Q395.588 72.748 398.286 71.114Q400.984 69.48 402.656 66.744L407.824 71.304Q405.848 74.724 401.782 77.08Q397.716 79.436 391.636 79.436ZM391.028 44.704Q388.824 44.704 386.962 45.464Q385.1 46.224 383.77 47.63Q382.44 49.036 381.68 50.936Q380.92 52.836 380.92 55.116V55.648H400.604V54.888Q400.604 50.252 397.982 47.478Q395.36 44.704 391.028 44.704Z"/><path d="M415.12 39.308H422.188L425.988 72.292H426.672L432.524 39.308H440.58L446.356 72.292H447.04L450.916 39.308H457.68L452.208 78.524H442.024L436.78 48.124H436.096L430.776 78.524H420.592Z"/><path d="M482 79.436Q477.668 79.436 474.21 77.992Q470.752 76.548 468.358 73.888Q465.964 71.228 464.672 67.428Q463.38 63.628 463.38 58.916Q463.38 54.204 464.672 50.404Q465.964 46.604 468.358 43.944Q470.752 41.284 474.21 39.84Q477.668 38.396 482 38.396Q486.332 38.396 489.79 39.84Q493.248 41.284 495.642 43.944Q498.036 46.604 499.328 50.404Q500.62 54.204 500.62 58.916Q500.62 63.628 499.328 67.428Q498.036 71.228 495.642 73.888Q493.248 76.548 489.79 77.992Q486.332 79.436 482 79.436ZM482 72.9Q486.56 72.9 489.258 70.126Q491.956 67.352 491.956 61.88V55.952Q491.956 50.48 489.258 47.706Q486.56 44.932 482 44.932Q477.44 44.932 474.742 47.706Q472.044 50.48 472.044 55.952V61.88Q472.044 67.352 474.742 70.126Q477.44 72.9 482 72.9Z"/><path d="M510.576 71.836H520.988V45.996H510.576V39.308H529.272V49.72H529.728Q530.26 47.668 531.248 45.768Q532.236 43.868 533.756 42.462Q535.276 41.056 537.48 40.182Q539.684 39.308 542.648 39.308H547.36V47.212H539.608Q535.048 47.212 532.16 49.91Q529.272 52.608 529.272 57.168V71.836H543.484V78.524H510.576Z"/><path d="M557.088 22.284H565.372V56.256H565.752L571.908 49.948L582.928 39.308H592.732L576.848 54.736L594.48 78.524H584.524L570.844 59.448L565.372 64.54V78.524H557.088Z"/></g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.8 KiB |
@@ -9,16 +9,23 @@ using BenchmarkDotNet.Configs;
|
||||
using BenchmarkDotNet.Jobs;
|
||||
using BenchmarkDotNet.Loggers;
|
||||
using BenchmarkDotNet.Running;
|
||||
using BenchmarkDotNet.Toolchains.InProcess.Emit;
|
||||
using Nitload.Benchmark;
|
||||
|
||||
if (args.Length > 0 && args[0] == "--disasm-swiss")
|
||||
{
|
||||
IConfig disassemblyConfig = ManualConfig.Create(DefaultConfig.Instance)
|
||||
.AddLogger(ConsoleLogger.Default)
|
||||
.AddJob(Job.ShortRun.WithWarmupCount(6));
|
||||
BenchmarkRunner.Run<SwissX86DisassemblyBench>(disassemblyConfig);
|
||||
return;
|
||||
}
|
||||
|
||||
IConfig config = ManualConfig.Create(DefaultConfig.Instance)
|
||||
.AddLogger(ConsoleLogger.Default)
|
||||
.AddJob(Job.Default
|
||||
.WithToolchain(InProcessEmitToolchain.Instance)
|
||||
.WithLaunchCount(1)
|
||||
.WithWarmupCount(6)
|
||||
.WithIterationCount(15)
|
||||
.WithInvocationCount(3)
|
||||
.WithUnrollFactor(1));
|
||||
_ = BenchmarkRunner.Run<TlsfAllocatorBench>(config);
|
||||
BenchmarkSwitcher.FromAssembly(typeof(TlsfAllocatorBench).Assembly).Run(args, config);
|
||||
|
||||
@@ -0,0 +1,306 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// file="SwissX86Bench"
|
||||
// Project: nFramework
|
||||
// Copyright (c) Nitload.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Configs;
|
||||
using Nitload.Common.HighPerformance.Memory;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Nitload.Benchmark
|
||||
{
|
||||
[MemoryDiagnoser]
|
||||
[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)]
|
||||
[CategoriesColumn]
|
||||
public unsafe class SwissX86Bench
|
||||
{
|
||||
|
||||
private const int QueryCount = 1 << 21;
|
||||
private const int ConcurrentQueryCount = QueryCount * 6;
|
||||
private const int MaximumResidentCount = 8192;
|
||||
|
||||
private byte* _buffer;
|
||||
private L4.SwissTable _swiss;
|
||||
private Dictionary<L4.AddressSource, ulong> _dictionary = null!;
|
||||
private ConcurrentDictionary<L4.AddressSource, ulong> _concurrentDictionary = null!;
|
||||
private L4.AddressSource[] _hitQueries = [];
|
||||
private L4.AddressSource[] _missQueries = [];
|
||||
private L4.AddressSource[] _writerQueries = [];
|
||||
private ulong[] _writerValues = [];
|
||||
private byte* _readBuffer;
|
||||
private byte* _writeBuffer;
|
||||
private ParallelOptions _parallelOptions = null!;
|
||||
private Func<ulong> _parallelLocalInit = null!;
|
||||
private Func<int, ParallelLoopState, ulong, ulong> _swissParallelBody = null!;
|
||||
private Func<int, ParallelLoopState, ulong, ulong> _concurrentDictionaryParallelBody = null!;
|
||||
private Action<ulong> _parallelLocalFinally = null!;
|
||||
private long _parallelChecksum;
|
||||
|
||||
[Params(25, 50, 100)]
|
||||
public int LoadPercent { get; set; }
|
||||
|
||||
private int ResidentCount => MaximumResidentCount * LoadPercent / 100;
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
{
|
||||
if (!Sse42.X64.IsSupported)
|
||||
throw new PlatformNotSupportedException("The Swiss X86 benchmark requires x64 SSE4.2.");
|
||||
|
||||
_buffer = (byte*)NativeMemory.AlignedAlloc((nuint)L4.BufferSize, 64);
|
||||
if (_buffer == null)
|
||||
throw new OutOfMemoryException("Could not allocate Swiss benchmark tables.");
|
||||
|
||||
_swiss = L4.SwissTable.Create(_buffer);
|
||||
_dictionary = new Dictionary<L4.AddressSource, ulong>(ResidentCount);
|
||||
_concurrentDictionary = new ConcurrentDictionary<L4.AddressSource, ulong>(
|
||||
Math.Max(1, Math.Min(Environment.ProcessorCount, 8)),
|
||||
ResidentCount);
|
||||
|
||||
L4.AddressSource[] residentKeys = new L4.AddressSource[ResidentCount];
|
||||
for (int i = 0; i < ResidentCount; i++)
|
||||
{
|
||||
L4.AddressSource key = MakeKey(i);
|
||||
ulong value = (ulong)i + 1;
|
||||
residentKeys[i] = key;
|
||||
if (!_swiss.TryAddOrSet(key, value))
|
||||
throw new InvalidOperationException($"Swiss insertion failed while seeding key {i}.");
|
||||
_dictionary.Add(key, value);
|
||||
_concurrentDictionary.TryAdd(key, value);
|
||||
}
|
||||
|
||||
_swiss.SwapBuffers();
|
||||
for (int i = 0; i < ResidentCount; i++)
|
||||
{
|
||||
if (!_swiss.TryAddOrSet(residentKeys[i], (ulong)i + 1))
|
||||
throw new InvalidOperationException($"Swiss writer-buffer seeding failed for key {i}.");
|
||||
}
|
||||
|
||||
_readBuffer = _swiss.ReadBufferForLockFreeBenchmark;
|
||||
_writeBuffer = _swiss.WriteBufferForLockFreeBenchmark;
|
||||
|
||||
_hitQueries = new L4.AddressSource[QueryCount];
|
||||
_missQueries = new L4.AddressSource[QueryCount];
|
||||
_writerQueries = new L4.AddressSource[QueryCount];
|
||||
_writerValues = new ulong[QueryCount];
|
||||
Random random = new(0x51_15_2026);
|
||||
for (int i = 0; i < QueryCount; i++)
|
||||
{
|
||||
_hitQueries[i] = residentKeys[random.Next(ResidentCount)];
|
||||
_missQueries[i] = MakeKey(MaximumResidentCount + i);
|
||||
_writerQueries[i] = residentKeys[random.Next(ResidentCount)];
|
||||
_writerValues[i] = (ulong)i + 0x10000;
|
||||
}
|
||||
|
||||
_parallelOptions = new ParallelOptions
|
||||
{
|
||||
MaxDegreeOfParallelism = Math.Max(2, Math.Min(Environment.ProcessorCount, 8))
|
||||
};
|
||||
_parallelLocalInit = static() => 0UL;
|
||||
_swissParallelBody = ReadSwissParallel;
|
||||
_concurrentDictionaryParallelBody = ReadConcurrentDictionaryParallel;
|
||||
_parallelLocalFinally = MergeParallelChecksum;
|
||||
|
||||
ValidateLookupResults();
|
||||
}
|
||||
|
||||
[GlobalCleanup]
|
||||
public void Cleanup()
|
||||
{
|
||||
_swiss.Dispose();
|
||||
NativeMemory.AlignedFree(_buffer);
|
||||
_buffer = null;
|
||||
}
|
||||
|
||||
[Benchmark(Baseline = true, OperationsPerInvoke = QueryCount)]
|
||||
[BenchmarkCategory("Hit lookup")]
|
||||
public ulong DictionaryHitLookup()
|
||||
{
|
||||
ulong checksum = 0;
|
||||
for (int i = 0; i < QueryCount; i++)
|
||||
checksum += _dictionary[_hitQueries[i]];
|
||||
return checksum;
|
||||
}
|
||||
|
||||
[Benchmark(OperationsPerInvoke = QueryCount)]
|
||||
[BenchmarkCategory("Hit lookup")]
|
||||
public ulong SwissLockFreeHitLookup()
|
||||
{
|
||||
ulong checksum = 0;
|
||||
for (int i = 0; i < QueryCount; i++)
|
||||
{
|
||||
if (!L4.SwissReaderX86.TryGet(_readBuffer, _hitQueries[i], out ulong value))
|
||||
throw new InvalidOperationException("Swiss table missed a seeded hit key.");
|
||||
checksum += value;
|
||||
}
|
||||
return checksum;
|
||||
}
|
||||
|
||||
[Benchmark(Baseline = true, OperationsPerInvoke = QueryCount)]
|
||||
[BenchmarkCategory("Miss lookup")]
|
||||
public int DictionaryMissLookup()
|
||||
{
|
||||
int misses = 0;
|
||||
for (int i = 0; i < QueryCount; i++)
|
||||
misses += _dictionary.TryGetValue(_missQueries[i], out _) ? 0 : 1;
|
||||
return misses;
|
||||
}
|
||||
|
||||
[Benchmark(OperationsPerInvoke = QueryCount)]
|
||||
[BenchmarkCategory("Miss lookup")]
|
||||
public int SwissLockFreeMissLookup()
|
||||
{
|
||||
int misses = 0;
|
||||
for (int i = 0; i < QueryCount; i++)
|
||||
misses += L4.SwissReaderX86.TryGet(_readBuffer, _missQueries[i], out _) ? 0 : 1;
|
||||
return misses;
|
||||
}
|
||||
|
||||
[Benchmark(Baseline = true, OperationsPerInvoke = ConcurrentQueryCount)]
|
||||
[BenchmarkCategory("Concurrent hit lookup")]
|
||||
public ulong ConcurrentDictionaryConcurrentHitLookup()
|
||||
{
|
||||
Interlocked.Exchange(ref _parallelChecksum, 0);
|
||||
Parallel.For(
|
||||
0,
|
||||
ConcurrentQueryCount,
|
||||
_parallelOptions,
|
||||
_parallelLocalInit,
|
||||
_concurrentDictionaryParallelBody,
|
||||
_parallelLocalFinally);
|
||||
return unchecked((ulong)Interlocked.Read(ref _parallelChecksum));
|
||||
}
|
||||
|
||||
[Benchmark(OperationsPerInvoke = ConcurrentQueryCount)]
|
||||
[BenchmarkCategory("Concurrent hit lookup")]
|
||||
public ulong SwissLockFreeConcurrentHitLookup()
|
||||
{
|
||||
Interlocked.Exchange(ref _parallelChecksum, 0);
|
||||
Parallel.For(
|
||||
0,
|
||||
ConcurrentQueryCount,
|
||||
_parallelOptions,
|
||||
_parallelLocalInit,
|
||||
_swissParallelBody,
|
||||
_parallelLocalFinally);
|
||||
return unchecked((ulong)Interlocked.Read(ref _parallelChecksum));
|
||||
}
|
||||
|
||||
[Benchmark(Baseline = true, OperationsPerInvoke = QueryCount)]
|
||||
[BenchmarkCategory("Upsert existing")]
|
||||
public ulong DictionaryUpsertExisting()
|
||||
{
|
||||
for (int i = 0; i < QueryCount; i++)
|
||||
_dictionary[_writerQueries[i]] = _writerValues[i];
|
||||
return _dictionary[_writerQueries[^1]];
|
||||
}
|
||||
|
||||
[Benchmark(OperationsPerInvoke = QueryCount)]
|
||||
[BenchmarkCategory("Upsert existing")]
|
||||
public ulong SwissUpsertExisting()
|
||||
{
|
||||
ulong failures = 0;
|
||||
for (int i = 0; i < QueryCount; i++)
|
||||
failures += L4.SwissWriterX86.TryAddOrSet(_writeBuffer, _writerQueries[i], _writerValues[i]) ? 0UL : 1UL;
|
||||
if (failures != 0 || !L4.SwissReaderX86.TryGet(_writeBuffer, _writerQueries[^1], out ulong value))
|
||||
throw new InvalidOperationException("Swiss upsert failed to preserve the final value.");
|
||||
return value;
|
||||
}
|
||||
|
||||
[Benchmark(Baseline = true, OperationsPerInvoke = QueryCount)]
|
||||
[BenchmarkCategory("Remove and reinsert")]
|
||||
public ulong DictionaryRemoveAndReinsert()
|
||||
{
|
||||
ulong checksum = 0;
|
||||
for (int i = 0; i < QueryCount; i++)
|
||||
{
|
||||
L4.AddressSource key = _writerQueries[i];
|
||||
ulong value = _dictionary[key];
|
||||
_dictionary.Remove(key);
|
||||
_dictionary.Add(key, value + 1);
|
||||
checksum += value;
|
||||
}
|
||||
return checksum;
|
||||
}
|
||||
|
||||
[Benchmark(OperationsPerInvoke = QueryCount)]
|
||||
[BenchmarkCategory("Remove and reinsert")]
|
||||
public ulong SwissRemoveAndReinsert()
|
||||
{
|
||||
ulong checksum = 0;
|
||||
for (int i = 0; i < QueryCount; i++)
|
||||
{
|
||||
L4.AddressSource key = _writerQueries[i];
|
||||
if (!L4.SwissWriterX86.TryRemove(_writeBuffer, key, out ulong value) ||
|
||||
!L4.SwissWriterX86.TryAddOrSet(_writeBuffer, key, value + 1)) {
|
||||
throw new InvalidOperationException("Swiss remove/reinsert failed.");
|
||||
}
|
||||
checksum += value;
|
||||
}
|
||||
return checksum;
|
||||
}
|
||||
|
||||
private static L4.AddressSource MakeKey(int index)
|
||||
{
|
||||
return new L4.AddressSource
|
||||
{
|
||||
Address = 0x1000_0000UL + ((ulong)index * 64UL),
|
||||
Description = ((ulong)index * 0x9e37_79b9UL) ^ 0xd1b5_4a32_d192_ed03UL
|
||||
};
|
||||
}
|
||||
|
||||
private ulong ReadSwissParallel(int index, ParallelLoopState state, ulong checksum)
|
||||
{
|
||||
int queryIndex = index & (QueryCount - 1);
|
||||
if (!L4.SwissReaderX86.TryGet(_readBuffer, _hitQueries[queryIndex], out ulong value))
|
||||
throw new InvalidOperationException("Swiss table missed a seeded concurrent hit key.");
|
||||
return checksum + value;
|
||||
}
|
||||
|
||||
private ulong ReadConcurrentDictionaryParallel(int index, ParallelLoopState state, ulong checksum)
|
||||
{
|
||||
int queryIndex = index & (QueryCount - 1);
|
||||
if (!_concurrentDictionary.TryGetValue(_hitQueries[queryIndex], out ulong value))
|
||||
throw new InvalidOperationException("ConcurrentDictionary missed a seeded hit key.");
|
||||
return checksum + value;
|
||||
}
|
||||
|
||||
private void MergeParallelChecksum(ulong checksum)
|
||||
{
|
||||
_ = Interlocked.Add(ref _parallelChecksum, unchecked((long)checksum));
|
||||
}
|
||||
|
||||
private void ValidateLookupResults()
|
||||
{
|
||||
for (int i = 0; i < _hitQueries.Length; i++)
|
||||
{
|
||||
bool dictionaryFound = _dictionary.TryGetValue(_hitQueries[i], out ulong dictionaryValue);
|
||||
bool swissFound = L4.SwissReaderX86.TryGet(_readBuffer, _hitQueries[i], out ulong swissValue);
|
||||
if (!dictionaryFound || !swissFound || dictionaryValue != swissValue)
|
||||
throw new InvalidOperationException("Swiss and Dictionary hit results differ.");
|
||||
|
||||
if (!_concurrentDictionary.TryGetValue(_hitQueries[i], out ulong concurrentValue) ||
|
||||
concurrentValue != dictionaryValue)
|
||||
throw new InvalidOperationException("ConcurrentDictionary and Dictionary hit results differ.");
|
||||
}
|
||||
|
||||
for (int i = 0; i < _missQueries.Length; i++)
|
||||
{
|
||||
bool dictionaryFound = _dictionary.TryGetValue(_missQueries[i], out _);
|
||||
bool swissFound = L4.SwissReaderX86.TryGet(_readBuffer, _missQueries[i], out _);
|
||||
if (dictionaryFound || swissFound)
|
||||
throw new InvalidOperationException("Swiss and Dictionary miss results differ.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// file="SwissX86DisassemblyBench"
|
||||
// Project: nFramework
|
||||
// Copyright (c) Nitload.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using BenchmarkDotNet.Diagnosers;
|
||||
using Nitload.Common.HighPerformance.Memory;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
|
||||
namespace Nitload.Benchmark
|
||||
{
|
||||
[DisassemblyDiagnoser(maxDepth: 3, exportHtml: true)]
|
||||
public unsafe class SwissX86DisassemblyBench
|
||||
{
|
||||
|
||||
private const int ResidentCount = 8192;
|
||||
|
||||
private byte* _buffer;
|
||||
private L4.SwissTable _table;
|
||||
private byte* _readBuffer;
|
||||
private byte* _writeBuffer;
|
||||
private L4.AddressSource _hitKey;
|
||||
private L4.AddressSource _missKey;
|
||||
private ulong _value;
|
||||
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
{
|
||||
if (!Sse42.X64.IsSupported)
|
||||
throw new PlatformNotSupportedException("The Swiss X86 disassembly probe requires x64 SSE4.2.");
|
||||
|
||||
_buffer = (byte*)NativeMemory.AlignedAlloc((nuint)L4.BufferSize, 64);
|
||||
if (_buffer == null)
|
||||
throw new OutOfMemoryException("Could not allocate Swiss disassembly buffers.");
|
||||
|
||||
_table = L4.SwissTable.Create(_buffer);
|
||||
for (int index = 0; index < ResidentCount; index++)
|
||||
{
|
||||
if (!L4.SwissWriterX86.TryAddOrSet(_table.WriteBufferForLockFreeBenchmark,
|
||||
MakeKey(index), (ulong)index + 1))
|
||||
throw new InvalidOperationException($"Failed to seed Swiss key {index}.");
|
||||
}
|
||||
|
||||
_table.SwapBuffers();
|
||||
_readBuffer = _table.ReadBufferForLockFreeBenchmark;
|
||||
_writeBuffer = _table.WriteBufferForLockFreeBenchmark;
|
||||
for (int index = 0; index < ResidentCount; index++)
|
||||
{
|
||||
if (!L4.SwissWriterX86.TryAddOrSet(_writeBuffer, MakeKey(index), (ulong)index + 1))
|
||||
throw new InvalidOperationException($"Failed to seed inactive Swiss key {index}.");
|
||||
}
|
||||
|
||||
_hitKey = MakeKey(ResidentCount / 2);
|
||||
_missKey = MakeKey(ResidentCount + 1);
|
||||
_value = 0x1234_5678;
|
||||
|
||||
if (!L4.SwissReaderX86.TryGet(_readBuffer, _hitKey, out _) ||
|
||||
L4.SwissReaderX86.TryGet(_readBuffer, _missKey, out _))
|
||||
throw new InvalidOperationException("Swiss reader setup validation failed.");
|
||||
}
|
||||
|
||||
[GlobalCleanup]
|
||||
public void Cleanup()
|
||||
{
|
||||
_table.Dispose();
|
||||
NativeMemory.AlignedFree(_buffer);
|
||||
_buffer = null;
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public bool RawReaderHit()
|
||||
{
|
||||
return L4.SwissReaderX86.TryGet(_readBuffer, _hitKey, out _value);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public bool RawReaderMiss()
|
||||
{
|
||||
return L4.SwissReaderX86.TryGet(_readBuffer, _missKey, out _value);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public bool RawWriterUpsertExisting()
|
||||
{
|
||||
return L4.SwissWriterX86.TryAddOrSet(_writeBuffer, _hitKey, _value);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public bool RawWriterRemoveAndReinsert()
|
||||
{
|
||||
return L4.SwissWriterX86.TryRemove(_writeBuffer, _hitKey, out ulong previousValue) &&
|
||||
L4.SwissWriterX86.TryAddOrSet(_writeBuffer, _hitKey, previousValue);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public bool TableReaderHit()
|
||||
{
|
||||
return _table.TryGet(_hitKey, out _value);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public bool TableReaderMiss()
|
||||
{
|
||||
return _table.TryGet(_missKey, out _value);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public bool TableWriterUpsertExisting()
|
||||
{
|
||||
return _table.TryAddOrSet(_hitKey, _value);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public bool TableWriterRemoveAndReinsert()
|
||||
{
|
||||
return _table.TryRemove(_hitKey, out ulong previousValue) &&
|
||||
_table.TryAddOrSet(_hitKey, previousValue);
|
||||
}
|
||||
|
||||
private static L4.AddressSource MakeKey(int index)
|
||||
{
|
||||
return new L4.AddressSource
|
||||
{
|
||||
Address = 0x1000_0000UL + ((ulong)index * 64UL),
|
||||
Description = ((ulong)index * 0x9e37_79b9UL) ^ 0xd1b5_4a32_d192_ed03UL
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -15,4 +15,5 @@ global using System.Threading;
|
||||
[assembly: InternalsVisibleTo("Nitload.RegularAnalysis")]
|
||||
[assembly: InternalsVisibleTo("Nitload.Graphics")]
|
||||
[assembly: InternalsVisibleTo("Nitload.Mathematics")]
|
||||
[assembly: InternalsVisibleTo("Nitload.Benchmark")]
|
||||
[assembly: InternalsVisibleTo("StgSharpDebug")]
|
||||
|
||||
@@ -27,6 +27,8 @@ namespace Nitload.Common.HighPerformance.Memory
|
||||
|
||||
internal const uint MaxBufferSize = 0xFFFFFFFF - 64U;
|
||||
|
||||
[FieldOffset(16)] internal AllocationState State;
|
||||
|
||||
public static uint SizeOf { get; } = (uint)Unsafe.SizeOf<CacheLineMetadata>();
|
||||
|
||||
#region tlsf linked list
|
||||
@@ -43,20 +45,27 @@ namespace Nitload.Common.HighPerformance.Memory
|
||||
[FieldOffset(8)] public uint PositionMask;
|
||||
[FieldOffset(12)] public uint SizeMask;
|
||||
|
||||
#endregion
|
||||
|
||||
[FieldOffset(16)] internal AllocationState State;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#pragma warning disable IDE0032
|
||||
[StructLayout(LayoutKind.Explicit, Size = 16)]
|
||||
public struct Handle : IDisposable
|
||||
public unsafe struct Handle : IDisposable
|
||||
{
|
||||
|
||||
[FieldOffset(0)] private nuint _handle;
|
||||
|
||||
[FieldOffset(8)] private nuint _reserved;
|
||||
|
||||
internal Handle(
|
||||
nuint handle,
|
||||
nuint reserved
|
||||
)
|
||||
{
|
||||
_handle = handle;
|
||||
_reserved = reserved;
|
||||
}
|
||||
|
||||
[Obsolete("You should not create a CacheLineHandle instance outside a L4", true)]
|
||||
public Handle() { }
|
||||
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// file="L4.Map"
|
||||
// Project: StgSharp
|
||||
// Copyright (c) Nitload.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Nitload.Common.HighPerformance.Memory
|
||||
{
|
||||
public unsafe partial class L4
|
||||
{
|
||||
|
||||
private const int _low_predict = 512;
|
||||
|
||||
private int _predict_count;
|
||||
private int _swiss_reader_counter;
|
||||
private int _swiss_swap_lock;
|
||||
private int _swiss_writer_lock;
|
||||
|
||||
public partial Handle Map(
|
||||
AddressSource source
|
||||
)
|
||||
{
|
||||
ulong handle;
|
||||
if (Volatile.Read(ref _predict_count) < _low_predict)
|
||||
{
|
||||
if (Interlocked.CompareExchange(ref _swiss_writer_lock, 1, 0) == 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
Predict();
|
||||
_ = Interlocked.Exchange(ref _swiss_swap_lock, 1);
|
||||
while (Volatile.Read(ref _swiss_reader_counter) != 0) { }
|
||||
|
||||
_addressMap.SwapBuffers();
|
||||
if (_addressMap.TryGet(source, out handle)) {
|
||||
throw new InvalidOperationException($"Unexpected map request: [address source: 0x{source.Address:x16}; layout profile: {source.Description:x16}].");
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_ = Interlocked.Exchange(ref _swiss_swap_lock, 0);
|
||||
_ = Interlocked.Exchange(ref _swiss_writer_lock, 0);
|
||||
}
|
||||
} else if (!TryGetAddress(source, out handle)) {
|
||||
throw new InvalidOperationException($"Unexpected map request: [address source: 0x{source.Address:x16}; layout profile: {source.Description:x16}].");
|
||||
}
|
||||
} else if (!TryGetAddress(source, out handle)) {
|
||||
throw new InvalidOperationException($"Unexpected map request: [address source: 0x{source.Address:x16}; layout profile: {source.Description:x16}].");
|
||||
}
|
||||
|
||||
CacheLineMetadata* metadata = (CacheLineMetadata*)handle;
|
||||
|
||||
// process metadata, unfinished
|
||||
uint offset = metadata->PositionMask;
|
||||
nuint address = /*base ptr + */ offset;
|
||||
_ = Interlocked.Decrement(ref _predict_count);
|
||||
|
||||
return new Handle(address, (nuint)handle);
|
||||
}
|
||||
|
||||
private bool TryGetAddress(
|
||||
AddressSource source,
|
||||
out ulong handle
|
||||
)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
while (Volatile.Read(ref _swiss_swap_lock) != 0) { }
|
||||
|
||||
_ = Interlocked.Increment(ref _swiss_reader_counter);
|
||||
if (Volatile.Read(ref _swiss_swap_lock) == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
_ = Interlocked.Decrement(ref _swiss_reader_counter);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return _addressMap.TryGet(source, out handle);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_ = Interlocked.Decrement(ref _swiss_reader_counter);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// file="L4.Predict"
|
||||
// Project: StgSharp
|
||||
// Copyright (c) Nitload.
|
||||
// SPDX-License-Identifier: MIT
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Nitload.Common.HighPerformance.Memory
|
||||
{
|
||||
public unsafe partial class L4
|
||||
{
|
||||
|
||||
internal partial void Predict() { }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
using System.Buffers;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
|
||||
namespace Nitload.Common.HighPerformance.Memory
|
||||
{
|
||||
@@ -16,8 +16,6 @@ namespace Nitload.Common.HighPerformance.Memory
|
||||
{
|
||||
|
||||
private const int BucketCapacity = 16;
|
||||
private const uint CrcMagic = 0x7f1c9961;
|
||||
|
||||
private const int GroupCount = 1 << GroupCountLog;
|
||||
private const int GroupCountLog = 10;
|
||||
private const int IdealCount = GroupCount * BucketCapacity;//16384
|
||||
@@ -36,17 +34,24 @@ namespace Nitload.Common.HighPerformance.Memory
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct AddressSource
|
||||
public struct AddressSource : IEquatable<AddressSource>
|
||||
{
|
||||
|
||||
public ulong Address;
|
||||
public ulong Description;
|
||||
|
||||
public override readonly bool Equals(
|
||||
object obj
|
||||
public readonly bool Equals(
|
||||
AddressSource other
|
||||
)
|
||||
{
|
||||
return obj is AddressSource source && this == source;
|
||||
return this == other;
|
||||
}
|
||||
|
||||
public override readonly bool Equals(
|
||||
object? obj
|
||||
)
|
||||
{
|
||||
return obj is AddressSource source && Equals(source);
|
||||
}
|
||||
|
||||
public override readonly int GetHashCode()
|
||||
@@ -88,127 +93,102 @@ namespace Nitload.Common.HighPerformance.Memory
|
||||
|
||||
}
|
||||
|
||||
internal sealed unsafe class SwissTable : IDisposable
|
||||
internal unsafe struct SwissTable : IDisposable
|
||||
{
|
||||
|
||||
private byte* _buffer_read;
|
||||
private byte* _buffer_write;
|
||||
private readonly ReaderWriterLockSlim _synchronization = new(LockRecursionPolicy.NoRecursion);
|
||||
|
||||
private SwissTable(byte* buffers)
|
||||
private ulong _published_buffer;
|
||||
|
||||
private SwissTable(
|
||||
byte* buffers
|
||||
)
|
||||
{
|
||||
_buffer_read = buffers;
|
||||
_published_buffer = (ulong)buffers;
|
||||
_buffer_write = buffers + TableBufferSize;
|
||||
InitializeBuffer(_buffer_read);
|
||||
InitializeBuffer(buffers);
|
||||
InitializeBuffer(_buffer_write);
|
||||
}
|
||||
|
||||
internal static SwissTable Create(byte* buffers)
|
||||
internal byte* ReadBufferForLockFreeBenchmark => (byte*)Volatile.Read(ref _published_buffer);
|
||||
|
||||
internal readonly byte* WriteBufferForLockFreeBenchmark => _buffer_write;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_ = Interlocked.Exchange(ref _published_buffer, 0);
|
||||
_buffer_write = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Publishes the write buffer. The caller must provide the phase barrier around this
|
||||
/// operation.
|
||||
/// </summary>
|
||||
public void SwapBuffers()
|
||||
{
|
||||
ulong previousBuffer = Interlocked.Exchange(ref _published_buffer, (ulong)_buffer_write);
|
||||
_buffer_write = (byte*)previousBuffer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes to the inactive buffer. The caller must serialize all writes.
|
||||
/// </summary>
|
||||
public readonly bool TryAddOrSet(
|
||||
AddressSource key,
|
||||
ulong value
|
||||
)
|
||||
{
|
||||
if (Sse42.IsSupported) {
|
||||
return SwissWriterX86.TryAddOrSet(_buffer_write, key, value);
|
||||
}
|
||||
|
||||
throw new NotImplementedException("The Swiss backend is not implemented for this ISA.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the published buffer. The caller must prevent swaps until all reads finish.
|
||||
/// </summary>
|
||||
public bool TryGet(
|
||||
AddressSource key,
|
||||
out ulong value
|
||||
)
|
||||
{
|
||||
byte* publishedBuffer = (byte*)Volatile.Read(ref _published_buffer);
|
||||
if (Sse42.IsSupported) {
|
||||
return SwissReaderX86.TryGet(publishedBuffer, key, out value);
|
||||
}
|
||||
|
||||
throw new NotImplementedException("The Swiss backend is not implemented for this ISA.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes from the inactive buffer. The caller must serialize all writes.
|
||||
/// </summary>
|
||||
public bool TryRemove(
|
||||
AddressSource key,
|
||||
out ulong value
|
||||
)
|
||||
{
|
||||
if (Sse42.IsSupported) {
|
||||
return SwissWriterX86.TryRemove(_buffer_write, key, out value);
|
||||
}
|
||||
|
||||
throw new NotImplementedException("The Swiss backend is not implemented for this ISA.");
|
||||
}
|
||||
|
||||
internal static SwissTable Create(
|
||||
byte* buffers
|
||||
)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(buffers);
|
||||
return new SwissTable(buffers);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the published buffer. Callers must prevent a swap while the read is in progress.
|
||||
/// </summary>
|
||||
public bool TryGet(AddressSource key, out ulong value)
|
||||
private static void InitializeBuffer(
|
||||
byte* buffer
|
||||
)
|
||||
{
|
||||
_synchronization.EnterReadLock();
|
||||
try
|
||||
{
|
||||
return SwissReaderX86.TryGet(_buffer_read, key, out value);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_synchronization.ExitReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes to the inactive buffer. Calls must be serialized by the caller.
|
||||
/// </summary>
|
||||
public bool TryAddOrSet(AddressSource key, ulong value)
|
||||
{
|
||||
_synchronization.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
return SwissWriterX86.TryAddOrSet(_buffer_write, key, value);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_synchronization.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes to the inactive buffer. Calls must be serialized by the caller.
|
||||
/// </summary>
|
||||
public bool TryRemove(AddressSource key, out ulong value)
|
||||
{
|
||||
_synchronization.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
return SwissWriterX86.TryRemove(_buffer_write, key, out value);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_synchronization.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the inactive buffer. Call only after all readers of its previous generation have quiesced.
|
||||
/// </summary>
|
||||
public void PrepareWriteBuffer()
|
||||
{
|
||||
_synchronization.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
InitializeBuffer(_buffer_write);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_synchronization.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Publishes the write buffer. The caller must provide the phase barrier around this operation.
|
||||
/// </summary>
|
||||
public void SwapBuffers()
|
||||
{
|
||||
_synchronization.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
byte* temporary = _buffer_read;
|
||||
_buffer_read = _buffer_write;
|
||||
_buffer_write = temporary;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_synchronization.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_synchronization.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
_buffer_read = null;
|
||||
_buffer_write = null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_synchronization.ExitWriteLock();
|
||||
}
|
||||
_synchronization.Dispose();
|
||||
}
|
||||
|
||||
private static void InitializeBuffer(byte* buffer)
|
||||
{
|
||||
Unsafe.InitBlock(buffer, (byte)SwissControl.Empty, (uint)IdealCount);
|
||||
Unsafe.InitBlock(buffer, (byte)SwissControl.Empty, IdealCount);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.Intrinsics;
|
||||
using System.Runtime.Intrinsics.X86;
|
||||
|
||||
@@ -14,7 +15,25 @@ namespace Nitload.Common.HighPerformance.Memory
|
||||
public partial class L4
|
||||
{
|
||||
|
||||
private readonly unsafe struct SwissWriterX86
|
||||
private const uint CrcMagic = 0x7f1c9961;
|
||||
|
||||
internal static class SwissHashX64
|
||||
{
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static uint Compute(
|
||||
AddressSource key
|
||||
)
|
||||
{
|
||||
ulong hash = CrcMagic;
|
||||
hash = Sse42.X64.Crc32(hash, key.Address);
|
||||
hash = Sse42.X64.Crc32(hash, key.Description);
|
||||
return (uint)hash;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal readonly unsafe struct SwissWriterX86
|
||||
{
|
||||
|
||||
public static bool TryAddOrSet(
|
||||
@@ -26,7 +45,7 @@ namespace Nitload.Common.HighPerformance.Memory
|
||||
Vector128<byte>* control_bytes = (Vector128<byte>*)buffer;
|
||||
SwissBucket* buckets = (SwissBucket*)(buffer + IdealCount);
|
||||
|
||||
uint hash = Sse42.Crc32(CrcMagic, (uint)key.GetHashCode());
|
||||
uint hash = SwissHashX64.Compute(key);
|
||||
uint begin = hash % GroupCount;
|
||||
|
||||
uint first_tomb_group = 0;
|
||||
@@ -108,7 +127,7 @@ namespace Nitload.Common.HighPerformance.Memory
|
||||
{
|
||||
Vector128<byte>* control_bytes = (Vector128<byte>*)buffer;
|
||||
SwissBucket* buckets = (SwissBucket*)(buffer + IdealCount);
|
||||
uint hash = Sse42.Crc32(CrcMagic, (uint)key.GetHashCode());
|
||||
uint hash = SwissHashX64.Compute(key);
|
||||
uint begin = hash % GroupCount;
|
||||
byte target_hash = (byte)((hash >> GroupCountLog) & ~(1 << 7));
|
||||
|
||||
@@ -149,7 +168,7 @@ namespace Nitload.Common.HighPerformance.Memory
|
||||
|
||||
}
|
||||
|
||||
private readonly unsafe struct SwissReaderX86
|
||||
internal readonly unsafe struct SwissReaderX86
|
||||
{
|
||||
|
||||
public static bool TryGet(
|
||||
@@ -161,7 +180,7 @@ namespace Nitload.Common.HighPerformance.Memory
|
||||
Vector128<byte>* control_bytes = (Vector128<byte>*)buffer;
|
||||
SwissBucket* buckets = (SwissBucket*)(buffer + IdealCount);
|
||||
|
||||
uint hash = Sse42.Crc32(CrcMagic, (uint)key.GetHashCode());
|
||||
uint hash = SwissHashX64.Compute(key);
|
||||
uint begin = hash % GroupCount;
|
||||
byte target_hash = (byte)((hash >> GroupCountLog) & ~(1 << 7));
|
||||
|
||||
@@ -186,7 +205,9 @@ namespace Nitload.Common.HighPerformance.Memory
|
||||
Vector128<byte> target_empty_v128 = Vector128.Create((byte)SwissControl.Empty);
|
||||
Vector128<byte> cmp_empty = Sse2.CompareEqual(control_bytes_v128, target_empty_v128);
|
||||
if (Sse2.MoveMask(cmp_empty) != 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
begin = (begin + 1) & (GroupCount - 1);
|
||||
}
|
||||
|
||||
@@ -54,6 +54,10 @@ namespace Nitload.Common.HighPerformance.Memory
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public partial Handle Map(
|
||||
AddressSource source
|
||||
);
|
||||
|
||||
internal void Collect()
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
@@ -77,5 +81,7 @@ namespace Nitload.Common.HighPerformance.Memory
|
||||
return _bufferAllocator.TryAlloc(size, out handle);
|
||||
}
|
||||
|
||||
internal partial void Predict();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -1,32 +0,0 @@
|
||||
#import "template.typ": cover, conf
|
||||
|
||||
// ===== 封面 =====
|
||||
// 字段均可选,自动排版:留空的字段会自动跳过。
|
||||
#cover(
|
||||
title: "Very长的文档标题示",
|
||||
title-en: "Very Long Document Title Example",
|
||||
subtitle: "副标题 Subtitle",
|
||||
date: "2026 年 6 月",
|
||||
)
|
||||
|
||||
// ===== 正文 =====
|
||||
#show: conf.with(number-pages: true)
|
||||
|
||||
= 第一章 引言
|
||||
|
||||
这是正文示例。正文中文使用宋体,English text uses Times New Roman by default。
|
||||
段落首行自动缩进两字符,两端对齐。
|
||||
|
||||
== 1.1 小节
|
||||
|
||||
行内代码示例:`let x = 1`,以及代码块:
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
println!("Hello, Typst!");
|
||||
}
|
||||
```
|
||||
|
||||
= 第二章 结论
|
||||
|
||||
这里是结论部分的内容。
|
||||
@@ -1,172 +0,0 @@
|
||||
// ============================================================
|
||||
// A4 文档模板
|
||||
// 封面:页边距 0,中文黑体 / 英文 Source Code Pro
|
||||
// 正文:上下 3.5cm,左右 2cm,中文宋体 / 英文 Times New Roman
|
||||
// 代码块:Source Code Pro
|
||||
// ============================================================
|
||||
|
||||
// ---- 字体配置(可按需修改)----
|
||||
#let cover-cjk = "SimHei" // 封面中文:黑体
|
||||
#let cover-latin = "Source Code Pro" // 封面英文
|
||||
#let body-cjk = "SimSun" // 正文中文:宋体
|
||||
#let body-latin = "Times New Roman" // 正文英文:新罗马
|
||||
#let mono-font = "Source Code Pro" // 代码块
|
||||
|
||||
// ---- 封面网格系统 ----
|
||||
// 坐标系:左上角为原点,x 向右、y 向下;坐标记为 (x, y) 即 (列, 行)。
|
||||
// 1 单位 = 1.2cm,一个单位方块为边长 1.5cm 的正方形。
|
||||
#let unit = 1.2cm
|
||||
#let sky = rgb("#4FC3F7") // 天蓝色
|
||||
#let ochre = rgb("#C9A24B") // 土黄色
|
||||
|
||||
// 在网格 (x, y) 处放置内容(以页面左上角为基准)
|
||||
#let cell(x, y, body) = place(top + left, dx: x * unit, dy: y * unit, body)
|
||||
|
||||
// 实心单位正方形
|
||||
#let sq(x, y, color) = cell(x, y, rect(width: unit, height: unit, fill: color, stroke: none))
|
||||
|
||||
// 单位等腰直角三角形,corner 指定直角所在角:
|
||||
// "tl" 左上 / "tr" 右上 / "bl" 左下 / "br" 右下
|
||||
#let tri(x, y, corner, color) = cell(x, y, {
|
||||
let pts = if corner == "tr" { ((0pt, 0pt), (unit, 0pt), (unit, unit)) }
|
||||
else if corner == "tl" { ((0pt, 0pt), (unit, 0pt), (0pt, unit)) }
|
||||
else if corner == "bl" { ((0pt, 0pt), (0pt, unit), (unit, unit)) }
|
||||
else { ((unit, 0pt), (unit, unit), (0pt, unit)) }
|
||||
polygon(fill: color, stroke: none, ..pts)
|
||||
})
|
||||
|
||||
// 单元格字母字号(NITLOAD 与主标题共用,保证视觉一致)
|
||||
#let glyph-size = 30pt
|
||||
|
||||
// 在单位格内居中放置一个字母
|
||||
#let glyph(x, y, ch, color) = cell(x, y, box(
|
||||
width: unit, height: unit, inset: 0pt,
|
||||
align(center + horizon, text(font: cover-cjk, size: glyph-size, weight: "medium", fill: color)[#ch]),
|
||||
))
|
||||
|
||||
// ---- 封面 ----
|
||||
// 所有字段均可选,留空则自动跳过,实现自动排版。
|
||||
#let cover(
|
||||
title: none, // 主标题(中文)
|
||||
title-en: none, // 主标题(英文,Source Code Pro 自行排版)
|
||||
subtitle: none, // 副标题
|
||||
logo: none, // 图片路径,如 image("logo.png")
|
||||
author: none, // 作者(可为单个字符串或数组)
|
||||
info: (:), // 附加信息字典,如 (学号: "123", 指导教师: "张三")
|
||||
date: none, // 日期
|
||||
fill: white, // 封面背景色
|
||||
decorate: true, // 是否绘制天蓝/土黄网格装饰色块
|
||||
) = {
|
||||
// 封面页:页边距为 0
|
||||
set page(
|
||||
paper: "a4",
|
||||
margin: 0pt,
|
||||
fill: fill,
|
||||
header: none,
|
||||
footer: none,
|
||||
)
|
||||
set text(font: (cover-latin, cover-cjk), lang: "zh")
|
||||
|
||||
// 网格装饰:NITLOAD 标识(坐标 (x, y) = (列, 行))。
|
||||
if decorate {
|
||||
// 天蓝色实心单位正方形
|
||||
// 土黄色实心单位正方形
|
||||
sq(0, 0, sky); sq(1, 0, sky); sq(2, 0, ochre)
|
||||
sq(0, 1, sky); sq(2, 1, sky)
|
||||
sq(0, 2, sky); sq(2, 2, sky)
|
||||
sq(0, 3, sky); sq(2, 3, sky)
|
||||
sq(0, 4, sky); sq(2, 4, sky)
|
||||
sq(0, 5, sky); sq(2, 5, sky)
|
||||
|
||||
// NITLOAD 竖排字样(列 x=1, 行 y=0..6),N 为土黄,其余天蓝
|
||||
let letters = ("N", "I", "T", "L", "O", "A", "D")
|
||||
for (i, ch) in letters.enumerate() {
|
||||
glyph(1, i, ch, if i == 0 { ochre } else { sky })
|
||||
}
|
||||
|
||||
// 主标题:中文逐字对齐网格,英文居中于单元格行。
|
||||
// 第 1 列到右缘的整格数 = 中文每行字数。
|
||||
let title-cols = calc.floor((21cm - 1 * unit) / unit)
|
||||
let title-width = 21cm - 1 * unit
|
||||
let cs = if type(title) == str { title.clusters() } else { () }
|
||||
|
||||
// 中文逐字对齐网格;混排西文字母按 2/3 格宽打包,预留整数格。
|
||||
// 中文视觉重心偏大,整体右移 0.1 格、下移 0.2 格
|
||||
let cn-dx = 0.1 * unit
|
||||
let cn-dy = 0.2 * unit
|
||||
let is-narrow = c => str.to-unicode(c.codepoints().first()) < 0x2E80
|
||||
// 分段:连续西文合为一段,其余每字一段
|
||||
let segs = ()
|
||||
let run = ""
|
||||
for ch in cs {
|
||||
if is-narrow(ch) { run += ch }
|
||||
else {
|
||||
if run != "" { segs.push(run); run = "" }
|
||||
segs.push(ch)
|
||||
}
|
||||
}
|
||||
if run != "" { segs.push(run) }
|
||||
|
||||
// 逐段放置:CJK 占 1 格,西文段占 ceil(字数 * 1/2) 格,溢出换行
|
||||
let cur = 0
|
||||
let row = 0
|
||||
for seg in segs {
|
||||
let w = if is-narrow(seg.first()) { calc.ceil(seg.len() * 1 / 2) } else { 1 }
|
||||
if cur + w > title-cols { cur = 0; row += 1 }
|
||||
let col = 1 + cur
|
||||
place(top + left, dx: col * unit + cn-dx, dy: (7 + row) * unit + cn-dy,
|
||||
box(width: w * unit, height: unit, inset: 0pt, align(center + horizon,
|
||||
text(font: (cover-cjk, cover-latin), size: glyph-size, fill: black)[#seg])))
|
||||
cur += w
|
||||
}
|
||||
let cn-rows = if segs.len() == 0 { 0 } else { row + 1 }
|
||||
|
||||
// 英文:左对齐于中文下方;多行时行距对齐网格(行距 = unit),左/上边距相等。
|
||||
// 英文行数需测量,故把依赖行数的笔画/三角形一并放入 context。
|
||||
context {
|
||||
let en-rows = 0
|
||||
if title-en != none {
|
||||
let en1 = text(font: (cover-latin, cover-cjk), size: glyph-size, fill: black)[#title-en]
|
||||
let lh = measure(en1).height
|
||||
let pad = (unit - lh) / 2
|
||||
let inner = {
|
||||
set par(leading: unit - lh)
|
||||
en1
|
||||
}
|
||||
let h = measure(box(width: title-width - pad, inner)).height
|
||||
en-rows = calc.max(1, int(calc.round((h - lh) / unit)) + 1)
|
||||
cell(1, 7 + cn-rows, box(width: title-width, inset: (left: pad, top: pad), inner))
|
||||
}
|
||||
let title-rows = cn-rows + en-rows
|
||||
// 左侧一笔向下延伸至主标题下端,三角形收尾;右侧保持原样
|
||||
for r in range(6, 6 + title-rows) { sq(0, r, sky) }
|
||||
tri(0, 6 + title-rows, "tr", sky)
|
||||
}
|
||||
tri(2, 6, "tl", sky)
|
||||
}
|
||||
|
||||
pagebreak(weak: true)
|
||||
}
|
||||
|
||||
// ---- 正文配置 ----
|
||||
#let conf(
|
||||
number-pages: true, // 是否显示页码
|
||||
doc,
|
||||
) = {
|
||||
set page(
|
||||
paper: "a4",
|
||||
margin: (top: 3.5cm, bottom: 3.5cm, left: 2cm, right: 2cm),
|
||||
numbering: if number-pages { "1" } else { none },
|
||||
number-align: center,
|
||||
)
|
||||
set text(font: (body-latin, body-cjk), lang: "zh", size: 12pt)
|
||||
set par(justify: true, leading: 1em, first-line-indent: (amount: 2em, all: true))
|
||||
|
||||
// 标题字体:中文黑体,英文 Source Code Pro
|
||||
show heading: set text(font: (cover-latin, cover-cjk))
|
||||
|
||||
// 代码块与行内代码:Source Code Pro
|
||||
show raw: set text(font: mono-font)
|
||||
|
||||
doc
|
||||
}
|
||||
Reference in New Issue
Block a user