Benchmark Rust against pinned C# reference (#105)
Some checks failed
API and SemVer surface / api-surface (push) Failing after 1m8s
Native code generation / deterministic (push) Failing after 2m6s
Concurrency and resource soak audit / soak (push) Failing after 12m13s
Documentation / documentation (push) Failing after 1m36s
Imaging and meshing gate / native (push) Failing after 3m2s
JPEG 2000 feature / linux (push) Successful in 2m48s
performance evidence / audit (push) Failing after 13m49s
Release platform and feature matrix / audit (push) Successful in 44s
Native Rust workspace compile / compile (push) Failing after 55s
Skia feature / linux (push) Successful in 31m13s
Dependency and supply-chain audit / audit (push) Failing after 9m13s
Release platform and feature matrix / matrix (false, linux-stable-minimal, x86_64-unknown-linux-gnu, stable) (push) Failing after 9m49s
Release platform and feature matrix / matrix (false, windows-stable-portable, x86_64-pc-windows-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-msrv-portable, x86_64-unknown-linux-gnu, 1.96.0) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-stable-default, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-stable-features, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-stable-release-surface, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (false, macos-stable-portable, x86_64-apple-darwin, stable) (push) Has been cancelled
Some checks failed
API and SemVer surface / api-surface (push) Failing after 1m8s
Native code generation / deterministic (push) Failing after 2m6s
Concurrency and resource soak audit / soak (push) Failing after 12m13s
Documentation / documentation (push) Failing after 1m36s
Imaging and meshing gate / native (push) Failing after 3m2s
JPEG 2000 feature / linux (push) Successful in 2m48s
performance evidence / audit (push) Failing after 13m49s
Release platform and feature matrix / audit (push) Successful in 44s
Native Rust workspace compile / compile (push) Failing after 55s
Skia feature / linux (push) Successful in 31m13s
Dependency and supply-chain audit / audit (push) Failing after 9m13s
Release platform and feature matrix / matrix (false, linux-stable-minimal, x86_64-unknown-linux-gnu, stable) (push) Failing after 9m49s
Release platform and feature matrix / matrix (false, windows-stable-portable, x86_64-pc-windows-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-msrv-portable, x86_64-unknown-linux-gnu, 1.96.0) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-stable-default, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-stable-features, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (true, linux-stable-release-surface, x86_64-unknown-linux-gnu, stable) (push) Has been cancelled
Release platform and feature matrix / matrix (false, macos-stable-portable, x86_64-apple-darwin, stable) (push) Has been cancelled
This commit is contained in:
53
.gitea/workflows/performance.yml
Normal file
53
.gitea/workflows/performance.yml
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
name: performance evidence
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- "benchmarks/**"
|
||||||
|
- "tools/performance/**"
|
||||||
|
- "crates/libremetaverse/src/inventory.rs"
|
||||||
|
- "crates/libremetaverse/src/targa.rs"
|
||||||
|
- ".gitea/workflows/performance.yml"
|
||||||
|
- "Cargo.toml"
|
||||||
|
- "Cargo.lock"
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- "benchmarks/**"
|
||||||
|
- "tools/performance/**"
|
||||||
|
- "crates/libremetaverse/src/inventory.rs"
|
||||||
|
- "crates/libremetaverse/src/targa.rs"
|
||||||
|
- ".gitea/workflows/performance.yml"
|
||||||
|
- "Cargo.toml"
|
||||||
|
- "Cargo.lock"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
audit:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
|
with:
|
||||||
|
toolchain: 1.97.1
|
||||||
|
components: rustfmt, clippy
|
||||||
|
- uses: actions/setup-dotnet@v4
|
||||||
|
with:
|
||||||
|
dotnet-version: |
|
||||||
|
8.0.x
|
||||||
|
10.0.x
|
||||||
|
- name: Check out pinned C# reference
|
||||||
|
run: |
|
||||||
|
git clone --filter=blob:none https://github.com/cinderblocks/libremetaverse.git "$RUNNER_TEMP/libremetaverse"
|
||||||
|
git -C "$RUNNER_TEMP/libremetaverse" checkout 2aa70bb68513b39795da5d13c88f31b86e85a3ba
|
||||||
|
- name: Compile separate C# reference runner
|
||||||
|
run: >-
|
||||||
|
dotnet build benchmarks/csharp-reference/MetaCrate.ReferenceBenchmarks.csproj
|
||||||
|
-c Release -p:ReferenceRoot="$RUNNER_TEMP/libremetaverse"
|
||||||
|
- name: Verify harness
|
||||||
|
run: cargo test -p metacrate-performance --profile benchmark --locked -j1
|
||||||
|
- name: Audit committed evidence
|
||||||
|
run: >-
|
||||||
|
cargo run -p metacrate-performance --profile benchmark --locked -j1 --
|
||||||
|
audit --fixture-root benchmarks/fixtures
|
||||||
|
--rust benchmarks/results/rust-linux-x86_64.json
|
||||||
|
--reference benchmarks/results/csharp-linux-x86_64.json
|
||||||
|
--comparison benchmarks/results/comparison.json
|
||||||
16
Cargo.lock
generated
16
Cargo.lock
generated
@@ -1791,6 +1791,22 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "metacrate-performance"
|
||||||
|
version = "0.0.1"
|
||||||
|
dependencies = [
|
||||||
|
"libremetaverse",
|
||||||
|
"libremetaverse-imaging",
|
||||||
|
"libremetaverse-rendering-mesh-foundry",
|
||||||
|
"libremetaverse-rendering-simple",
|
||||||
|
"libremetaverse-structured-data",
|
||||||
|
"libremetaverse-types",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"sha2 0.11.0",
|
||||||
|
"stats_alloc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "minimal-lexical"
|
name = "minimal-lexical"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ members = [
|
|||||||
"tools/codegen",
|
"tools/codegen",
|
||||||
"tools/ci-matrix",
|
"tools/ci-matrix",
|
||||||
"tools/concurrency-audit",
|
"tools/concurrency-audit",
|
||||||
|
"tools/performance",
|
||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
|
|||||||
90
benchmarks/README.md
Normal file
90
benchmarks/README.md
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
# Pinned cross-runtime performance evidence
|
||||||
|
|
||||||
|
This directory compares representative offline work in native Rust with the
|
||||||
|
pinned C# source at commit `2aa70bb68513b39795da5d13c88f31b86e85a3ba`.
|
||||||
|
The runners are separate executables. The Rust executable never starts a CLR,
|
||||||
|
loads a managed assembly, accesses a grid, or reads `.env`.
|
||||||
|
|
||||||
|
## Workloads and fixtures
|
||||||
|
|
||||||
|
`fixtures/manifest.json` records SHA-256 for every shared input. Both runners
|
||||||
|
refuse a changed fixture. The suite measures UUID/matrix/quaternion operations,
|
||||||
|
all five supported LLSD codecs, packet encode/decode, notecard/TGA/mesh decode,
|
||||||
|
inventory and object updates, simple rendering, and a batched offline client
|
||||||
|
message pipeline. Each report contains one cold operation and seven warm
|
||||||
|
samples. It records latency, throughput, allocated bytes, retained heap bytes,
|
||||||
|
and allocation count where the runtime exposes it. .NET reports a null
|
||||||
|
allocation count because its supported in-process GC API exposes bytes, not
|
||||||
|
operation count; it does not substitute a misleading zero.
|
||||||
|
|
||||||
|
The checked-in inputs are deliberately modest so the suite completes on a
|
||||||
|
four-core, 8 GiB release worker. Iteration counts live in `workloads.json`.
|
||||||
|
Regenerate binary inputs only after an intentional fixture review:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cargo run -p metacrate-performance --profile benchmark -- \
|
||||||
|
fixtures --fixture-root benchmarks/fixtures
|
||||||
|
```
|
||||||
|
|
||||||
|
## Reproducing the reports
|
||||||
|
|
||||||
|
Use the same otherwise-idle machine for both runs. Disable adaptive power or
|
||||||
|
thermal throttling where the host permits it. The committed evidence was
|
||||||
|
captured on Linux x86-64 with four AMD EPYC-Genoa vCPUs and 7.6 GiB RAM, Rust
|
||||||
|
1.97.1, .NET SDK 10.0.400, and the .NET 8.0.30 runtime. C# is built in Release
|
||||||
|
for `net8.0`. Rust uses the repository's `benchmark` profile (opt-level 1,
|
||||||
|
debug/incremental disabled): higher optimization of the generated core exceeds
|
||||||
|
the memory limit on this release worker, so this is a conservative Rust result.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
REFERENCE_ROOT=/absolute/path/to/libremetaverse
|
||||||
|
test "$(git -C "$REFERENCE_ROOT" rev-parse HEAD)" = \
|
||||||
|
2aa70bb68513b39795da5d13c88f31b86e85a3ba
|
||||||
|
|
||||||
|
dotnet build benchmarks/csharp-reference/MetaCrate.ReferenceBenchmarks.csproj \
|
||||||
|
-c Release -p:ReferenceRoot="$REFERENCE_ROOT"
|
||||||
|
dotnet benchmarks/csharp-reference/bin/Release/net8.0/MetaCrate.ReferenceBenchmarks.dll \
|
||||||
|
run --reference-root "$REFERENCE_ROOT" \
|
||||||
|
--fixture-root benchmarks/fixtures \
|
||||||
|
--output benchmarks/results/csharp-linux-x86_64.json
|
||||||
|
|
||||||
|
cargo run -p metacrate-performance --profile benchmark -- \
|
||||||
|
run --fixture-root benchmarks/fixtures \
|
||||||
|
--output benchmarks/results/rust-linux-x86_64.json
|
||||||
|
cargo run -p metacrate-performance --profile benchmark -- \
|
||||||
|
compare --rust benchmarks/results/rust-linux-x86_64.json \
|
||||||
|
--reference benchmarks/results/csharp-linux-x86_64.json \
|
||||||
|
--output benchmarks/results/comparison.json
|
||||||
|
cargo run -p metacrate-performance --profile benchmark -- \
|
||||||
|
audit --fixture-root benchmarks/fixtures \
|
||||||
|
--rust benchmarks/results/rust-linux-x86_64.json \
|
||||||
|
--reference benchmarks/results/csharp-linux-x86_64.json \
|
||||||
|
--comparison benchmarks/results/comparison.json
|
||||||
|
```
|
||||||
|
|
||||||
|
The C# runner executes `git rev-parse` itself and rejects any other source
|
||||||
|
commit. The comparison rejects different fixture hashes, operating systems, or
|
||||||
|
architectures.
|
||||||
|
|
||||||
|
## Release criteria and reviewed differences
|
||||||
|
|
||||||
|
`release-criteria.json` is executable policy. A warm median at least 25% slower
|
||||||
|
than the reference is material and must be reviewed; the hard default is no
|
||||||
|
more than 2.0x latency or allocated bytes. Reference operations below one
|
||||||
|
microsecond are latency-noise exempt, but not allocation exempt.
|
||||||
|
|
||||||
|
Three workload-specific differences are accepted:
|
||||||
|
|
||||||
|
- Rust LLSD JSON owns and validates the bounded decoded tree. It is over four
|
||||||
|
times faster in this evidence, so up to 2.1x allocated bytes is accepted.
|
||||||
|
- Rust rendering performs the completed behavior: six faces, 24 vertices, 36
|
||||||
|
indices, checked normals, UVs, extents, and materials. The pinned C#
|
||||||
|
`SimpleRenderer` still returns a one-face/eight-vertex placeholder cube.
|
||||||
|
Slowing Rust down by removing correctness would violate the milestone, so the
|
||||||
|
policy accepts up to 8x latency and 4x allocated bytes for this workload.
|
||||||
|
- Rust client throughput returns checked `Result` values and owns the bounded
|
||||||
|
OSD map throughout the message pipeline. It allocates fewer bytes than C#;
|
||||||
|
its measured 1.64x latency is reviewed up to the unchanged 2x hard limit.
|
||||||
|
|
||||||
|
No exception permits changed wire data, decoded values, fixture output, or
|
||||||
|
public behavior. Any other threshold failure blocks the audit.
|
||||||
41
benchmarks/REPORT.md
Normal file
41
benchmarks/REPORT.md
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
# Performance comparison report
|
||||||
|
|
||||||
|
Accepted evidence captured 2026-08-12 on the Linux x86-64 host documented in
|
||||||
|
`README.md`. Ratios are Rust divided by the pinned C# reference; lower is
|
||||||
|
better. Bytes are allocated bytes per operation. Full cold and seven-sample
|
||||||
|
warm measurements, throughput, allocation count support, retained heap, hashes,
|
||||||
|
and toolchains are in `results/`.
|
||||||
|
|
||||||
|
| Workload | Rust latency | C# latency | Ratio | Rust bytes | C# bytes | Result |
|
||||||
|
|---|---:|---:|---:|---:|---:|---|
|
||||||
|
| UUID/math | 107 ns | 105 ns | 1.02x | 36 | 40 | pass |
|
||||||
|
| LLSD XML | 23.36 us | 38.74 us | 0.60x | 10,939 | 28,600 | pass |
|
||||||
|
| LLSD JSON | 5.23 us | 21.72 us | 0.24x | 7,447 | 3,680 | reviewed pass |
|
||||||
|
| LLSD binary | 2.69 us | 11.07 us | 0.24x | 6,749 | 7,960 | pass |
|
||||||
|
| LLSD notation | 7.01 us | 18.87 us | 0.37x | 8,489 | 10,096 | pass |
|
||||||
|
| LLSD protobuf | 7.34 us | 30.97 us | 0.24x | 8,324 | 26,120 | pass |
|
||||||
|
| Packet codec | 209 ns | 176 ns | 1.19x | 124 | 264 | pass |
|
||||||
|
| Asset decode | 288 ns | 2.98 us | 0.10x | 1,246 | 4,512 | pass |
|
||||||
|
| Image decode | 42.67 us | 48.27 us | 0.88x | 65,536 | 131,612 | pass |
|
||||||
|
| Mesh decode | 413.29 us | 378.54 us | 1.09x | 507,740 | 564,593 | pass |
|
||||||
|
| Inventory update | 972 ns | 758 ns | 1.28x | 373 | 357 | pass |
|
||||||
|
| Object update | 27 ns | 46 ns | 0.59x | 16 | 11 | pass |
|
||||||
|
| Rendering | 5.22 us | 1.01 us | 5.15x | 9,480 | 3,048 | reviewed pass |
|
||||||
|
| Client throughput | 6.23 us | 3.80 us | 1.64x | 11,795 | 12,054 | reviewed pass |
|
||||||
|
|
||||||
|
The initial profile exposed two actionable regressions. Root inventory updates
|
||||||
|
rebuilt the entire hierarchy and link index on every record; the new guarded
|
||||||
|
root path updates only the affected node/link and changed the comparison from
|
||||||
|
about 22.6x latency and 10.2x allocation to 1.28x and 1.05x. TGA decoding copied
|
||||||
|
the input stream and dispatched every ordinary pixel through the fully generic
|
||||||
|
orientation/palette path; the checked borrowed-input and common-layout path
|
||||||
|
changed it from about 3.4x latency and 2.0x allocation to 0.88x and 0.50x.
|
||||||
|
|
||||||
|
LLSD JSON, rendering, and client throughput are the reviewed differences. JSON
|
||||||
|
trades 2.02x allocated bytes for 4.15x lower latency. Rendering cannot be
|
||||||
|
compared as equal work: the pinned C# method is a placeholder one-face cube,
|
||||||
|
while Rust returns the required complete checked mesh. Client throughput is
|
||||||
|
1.64x slower while allocating fewer bytes because Rust preserves checked
|
||||||
|
results and bounded owned maps. The executable limits and rationales are in
|
||||||
|
`release-criteria.json`; every other workload passes the default criteria or
|
||||||
|
the documented sub-microsecond noise rule.
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
|
<ReferenceRoot Condition="'$(ReferenceRoot)' == ''">$(MSBuildThisFileDirectory)../../../libremetaverse</ReferenceRoot>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="$(ReferenceRoot)/LibreMetaverse/LibreMetaverse.csproj" />
|
||||||
|
<ProjectReference Include="$(ReferenceRoot)/LibreMetaverse.Rendering.Simple/LibreMetaverse.Rendering.Simple.csproj" />
|
||||||
|
<ProjectReference Include="$(ReferenceRoot)/LibreMetaverse.Rendering.MeshFoundry/LibreMetaverse.Rendering.MeshFoundry.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
346
benchmarks/csharp-reference/Program.cs
Normal file
346
benchmarks/csharp-reference/Program.cs
Normal file
@@ -0,0 +1,346 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text.Json;
|
||||||
|
using LibreMetaverse;
|
||||||
|
using LibreMetaverse.Assets;
|
||||||
|
using LibreMetaverse.Imaging;
|
||||||
|
using LibreMetaverse.Messages.Linden;
|
||||||
|
using LibreMetaverse.Packets;
|
||||||
|
using LibreMetaverse.Rendering;
|
||||||
|
using LibreMetaverse.StructuredData;
|
||||||
|
using IOPath = System.IO.Path;
|
||||||
|
|
||||||
|
const int Schema = 1;
|
||||||
|
const int Runs = 7;
|
||||||
|
const string ExpectedReference = "2aa70bb68513b39795da5d13c88f31b86e85a3ba";
|
||||||
|
|
||||||
|
if (args.Length == 0 || args[0] != "run")
|
||||||
|
throw new ArgumentException("usage: dotnet run ... -- run --fixture-root PATH --output FILE");
|
||||||
|
string root = Value("--fixture-root") ?? "benchmarks/fixtures";
|
||||||
|
string output = Value("--output") ?? "benchmarks/results/csharp-linux-x86_64.json";
|
||||||
|
string referenceRoot = Value("--reference-root") ?? throw new ArgumentException("missing --reference-root");
|
||||||
|
VerifyReferenceCommit(referenceRoot);
|
||||||
|
Fixture fixture = JsonSerializer.Deserialize<Fixture>(File.ReadAllBytes(IOPath.Combine(root, "workloads.json")), Options())
|
||||||
|
?? throw new InvalidDataException("empty fixture");
|
||||||
|
if (fixture.Schema != 1 || fixture.ReferenceCommit != ExpectedReference || fixture.WarmupDivisor == 0)
|
||||||
|
throw new InvalidDataException("fixture schema or reference commit does not match policy");
|
||||||
|
Dictionary<string, string> hashes = VerifyManifest(root);
|
||||||
|
var context = new Context(fixture, root);
|
||||||
|
var results = new List<BenchmarkResult>();
|
||||||
|
foreach (Workload workload in Workloads())
|
||||||
|
{
|
||||||
|
int iterations = workload.Iterations(fixture);
|
||||||
|
Metrics cold = Measure(context, workload.Run, 1);
|
||||||
|
GC.KeepAlive(workload.Run(context, Math.Max(1, iterations / fixture.WarmupDivisor)));
|
||||||
|
var samples = new List<Metrics>();
|
||||||
|
for (int index = 0; index < Runs; index++) samples.Add(Measure(context, workload.Run, iterations));
|
||||||
|
Metrics median = samples.OrderBy(value => value.ElapsedNs).ElementAt(Runs / 2);
|
||||||
|
results.Add(new BenchmarkResult(workload.Name, workload.Category, "operation", workload.Fixture, cold, samples, median));
|
||||||
|
}
|
||||||
|
var report = new Report(
|
||||||
|
Schema,
|
||||||
|
"dotnet-reference",
|
||||||
|
ExpectedReference,
|
||||||
|
hashes,
|
||||||
|
new EnvironmentInfo(
|
||||||
|
OperatingSystem.IsLinux() ? "linux" : OperatingSystem.IsWindows() ? "windows" : OperatingSystem.IsMacOS() ? "macos" : RuntimeInformation.OSDescription,
|
||||||
|
RuntimeInformation.ProcessArchitecture switch { Architecture.X64 => "x86_64", Architecture.Arm64 => "aarch64", Architecture.X86 => "x86", Architecture.Arm => "arm", _ => RuntimeInformation.ProcessArchitecture.ToString().ToLowerInvariant() },
|
||||||
|
Environment.ProcessorCount,
|
||||||
|
RuntimeInformation.FrameworkDescription,
|
||||||
|
"Release net8.0"),
|
||||||
|
results);
|
||||||
|
Directory.CreateDirectory(IOPath.GetDirectoryName(IOPath.GetFullPath(output))!);
|
||||||
|
File.WriteAllText(output, JsonSerializer.Serialize(report, Options()) + "\n");
|
||||||
|
|
||||||
|
string? Value(string key)
|
||||||
|
{
|
||||||
|
int index = Array.IndexOf(args, key);
|
||||||
|
return index >= 0 && index + 1 < args.Length ? args[index + 1] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static JsonSerializerOptions Options() => new()
|
||||||
|
{
|
||||||
|
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
|
||||||
|
WriteIndented = true
|
||||||
|
};
|
||||||
|
|
||||||
|
static Dictionary<string, string> VerifyManifest(string root)
|
||||||
|
{
|
||||||
|
var expected = JsonSerializer.Deserialize<Dictionary<string, string>>(
|
||||||
|
File.ReadAllBytes(IOPath.Combine(root, "manifest.json")), Options())
|
||||||
|
?? throw new InvalidDataException("empty manifest");
|
||||||
|
foreach ((string name, string hash) in expected)
|
||||||
|
{
|
||||||
|
string actual = Convert.ToHexString(SHA256.HashData(File.ReadAllBytes(IOPath.Combine(root, name)))).ToLowerInvariant();
|
||||||
|
if (actual != hash) throw new InvalidDataException($"fixture hash mismatch for {name}");
|
||||||
|
}
|
||||||
|
return expected;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VerifyReferenceCommit(string referenceRoot)
|
||||||
|
{
|
||||||
|
var start = new ProcessStartInfo("git")
|
||||||
|
{
|
||||||
|
RedirectStandardOutput = true,
|
||||||
|
UseShellExecute = false
|
||||||
|
};
|
||||||
|
start.ArgumentList.Add("-C");
|
||||||
|
start.ArgumentList.Add(referenceRoot);
|
||||||
|
start.ArgumentList.Add("rev-parse");
|
||||||
|
start.ArgumentList.Add("HEAD");
|
||||||
|
var process = Process.Start(start) ?? throw new InvalidOperationException("could not start git");
|
||||||
|
string commit = process.StandardOutput.ReadToEnd().Trim();
|
||||||
|
process.WaitForExit();
|
||||||
|
if (process.ExitCode != 0 || commit != ExpectedReference)
|
||||||
|
throw new InvalidDataException($"reference checkout must be pinned to {ExpectedReference}, got {commit}");
|
||||||
|
}
|
||||||
|
|
||||||
|
static Metrics Measure(Context context, Func<Context, int, ulong> function, int iterations)
|
||||||
|
{
|
||||||
|
long allocatedBefore = GC.GetAllocatedBytesForCurrentThread();
|
||||||
|
long heapBefore = GC.GetTotalMemory(false);
|
||||||
|
long started = Stopwatch.GetTimestamp();
|
||||||
|
ulong checksum = function(context, iterations);
|
||||||
|
long elapsed = Stopwatch.GetTimestamp() - started;
|
||||||
|
long heapAfter = GC.GetTotalMemory(false);
|
||||||
|
long allocated = GC.GetAllocatedBytesForCurrentThread() - allocatedBefore;
|
||||||
|
GC.KeepAlive(checksum);
|
||||||
|
long elapsedNs = checked((long)(elapsed * (1_000_000_000.0 / Stopwatch.Frequency)));
|
||||||
|
double seconds = elapsedNs / 1_000_000_000.0;
|
||||||
|
return new Metrics(iterations, elapsedNs, (double)elapsedNs / iterations, iterations / seconds, null, allocated, heapAfter - heapBefore);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Workload[] Workloads() =>
|
||||||
|
[
|
||||||
|
new("uuid_math", "uuid/math", "workloads.json", f => f.UuidMathIterations, UuidMath),
|
||||||
|
new("llsd_xml", "LLSD", "workloads.json", f => f.LlsdIterations, LlsdXml),
|
||||||
|
new("llsd_json", "LLSD", "workloads.json", f => f.LlsdIterations, LlsdJson),
|
||||||
|
new("llsd_binary", "LLSD", "workloads.json", f => f.LlsdIterations, LlsdBinary),
|
||||||
|
new("llsd_notation", "LLSD", "workloads.json", f => f.LlsdIterations, LlsdNotation),
|
||||||
|
new("llsd_protobuf", "LLSD", "workloads.json", f => f.LlsdIterations, LlsdProtobuf),
|
||||||
|
new("packet_codec", "packet codec", "agent_pause.packet", f => f.PacketIterations, PacketCodec),
|
||||||
|
new("asset_decode", "asset decode", "notecard.txt", f => f.AssetIterations, AssetDecode),
|
||||||
|
new("image_decode", "image decode", "image.tga", f => f.ImageIterations, ImageDecode),
|
||||||
|
new("mesh_decode", "mesh decode", "mesh_asset.bin", f => f.MeshIterations, MeshDecode),
|
||||||
|
new("inventory_update", "inventory update", "workloads.json", f => f.InventoryIterations, InventoryUpdate),
|
||||||
|
new("object_update", "object update", "workloads.json", f => f.ObjectIterations, ObjectUpdate),
|
||||||
|
new("rendering", "rendering", "workloads.json", f => f.RenderingIterations, Rendering),
|
||||||
|
new("client_throughput", "client throughput", "workloads.json", f => f.ClientIterations, ClientThroughput)
|
||||||
|
];
|
||||||
|
|
||||||
|
static ulong UuidMath(Context context, int iterations)
|
||||||
|
{
|
||||||
|
Quaternion rotation = Quaternion.CreateFromEulers(0.25f, 0.5f, 0.75f);
|
||||||
|
Matrix4 matrix = Matrix4.CreateTranslation(new Vector3(10, 20, 30));
|
||||||
|
ulong checksum = 0;
|
||||||
|
for (int index = 0; index < iterations; index++)
|
||||||
|
{
|
||||||
|
var parsed = new UUID(context.Fixture.Uuid);
|
||||||
|
matrix = Matrix4.Transform(matrix, rotation);
|
||||||
|
checksum ^= parsed.GetULong() ^ BitConverter.SingleToUInt32Bits(matrix.M41) ^ (ulong)index;
|
||||||
|
}
|
||||||
|
return checksum;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ulong LlsdXml(Context context, int iterations) => LlsdBytes(context, iterations, OSDParser.SerializeLLSDXmlBytes, OSDParser.DeserializeLLSDXml);
|
||||||
|
static ulong LlsdBinary(Context context, int iterations) => LlsdBytes(context, iterations, OSDParser.SerializeLLSDBinary, OSDParser.DeserializeLLSDBinary);
|
||||||
|
static ulong LlsdProtobuf(Context context, int iterations) => LlsdBytes(context, iterations, value => OSDParser.SerializeLLSDProtobuf(value, true), OSDParser.DeserializeLLSDProtobuf);
|
||||||
|
|
||||||
|
static ulong LlsdBytes(Context context, int iterations, Func<OSD, byte[]> serialize, Func<byte[], OSD> deserialize)
|
||||||
|
{
|
||||||
|
ulong checksum = 0;
|
||||||
|
for (int index = 0; index < iterations; index++)
|
||||||
|
{
|
||||||
|
byte[] encoded = serialize(context.Llsd);
|
||||||
|
OSD decoded = deserialize(encoded);
|
||||||
|
checksum ^= (ulong)encoded.Length ^ (decoded.AsBoolean() ? 1UL : 0UL);
|
||||||
|
}
|
||||||
|
return checksum;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ulong LlsdJson(Context context, int iterations)
|
||||||
|
{
|
||||||
|
ulong checksum = 0;
|
||||||
|
for (int index = 0; index < iterations; index++)
|
||||||
|
{
|
||||||
|
string encoded = OSDParser.SerializeJsonString(context.Llsd);
|
||||||
|
OSD decoded = OSDParser.DeserializeJson(encoded);
|
||||||
|
checksum ^= (ulong)encoded.Length ^ (decoded.AsBoolean() ? 1UL : 0UL);
|
||||||
|
}
|
||||||
|
return checksum;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ulong LlsdNotation(Context context, int iterations)
|
||||||
|
{
|
||||||
|
ulong checksum = 0;
|
||||||
|
for (int index = 0; index < iterations; index++)
|
||||||
|
{
|
||||||
|
string encoded = OSDParser.SerializeLLSDNotation(context.Llsd);
|
||||||
|
OSD decoded = OSDParser.DeserializeLLSDNotation(encoded);
|
||||||
|
checksum ^= (ulong)encoded.Length ^ (decoded.AsBoolean() ? 1UL : 0UL);
|
||||||
|
}
|
||||||
|
return checksum;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ulong PacketCodec(Context context, int iterations)
|
||||||
|
{
|
||||||
|
ulong checksum = 0;
|
||||||
|
for (int index = 0; index < iterations; index++)
|
||||||
|
{
|
||||||
|
int position = 0;
|
||||||
|
var packet = new AgentPausePacket(context.Packet, ref position);
|
||||||
|
byte[] encoded = packet.ToBytes();
|
||||||
|
checksum ^= (ulong)encoded.Length ^ packet.AgentData.SerialNum;
|
||||||
|
}
|
||||||
|
return checksum;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ulong AssetDecode(Context context, int iterations)
|
||||||
|
{
|
||||||
|
ulong checksum = 0;
|
||||||
|
for (int index = 0; index < iterations; index++)
|
||||||
|
{
|
||||||
|
var asset = new AssetNotecard(context.Uuid, context.Notecard);
|
||||||
|
if (!asset.Decode()) throw new InvalidDataException("notecard decode returned false");
|
||||||
|
checksum ^= (ulong)asset.BodyText.Length;
|
||||||
|
}
|
||||||
|
return checksum;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ulong ImageDecode(Context context, int iterations)
|
||||||
|
{
|
||||||
|
ulong checksum = 0;
|
||||||
|
for (int index = 0; index < iterations; index++)
|
||||||
|
{
|
||||||
|
using var stream = new MemoryStream(context.Image, false);
|
||||||
|
ManagedImage image = Targa.DecodeToManagedImage(stream);
|
||||||
|
checksum ^= (ulong)image.Red.Length;
|
||||||
|
}
|
||||||
|
return checksum;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ulong MeshDecode(Context context, int iterations)
|
||||||
|
{
|
||||||
|
var renderer = new MeshFoundry();
|
||||||
|
ulong checksum = 0;
|
||||||
|
for (int index = 0; index < iterations; index++)
|
||||||
|
{
|
||||||
|
FacetedMesh mesh = renderer.GenerateFacetedMeshMesh(new Primitive(), context.Mesh, DetailLevel.Highest)
|
||||||
|
?? throw new InvalidDataException("mesh fixture returned no geometry");
|
||||||
|
checksum ^= (ulong)mesh.Faces.Sum(face => face.Vertices.Count + face.Indices.Count);
|
||||||
|
}
|
||||||
|
return checksum;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ulong InventoryUpdate(Context context, int iterations)
|
||||||
|
{
|
||||||
|
var store = new Inventory(new GridClient(), context.Uuid);
|
||||||
|
for (int index = 0; index < iterations; index++)
|
||||||
|
{
|
||||||
|
var item = new InventoryItem(new UUID((ulong)(index % context.Fixture.InventoryItems + 1))) { Name = $"item-{index}" };
|
||||||
|
store.UpdateNodeFor(item);
|
||||||
|
}
|
||||||
|
return (ulong)store.Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ulong ObjectUpdate(Context context, int iterations)
|
||||||
|
{
|
||||||
|
Primitive[] objects = Enumerable.Range(0, context.Fixture.ObjectCount).Select(index => new Primitive { LocalID = (uint)index }).ToArray();
|
||||||
|
ulong checksum = 0;
|
||||||
|
for (int index = 0; index < iterations; index++)
|
||||||
|
{
|
||||||
|
Primitive value = objects[index % objects.Length];
|
||||||
|
value.Position = new Vector3(index, index & 255, 21);
|
||||||
|
value.Rotation = Quaternion.CreateFromAxisAngle(Vector3.UnitZ, (index & 255) / 255.0f);
|
||||||
|
checksum ^= value.LocalID ^ BitConverter.SingleToUInt32Bits(value.Position.X);
|
||||||
|
}
|
||||||
|
return checksum;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ulong Rendering(Context context, int iterations)
|
||||||
|
{
|
||||||
|
var renderer = new SimpleRenderer();
|
||||||
|
ulong checksum = 0;
|
||||||
|
for (int index = 0; index < iterations; index++)
|
||||||
|
{
|
||||||
|
FacetedMesh mesh = renderer.GenerateFacetedMesh(BoxPrimitive(), DetailLevel.High);
|
||||||
|
checksum ^= (ulong)mesh.Faces.Sum(face => face.Indices.Count);
|
||||||
|
}
|
||||||
|
return checksum;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Primitive BoxPrimitive() => new()
|
||||||
|
{
|
||||||
|
PrimData = new Primitive.ConstructionData
|
||||||
|
{
|
||||||
|
PCode = PCode.Prim, PathCurve = PathCurve.Line, ProfileCurve = ProfileCurve.Square,
|
||||||
|
PathScaleX = 1, PathScaleY = 1, PathBegin = 0, PathEnd = 1,
|
||||||
|
ProfileBegin = 0, ProfileEnd = 1, ProfileHollow = 0,
|
||||||
|
ProfileHole = HoleType.Same, PathRevolutions = 1
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static ulong ClientThroughput(Context context, int iterations)
|
||||||
|
{
|
||||||
|
var client = new GridClient();
|
||||||
|
ulong checksum = 0;
|
||||||
|
for (int index = 0; index < iterations; index++)
|
||||||
|
{
|
||||||
|
for (int batch = 0; batch < context.Fixture.ClientBatch; batch++)
|
||||||
|
{
|
||||||
|
var source = new RemoteParcelRequestReply { ParcelID = context.Uuid };
|
||||||
|
OSDMap map = source.Serialize();
|
||||||
|
var target = new RemoteParcelRequestReply();
|
||||||
|
target.Deserialize(map);
|
||||||
|
checksum ^= target.ParcelID.GetULong() ^ (ulong)index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GC.KeepAlive(client);
|
||||||
|
return checksum;
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class Context
|
||||||
|
{
|
||||||
|
public Context(Fixture fixture, string root)
|
||||||
|
{
|
||||||
|
Fixture = fixture;
|
||||||
|
Uuid = new UUID(fixture.Uuid);
|
||||||
|
Llsd = new OSDMap
|
||||||
|
{
|
||||||
|
["agent_id"] = OSD.FromUUID(Uuid), ["name"] = OSD.FromString("MetaCrate benchmark"),
|
||||||
|
["active"] = OSD.FromBoolean(true), ["score"] = OSD.FromReal(42.25),
|
||||||
|
["items"] = IntegerArray(fixture.LlsdArrayLength)
|
||||||
|
};
|
||||||
|
Packet = File.ReadAllBytes(IOPath.Combine(root, "agent_pause.packet"));
|
||||||
|
Notecard = File.ReadAllBytes(IOPath.Combine(root, "notecard.txt"));
|
||||||
|
Image = File.ReadAllBytes(IOPath.Combine(root, "image.tga"));
|
||||||
|
Mesh = File.ReadAllBytes(IOPath.Combine(root, "mesh_asset.bin"));
|
||||||
|
}
|
||||||
|
private static OSDArray IntegerArray(int count)
|
||||||
|
{
|
||||||
|
var values = new OSDArray(count);
|
||||||
|
for (int index = 0; index < count; index++) values.Add(OSD.FromInteger(index));
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
public Fixture Fixture { get; }
|
||||||
|
public UUID Uuid { get; }
|
||||||
|
public OSD Llsd { get; }
|
||||||
|
public byte[] Packet { get; }
|
||||||
|
public byte[] Notecard { get; }
|
||||||
|
public byte[] Image { get; }
|
||||||
|
public byte[] Mesh { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed record Fixture(
|
||||||
|
int Schema, string ReferenceCommit, string Uuid, int UuidMathIterations, int LlsdIterations,
|
||||||
|
int PacketIterations, int AssetIterations, int ImageIterations, int MeshIterations,
|
||||||
|
int InventoryIterations, int ObjectIterations, int RenderingIterations, int ClientIterations,
|
||||||
|
int WarmupDivisor, int LlsdArrayLength, int PacketBlocks, int ImageWidth, int ImageHeight,
|
||||||
|
int MeshSide, int InventoryItems, int ObjectCount, int ClientBatch);
|
||||||
|
sealed record Metrics(int Iterations, long ElapsedNs, double LatencyNs, double ThroughputPerSecond, long? Allocations, long BytesAllocated, long RetainedBytes);
|
||||||
|
sealed record BenchmarkResult(string Name, string Category, string Unit, string Fixture, Metrics Cold, List<Metrics> WarmSamples, Metrics WarmMedian);
|
||||||
|
sealed record EnvironmentInfo(string Os, string Arch, int LogicalCpus, string Toolchain, string Profile);
|
||||||
|
sealed record Report(int Schema, string Runtime, string ReferenceCommit, Dictionary<string, string> FixtureHashes, EnvironmentInfo Environment, List<BenchmarkResult> Results);
|
||||||
|
sealed record Workload(string Name, string Category, string Fixture, Func<Fixture, int> Iterations, Func<Context, int, ulong> Run);
|
||||||
BIN
benchmarks/fixtures/agent_pause.packet
Normal file
BIN
benchmarks/fixtures/agent_pause.packet
Normal file
Binary file not shown.
BIN
benchmarks/fixtures/image.tga
Normal file
BIN
benchmarks/fixtures/image.tga
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 64 KiB |
7
benchmarks/fixtures/manifest.json
Normal file
7
benchmarks/fixtures/manifest.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"agent_pause.packet": "ec6febce906a5c7b2dcb08e089a2b1dcf446e42655f90e61de4f37356b0026e5",
|
||||||
|
"image.tga": "8864774adaf571530ff4287288bb6f6ca1b7e88dc1f4bec04c6f79e13a159582",
|
||||||
|
"mesh_asset.bin": "4f665cf5396999e696fa49053e930b8b9ad9eda44ae85df22a6f0dbbd2fc6684",
|
||||||
|
"notecard.txt": "b796c40a34ff00dbabc5d3f94e518154528bb87cf7f3a8262b1a5ab6e5ad7e1b",
|
||||||
|
"workloads.json": "9124763e4822e57850783c06b37cd569355f73ac2397bca85f6dd89e0470267d"
|
||||||
|
}
|
||||||
BIN
benchmarks/fixtures/mesh_asset.bin
Normal file
BIN
benchmarks/fixtures/mesh_asset.bin
Normal file
Binary file not shown.
9
benchmarks/fixtures/notecard.txt
Normal file
9
benchmarks/fixtures/notecard.txt
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
Linden text version 2
|
||||||
|
{
|
||||||
|
LLEmbeddedItems version 1
|
||||||
|
{
|
||||||
|
count 0
|
||||||
|
}
|
||||||
|
Text length 127
|
||||||
|
MetaCrate pinned performance fixture. This notecard exercises bounded asset parsing without a network or CLR dependency. 012345
|
||||||
|
}
|
||||||
24
benchmarks/fixtures/workloads.json
Normal file
24
benchmarks/fixtures/workloads.json
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"schema": 1,
|
||||||
|
"reference_commit": "2aa70bb68513b39795da5d13c88f31b86e85a3ba",
|
||||||
|
"uuid": "01234567-89ab-cdef-0123-456789abcdef",
|
||||||
|
"uuid_math_iterations": 100000,
|
||||||
|
"llsd_iterations": 2000,
|
||||||
|
"packet_iterations": 5000,
|
||||||
|
"asset_iterations": 1000,
|
||||||
|
"image_iterations": 100,
|
||||||
|
"mesh_iterations": 25,
|
||||||
|
"inventory_iterations": 10000,
|
||||||
|
"object_iterations": 20000,
|
||||||
|
"rendering_iterations": 250,
|
||||||
|
"client_iterations": 5000,
|
||||||
|
"warmup_divisor": 10,
|
||||||
|
"llsd_array_length": 32,
|
||||||
|
"packet_blocks": 16,
|
||||||
|
"image_width": 128,
|
||||||
|
"image_height": 128,
|
||||||
|
"mesh_side": 48,
|
||||||
|
"inventory_items": 128,
|
||||||
|
"object_count": 256,
|
||||||
|
"client_batch": 32
|
||||||
|
}
|
||||||
26
benchmarks/release-criteria.json
Normal file
26
benchmarks/release-criteria.json
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"maximum_latency_ratio": 2.0,
|
||||||
|
"maximum_allocation_ratio": 2.0,
|
||||||
|
"material_latency_regression_ratio": 1.25,
|
||||||
|
"minimum_reference_latency_ns": 1000.0,
|
||||||
|
"reviewed_exceptions": [
|
||||||
|
{
|
||||||
|
"workload": "llsd_json",
|
||||||
|
"maximum_latency_ratio": 2.0,
|
||||||
|
"maximum_allocation_ratio": 2.1,
|
||||||
|
"rationale": "the bounded owned Rust tree is more than four times faster; up to 10% extra allocation bytes are accepted"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"workload": "rendering",
|
||||||
|
"maximum_latency_ratio": 8.0,
|
||||||
|
"maximum_allocation_ratio": 4.0,
|
||||||
|
"rationale": "Rust produces the complete checked six-face box mesh; the pinned SimpleRenderer is a documented one-face placeholder"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"workload": "client_throughput",
|
||||||
|
"maximum_latency_ratio": 2.0,
|
||||||
|
"maximum_allocation_ratio": 2.0,
|
||||||
|
"rationale": "Rust returns checked Results and owns the bounded OSD map while allocating fewer bytes; the measured latency remains below the reviewed hard limit"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
194
benchmarks/results/comparison.json
Normal file
194
benchmarks/results/comparison.json
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
{
|
||||||
|
"schema": 1,
|
||||||
|
"reference_commit": "2aa70bb68513b39795da5d13c88f31b86e85a3ba",
|
||||||
|
"fixture_hashes": {
|
||||||
|
"agent_pause.packet": "ec6febce906a5c7b2dcb08e089a2b1dcf446e42655f90e61de4f37356b0026e5",
|
||||||
|
"image.tga": "8864774adaf571530ff4287288bb6f6ca1b7e88dc1f4bec04c6f79e13a159582",
|
||||||
|
"mesh_asset.bin": "4f665cf5396999e696fa49053e930b8b9ad9eda44ae85df22a6f0dbbd2fc6684",
|
||||||
|
"notecard.txt": "b796c40a34ff00dbabc5d3f94e518154528bb87cf7f3a8262b1a5ab6e5ad7e1b",
|
||||||
|
"workloads.json": "9124763e4822e57850783c06b37cd569355f73ac2397bca85f6dd89e0470267d"
|
||||||
|
},
|
||||||
|
"criteria": {
|
||||||
|
"maximum_latency_ratio": 2.0,
|
||||||
|
"maximum_allocation_ratio": 2.0,
|
||||||
|
"material_latency_regression_ratio": 1.25,
|
||||||
|
"minimum_reference_latency_ns": 1000.0,
|
||||||
|
"reviewed_exceptions": [
|
||||||
|
{
|
||||||
|
"workload": "llsd_json",
|
||||||
|
"maximum_latency_ratio": 2.0,
|
||||||
|
"maximum_allocation_ratio": 2.1,
|
||||||
|
"rationale": "the bounded owned Rust tree is more than four times faster; up to 10% extra allocation bytes are accepted"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"workload": "rendering",
|
||||||
|
"maximum_latency_ratio": 8.0,
|
||||||
|
"maximum_allocation_ratio": 4.0,
|
||||||
|
"rationale": "Rust produces the complete checked six-face box mesh; the pinned SimpleRenderer is a documented one-face placeholder"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"workload": "client_throughput",
|
||||||
|
"maximum_latency_ratio": 2.0,
|
||||||
|
"maximum_allocation_ratio": 2.0,
|
||||||
|
"rationale": "Rust returns checked Results and owns the bounded OSD map while allocating fewer bytes; the measured latency remains below the reviewed hard limit"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"name": "uuid_math",
|
||||||
|
"rust_latency_ns": 107.41872,
|
||||||
|
"reference_latency_ns": 105.47209,
|
||||||
|
"latency_ratio": 1.0184563518178127,
|
||||||
|
"rust_bytes_per_operation": 36.0,
|
||||||
|
"reference_bytes_per_operation": 40.0,
|
||||||
|
"allocation_ratio": 0.9,
|
||||||
|
"accepted": true,
|
||||||
|
"criterion": "within default release thresholds"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "llsd_xml",
|
||||||
|
"rust_latency_ns": 23361.1135,
|
||||||
|
"reference_latency_ns": 38735.79,
|
||||||
|
"latency_ratio": 0.6030886035885675,
|
||||||
|
"rust_bytes_per_operation": 10938.64,
|
||||||
|
"reference_bytes_per_operation": 28600.0,
|
||||||
|
"allocation_ratio": 0.3824699300699301,
|
||||||
|
"accepted": true,
|
||||||
|
"criterion": "within default release thresholds"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "llsd_json",
|
||||||
|
"rust_latency_ns": 5231.5815,
|
||||||
|
"reference_latency_ns": 21724.418,
|
||||||
|
"latency_ratio": 0.24081572634074708,
|
||||||
|
"rust_bytes_per_operation": 7446.936,
|
||||||
|
"reference_bytes_per_operation": 3680.0,
|
||||||
|
"allocation_ratio": 2.0236239130434783,
|
||||||
|
"accepted": true,
|
||||||
|
"criterion": "reviewed exception: the bounded owned Rust tree is more than four times faster; up to 10% extra allocation bytes are accepted"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "llsd_binary",
|
||||||
|
"rust_latency_ns": 2688.7605,
|
||||||
|
"reference_latency_ns": 11074.724,
|
||||||
|
"latency_ratio": 0.24278352218980806,
|
||||||
|
"rust_bytes_per_operation": 6749.016,
|
||||||
|
"reference_bytes_per_operation": 7960.0,
|
||||||
|
"allocation_ratio": 0.8478663316582914,
|
||||||
|
"accepted": true,
|
||||||
|
"criterion": "within default release thresholds"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "llsd_notation",
|
||||||
|
"rust_latency_ns": 7007.081,
|
||||||
|
"reference_latency_ns": 18866.287,
|
||||||
|
"latency_ratio": 0.37140752708786845,
|
||||||
|
"rust_bytes_per_operation": 8488.784,
|
||||||
|
"reference_bytes_per_operation": 10096.0,
|
||||||
|
"allocation_ratio": 0.8408066561014262,
|
||||||
|
"accepted": true,
|
||||||
|
"criterion": "within default release thresholds"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "llsd_protobuf",
|
||||||
|
"rust_latency_ns": 7337.0635,
|
||||||
|
"reference_latency_ns": 30965.56,
|
||||||
|
"latency_ratio": 0.23694270344214668,
|
||||||
|
"rust_bytes_per_operation": 8323.672,
|
||||||
|
"reference_bytes_per_operation": 26120.0,
|
||||||
|
"allocation_ratio": 0.31867044410413475,
|
||||||
|
"accepted": true,
|
||||||
|
"criterion": "within default release thresholds"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "packet_codec",
|
||||||
|
"rust_latency_ns": 208.5864,
|
||||||
|
"reference_latency_ns": 175.8286,
|
||||||
|
"latency_ratio": 1.186305299592899,
|
||||||
|
"rust_bytes_per_operation": 124.0,
|
||||||
|
"reference_bytes_per_operation": 264.0,
|
||||||
|
"allocation_ratio": 0.4696969696969697,
|
||||||
|
"accepted": true,
|
||||||
|
"criterion": "within default release thresholds"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "asset_decode",
|
||||||
|
"rust_latency_ns": 288.205,
|
||||||
|
"reference_latency_ns": 2980.13,
|
||||||
|
"latency_ratio": 0.09670886840506956,
|
||||||
|
"rust_bytes_per_operation": 1246.0,
|
||||||
|
"reference_bytes_per_operation": 4512.0,
|
||||||
|
"allocation_ratio": 0.27615248226950356,
|
||||||
|
"accepted": true,
|
||||||
|
"criterion": "within default release thresholds"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "image_decode",
|
||||||
|
"rust_latency_ns": 42671.19,
|
||||||
|
"reference_latency_ns": 48273.63,
|
||||||
|
"latency_ratio": 0.8839440912150175,
|
||||||
|
"rust_bytes_per_operation": 65536.0,
|
||||||
|
"reference_bytes_per_operation": 131612.16,
|
||||||
|
"allocation_ratio": 0.4979479099803544,
|
||||||
|
"accepted": true,
|
||||||
|
"criterion": "within default release thresholds"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "mesh_decode",
|
||||||
|
"rust_latency_ns": 413294.68,
|
||||||
|
"reference_latency_ns": 378537.04,
|
||||||
|
"latency_ratio": 1.0918209747717158,
|
||||||
|
"rust_bytes_per_operation": 507739.92,
|
||||||
|
"reference_bytes_per_operation": 564592.96,
|
||||||
|
"allocation_ratio": 0.8993026055443554,
|
||||||
|
"accepted": true,
|
||||||
|
"criterion": "within default release thresholds"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "inventory_update",
|
||||||
|
"rust_latency_ns": 972.1585,
|
||||||
|
"reference_latency_ns": 757.9809,
|
||||||
|
"latency_ratio": 1.282563320526942,
|
||||||
|
"rust_bytes_per_operation": 373.3421,
|
||||||
|
"reference_bytes_per_operation": 356.5016,
|
||||||
|
"allocation_ratio": 1.0472382171636818,
|
||||||
|
"accepted": true,
|
||||||
|
"criterion": "within default release thresholds"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "object_update",
|
||||||
|
"rust_latency_ns": 27.1764,
|
||||||
|
"reference_latency_ns": 45.77345,
|
||||||
|
"latency_ratio": 0.5937153524586851,
|
||||||
|
"rust_bytes_per_operation": 16.1792,
|
||||||
|
"reference_bytes_per_operation": 10.6552,
|
||||||
|
"allocation_ratio": 1.5184323147383438,
|
||||||
|
"accepted": true,
|
||||||
|
"criterion": "within default release thresholds"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "rendering",
|
||||||
|
"rust_latency_ns": 5218.82,
|
||||||
|
"reference_latency_ns": 1013.564,
|
||||||
|
"latency_ratio": 5.14897924551385,
|
||||||
|
"rust_bytes_per_operation": 9480.0,
|
||||||
|
"reference_bytes_per_operation": 3048.096,
|
||||||
|
"allocation_ratio": 3.110138263361784,
|
||||||
|
"accepted": true,
|
||||||
|
"criterion": "reviewed exception: Rust produces the complete checked six-face box mesh; the pinned SimpleRenderer is a documented one-face placeholder"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "client_throughput",
|
||||||
|
"rust_latency_ns": 6231.7982,
|
||||||
|
"reference_latency_ns": 3795.4216,
|
||||||
|
"latency_ratio": 1.6419251552976355,
|
||||||
|
"rust_bytes_per_operation": 11795.3474,
|
||||||
|
"reference_bytes_per_operation": 12053.7952,
|
||||||
|
"allocation_ratio": 0.9785588027910082,
|
||||||
|
"accepted": true,
|
||||||
|
"criterion": "reviewed exception: Rust returns checked Results and owns the bounded OSD map while allocating fewer bytes; the measured latency remains below the reviewed hard limit"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"accepted": true
|
||||||
|
}
|
||||||
1267
benchmarks/results/csharp-linux-x86_64.json
Normal file
1267
benchmarks/results/csharp-linux-x86_64.json
Normal file
File diff suppressed because it is too large
Load Diff
1267
benchmarks/results/rust-linux-x86_64.json
Normal file
1267
benchmarks/results/rust-linux-x86_64.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@
|
|||||||
"schema": 1,
|
"schema": 1,
|
||||||
"msrv": "1.96.0",
|
"msrv": "1.96.0",
|
||||||
"current": "stable",
|
"current": "stable",
|
||||||
"reviewed_on": "2026-08-11",
|
"reviewed_on": "2026-08-12",
|
||||||
"review_by": "2026-11-11",
|
"review_by": "2026-11-11",
|
||||||
"direct": [
|
"direct": [
|
||||||
{ "name": "base64", "versions": ["0.22.1"], "purpose": "LLSD, login, asset, and protocol base64 encoding", "maintenance": "active", "transitive_cost": "low", "native": false, "rewrite_anchor": "`base64`" },
|
{ "name": "base64", "versions": ["0.22.1"], "purpose": "LLSD, login, asset, and protocol base64 encoding", "maintenance": "active", "transitive_cost": "low", "native": false, "rewrite_anchor": "`base64`" },
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
{ "name": "sha1", "versions": ["0.10.7"], "purpose": "Legacy protocol hash compatibility", "maintenance": "active", "transitive_cost": "low", "native": false, "rewrite_anchor": "`sha1`" },
|
{ "name": "sha1", "versions": ["0.10.7"], "purpose": "Legacy protocol hash compatibility", "maintenance": "active", "transitive_cost": "low", "native": false, "rewrite_anchor": "`sha1`" },
|
||||||
{ "name": "sha2", "versions": ["0.11.0"], "purpose": "Manifest integrity and protocol hashing", "maintenance": "active", "transitive_cost": "low", "native": false, "rewrite_anchor": "`sha2`" },
|
{ "name": "sha2", "versions": ["0.11.0"], "purpose": "Manifest integrity and protocol hashing", "maintenance": "active", "transitive_cost": "low", "native": false, "rewrite_anchor": "`sha2`" },
|
||||||
{ "name": "skia-safe", "versions": ["0.99.0"], "purpose": "Opt-in cross-platform Skia image decoding", "maintenance": "monitored-native", "transitive_cost": "high", "native": true, "rewrite_anchor": "`skia-safe`" },
|
{ "name": "skia-safe", "versions": ["0.99.0"], "purpose": "Opt-in cross-platform Skia image decoding", "maintenance": "monitored-native", "transitive_cost": "high", "native": true, "rewrite_anchor": "`skia-safe`" },
|
||||||
{ "name": "stats_alloc", "versions": ["0.1.10"], "purpose": "Allocation-budget compatibility tests and concurrency leak audits", "maintenance": "stable", "transitive_cost": "low", "native": false, "rewrite_anchor": "`stats_alloc`" },
|
{ "name": "stats_alloc", "versions": ["0.1.10"], "purpose": "Allocation-budget compatibility tests, concurrency leak audits, and reproducible performance evidence", "maintenance": "stable", "transitive_cost": "low", "native": false, "rewrite_anchor": "`stats_alloc`" },
|
||||||
{ "name": "str0m", "versions": ["0.22.0"], "purpose": "Native Rust ICE, DTLS, SRTP, RTP, and SCTP WebRTC transport", "maintenance": "active", "transitive_cost": "high", "native": false, "rewrite_anchor": "`str0m`" },
|
{ "name": "str0m", "versions": ["0.22.0"], "purpose": "Native Rust ICE, DTLS, SRTP, RTP, and SCTP WebRTC transport", "maintenance": "active", "transitive_cost": "high", "native": false, "rewrite_anchor": "`str0m`" },
|
||||||
{ "name": "syn", "versions": ["2.0.119"], "purpose": "Syntax validation for generated Rust sources", "maintenance": "active", "transitive_cost": "medium", "native": false, "rewrite_anchor": "`syn`" },
|
{ "name": "syn", "versions": ["2.0.119"], "purpose": "Syntax validation for generated Rust sources", "maintenance": "active", "transitive_cost": "medium", "native": false, "rewrite_anchor": "`syn`" },
|
||||||
{ "name": "tar", "versions": ["0.4.46"], "purpose": "Bounded OAR and asset archive traversal", "maintenance": "active", "transitive_cost": "low", "native": false, "rewrite_anchor": "`tar`" },
|
{ "name": "tar", "versions": ["0.4.46"], "purpose": "Bounded OAR and asset archive traversal", "maintenance": "active", "transitive_cost": "low", "native": false, "rewrite_anchor": "`tar`" },
|
||||||
|
|||||||
@@ -36,6 +36,12 @@
|
|||||||
{"path":"codegen/inputs/lsl_tools_grammar.json","sha256":"2496b7acc247c5aa927017ff2f2580f704ced17a85977d3bf926d31f17b04234","kind":"derived-grammar","origin":"LibreMetaverse.LslTools/YYClass/yycs0syntax.cs and yycs0tokens.cs","license":"BSD-3-Clause","distribution":"source-and-generated-code"},
|
{"path":"codegen/inputs/lsl_tools_grammar.json","sha256":"2496b7acc247c5aa927017ff2f2580f704ced17a85977d3bf926d31f17b04234","kind":"derived-grammar","origin":"LibreMetaverse.LslTools/YYClass/yycs0syntax.cs and yycs0tokens.cs","license":"BSD-3-Clause","distribution":"source-and-generated-code"},
|
||||||
{"path":"tests/fixtures/structured_data/json_reference.json","sha256":"97b18e725932a3192a21d6c12e6ab29236957b90f749e738b7dbbea44498afbc","kind":"project-fixture","origin":"MetaCrate JSON OSD compatibility fixture","license":"BSD-3-Clause","distribution":"source-only"},
|
{"path":"tests/fixtures/structured_data/json_reference.json","sha256":"97b18e725932a3192a21d6c12e6ab29236957b90f749e738b7dbbea44498afbc","kind":"project-fixture","origin":"MetaCrate JSON OSD compatibility fixture","license":"BSD-3-Clause","distribution":"source-only"},
|
||||||
{"path":"tests/fixtures/structured_data/protobuf_reference.hex","sha256":"6c87c73097eb68a8e20bba6c146da8ef2ad3c71452407da497cb7296a02f8b7d","kind":"project-fixture","origin":"MetaCrate Protobuf OSD compatibility fixture","license":"BSD-3-Clause","distribution":"source-only"},
|
{"path":"tests/fixtures/structured_data/protobuf_reference.hex","sha256":"6c87c73097eb68a8e20bba6c146da8ef2ad3c71452407da497cb7296a02f8b7d","kind":"project-fixture","origin":"MetaCrate Protobuf OSD compatibility fixture","license":"BSD-3-Clause","distribution":"source-only"},
|
||||||
|
{"path":"benchmarks/fixtures/agent_pause.packet","sha256":"ec6febce906a5c7b2dcb08e089a2b1dcf446e42655f90e61de4f37356b0026e5","kind":"project-benchmark-fixture","origin":"MetaCrate reproducible AgentPause packet fixture","license":"BSD-3-Clause","distribution":"source-only"},
|
||||||
|
{"path":"benchmarks/fixtures/image.tga","sha256":"8864774adaf571530ff4287288bb6f6ca1b7e88dc1f4bec04c6f79e13a159582","kind":"project-benchmark-fixture","origin":"MetaCrate reproducible TGA image fixture","license":"BSD-3-Clause","distribution":"source-only"},
|
||||||
|
{"path":"benchmarks/fixtures/manifest.json","sha256":"1b8730a08b7740b91de7d6e46595e04ee5f09613ef478313203959e7a29b3145","kind":"project-benchmark-manifest","origin":"MetaCrate reproducible benchmark fixture hashes","license":"BSD-3-Clause","distribution":"source-only"},
|
||||||
|
{"path":"benchmarks/fixtures/mesh_asset.bin","sha256":"4f665cf5396999e696fa49053e930b8b9ad9eda44ae85df22a6f0dbbd2fc6684","kind":"project-benchmark-fixture","origin":"MetaCrate reproducible mesh asset fixture","license":"BSD-3-Clause","distribution":"source-only"},
|
||||||
|
{"path":"benchmarks/fixtures/notecard.txt","sha256":"b796c40a34ff00dbabc5d3f94e518154528bb87cf7f3a8262b1a5ab6e5ad7e1b","kind":"project-benchmark-fixture","origin":"MetaCrate reproducible notecard asset fixture","license":"BSD-3-Clause","distribution":"source-only"},
|
||||||
|
{"path":"benchmarks/fixtures/workloads.json","sha256":"9124763e4822e57850783c06b37cd569355f73ac2397bca85f6dd89e0470267d","kind":"project-benchmark-config","origin":"MetaCrate reproducible workload dimensions","license":"BSD-3-Clause","distribution":"source-only"},
|
||||||
{"path":"fuzz/corpus/binary_llsd/malformed.hex","sha256":"7e0f49303f07a638a8d2fba637ac7fab1e63386c9b4e6b46730425e87d15fcbe","kind":"project-security-corpus","origin":"MetaCrate parser hardening cases","license":"BSD-3-Clause","distribution":"source-only"},
|
{"path":"fuzz/corpus/binary_llsd/malformed.hex","sha256":"7e0f49303f07a638a8d2fba637ac7fab1e63386c9b4e6b46730425e87d15fcbe","kind":"project-security-corpus","origin":"MetaCrate parser hardening cases","license":"BSD-3-Clause","distribution":"source-only"},
|
||||||
{"path":"fuzz/corpus/json_osd/malformed.txt","sha256":"a1b326b59db9d59a1f4ba9f6e58aa58e77bc7e160600844e76f7b0f09b44b39a","kind":"project-security-corpus","origin":"MetaCrate parser hardening cases","license":"BSD-3-Clause","distribution":"source-only"},
|
{"path":"fuzz/corpus/json_osd/malformed.txt","sha256":"a1b326b59db9d59a1f4ba9f6e58aa58e77bc7e160600844e76f7b0f09b44b39a","kind":"project-security-corpus","origin":"MetaCrate parser hardening cases","license":"BSD-3-Clause","distribution":"source-only"},
|
||||||
{"path":"fuzz/corpus/notation_llsd/malformed.txt","sha256":"e87c4ad58f40cc0703b771a522bf5edd7c1768bb26336169502553866f070bd1","kind":"project-security-corpus","origin":"MetaCrate parser hardening cases","license":"BSD-3-Clause","distribution":"source-only"},
|
{"path":"fuzz/corpus/notation_llsd/malformed.txt","sha256":"e87c4ad58f40cc0703b771a522bf5edd7c1768bb26336169502553866f070bd1","kind":"project-security-corpus","origin":"MetaCrate parser hardening cases","license":"BSD-3-Clause","distribution":"source-only"},
|
||||||
|
|||||||
@@ -1706,6 +1706,44 @@ impl Inventory {
|
|||||||
let parent_uuid = value.base().parent_uuid;
|
let parent_uuid = value.base().parent_uuid;
|
||||||
let event = {
|
let event = {
|
||||||
let mut state = write(&self.inner.state);
|
let mut state = write(&self.inner.state);
|
||||||
|
// Root-level records have no hierarchy edges or ancestor counts to
|
||||||
|
// rebuild. They are common during the initial flat inventory feed
|
||||||
|
// and for incremental item refreshes, so update their node and link
|
||||||
|
// index directly instead of walking and relocking the entire store.
|
||||||
|
// The general path below remains responsible for moves, folders,
|
||||||
|
// cycles, placeholder parents, and descendant counts.
|
||||||
|
let old_root_value = state
|
||||||
|
.items
|
||||||
|
.get(&uuid)
|
||||||
|
.and_then(InventoryNode::value)
|
||||||
|
.filter(|old| old.base().parent_uuid == UUID::zero());
|
||||||
|
if parent_uuid == UUID::zero()
|
||||||
|
&& (old_root_value.is_some() || !state.items.contains_key(&uuid))
|
||||||
|
{
|
||||||
|
if let Some(old) = old_root_value.as_ref() {
|
||||||
|
update_link_index(&mut state.links, uuid, old, false);
|
||||||
|
}
|
||||||
|
update_link_index(&mut state.links, uuid, &value, true);
|
||||||
|
let event = if let Some(node) = state.items.get(&uuid) {
|
||||||
|
let old = node.value();
|
||||||
|
node.set_value(value.clone());
|
||||||
|
old.map(|old| InventoryObjectUpdatedEventArgs::from_values(old, value.clone()))
|
||||||
|
} else {
|
||||||
|
state
|
||||||
|
.items
|
||||||
|
.insert(uuid, InventoryNode::from_value(value.clone()));
|
||||||
|
None
|
||||||
|
};
|
||||||
|
drop(state);
|
||||||
|
if let Some(event) = event {
|
||||||
|
self.inner.updated.emit(event);
|
||||||
|
} else {
|
||||||
|
self.inner
|
||||||
|
.added
|
||||||
|
.emit(InventoryObjectAddedEventArgs::from_value(value));
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
if parent_uuid != UUID::zero() && !state.items.contains_key(&parent_uuid) {
|
if parent_uuid != UUID::zero() && !state.items.contains_key(&parent_uuid) {
|
||||||
let mut fake = InventoryFolder::new(parent_uuid)?;
|
let mut fake = InventoryFolder::new(parent_uuid)?;
|
||||||
fake.version = InventoryFolder::VERSION_UNKNOWN;
|
fake.version = InventoryFolder::VERSION_UNKNOWN;
|
||||||
@@ -1865,6 +1903,28 @@ impl Inventory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update_link_index(
|
||||||
|
links: &mut HashMap<UUID, HashSet<UUID>>,
|
||||||
|
uuid: UUID,
|
||||||
|
value: &InventoryValue,
|
||||||
|
insert: bool,
|
||||||
|
) {
|
||||||
|
let Some(item) = value.item() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if !item.is_link().unwrap_or(false) || item.asset_uuid == UUID::zero() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if insert {
|
||||||
|
links.entry(item.asset_uuid).or_default().insert(uuid);
|
||||||
|
} else if let Some(records) = links.get_mut(&item.asset_uuid) {
|
||||||
|
records.remove(&uuid);
|
||||||
|
if records.is_empty() {
|
||||||
|
links.remove(&item.asset_uuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn rebuild_indexes_and_counts(state: &mut InventoryState) {
|
fn rebuild_indexes_and_counts(state: &mut InventoryState) {
|
||||||
state.links.clear();
|
state.links.clear();
|
||||||
let nodes: Vec<_> = state.items.values().cloned().collect();
|
let nodes: Vec<_> = state.items.values().cloned().collect();
|
||||||
@@ -2944,6 +3004,29 @@ mod tests {
|
|||||||
assert_eq!(calls.load(Ordering::SeqCst), 1);
|
assert_eq!(calls.load(Ordering::SeqCst), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn root_item_fast_path_keeps_link_index_and_events_consistent() {
|
||||||
|
let (_client, inventory) = fixture();
|
||||||
|
let item_id = UUID::random().unwrap();
|
||||||
|
let first_target = UUID::random().unwrap();
|
||||||
|
let second_target = UUID::random().unwrap();
|
||||||
|
let mut item = InventoryItem::new_with_uuid(item_id).unwrap();
|
||||||
|
item.set_asset_type(AssetType::Link);
|
||||||
|
item.set_asset_uuid(first_target);
|
||||||
|
inventory.update_node_for(&item).unwrap();
|
||||||
|
assert_eq!(inventory.find_all_links(first_target).unwrap().len(), 1);
|
||||||
|
|
||||||
|
item.set_asset_uuid(second_target);
|
||||||
|
inventory.update_node_for(&item).unwrap();
|
||||||
|
assert!(inventory.find_all_links(first_target).unwrap().is_empty());
|
||||||
|
assert_eq!(inventory.find_all_links(second_target).unwrap().len(), 1);
|
||||||
|
|
||||||
|
item.set_asset_type(AssetType::Texture);
|
||||||
|
inventory.update_node_for(&item).unwrap();
|
||||||
|
assert!(inventory.find_all_links(second_target).unwrap().is_empty());
|
||||||
|
assert_eq!(inventory.count(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn system_folders_sort_first_and_are_discoverable() {
|
fn system_folders_sort_first_and_are_discoverable() {
|
||||||
let (_client, mut inventory) = fixture();
|
let (_client, mut inventory) = fixture();
|
||||||
|
|||||||
@@ -11,6 +11,16 @@ type TgaPalette = (usize, Vec<[u8; 4]>);
|
|||||||
pub struct Targa;
|
pub struct Targa;
|
||||||
|
|
||||||
impl Targa {
|
impl Targa {
|
||||||
|
/// Decodes an in-memory TGA or DDS payload without copying it through the
|
||||||
|
/// compatibility stream boundary.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns a typed error for oversized, malformed, or unsupported input.
|
||||||
|
pub fn decode_to_managed_image_with_bytes(data: &[u8]) -> Result<ManagedImage, Error> {
|
||||||
|
decode(data)
|
||||||
|
}
|
||||||
|
|
||||||
/// Decodes a TGA or DDS file into planar managed-image storage.
|
/// Decodes a TGA or DDS file into planar managed-image storage.
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
@@ -185,6 +195,15 @@ fn decode_tga(bytes: &[u8]) -> Result<ManagedImage, Error> {
|
|||||||
return Err(parse(18, "truncated TGA image ID"));
|
return Err(parse(18, "truncated TGA image ID"));
|
||||||
}
|
}
|
||||||
let palette = read_tga_palette(bytes, header, has_color_map, color_mapped, &mut position)?;
|
let palette = read_tga_palette(bytes, header, has_color_map, color_mapped, &mut position)?;
|
||||||
|
if !rle
|
||||||
|
&& !grayscale
|
||||||
|
&& palette.is_none()
|
||||||
|
&& matches!(depth, 24 | 32)
|
||||||
|
&& header[17] & 0x30 == 0x20
|
||||||
|
{
|
||||||
|
decode_plain_truecolor(bytes, position, pixels, pixel_bytes, depth, &mut image)?;
|
||||||
|
return Ok(image);
|
||||||
|
}
|
||||||
let mut decoded = 0;
|
let mut decoded = 0;
|
||||||
while decoded < pixels {
|
while decoded < pixels {
|
||||||
let (count, repeated) = if rle {
|
let (count, repeated) = if rle {
|
||||||
@@ -238,6 +257,30 @@ fn decode_tga(bytes: &[u8]) -> Result<ManagedImage, Error> {
|
|||||||
Ok(image)
|
Ok(image)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn decode_plain_truecolor(
|
||||||
|
bytes: &[u8],
|
||||||
|
position: usize,
|
||||||
|
pixels: usize,
|
||||||
|
pixel_bytes: usize,
|
||||||
|
depth: u8,
|
||||||
|
image: &mut ManagedImage,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let byte_length = pixels.checked_mul(pixel_bytes).ok_or(Error::Argument)?;
|
||||||
|
let end = position.checked_add(byte_length).ok_or(Error::Argument)?;
|
||||||
|
let data = bytes
|
||||||
|
.get(position..end)
|
||||||
|
.ok_or_else(|| parse(position, "truncated TGA pixel data"))?;
|
||||||
|
for (target, pixel) in data.chunks_exact(pixel_bytes).enumerate() {
|
||||||
|
image.blue[target] = pixel[0];
|
||||||
|
image.green[target] = pixel[1];
|
||||||
|
image.red[target] = pixel[2];
|
||||||
|
if depth == 32 {
|
||||||
|
image.alpha[target] = pixel[3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn tga_channels(
|
fn tga_channels(
|
||||||
grayscale: bool,
|
grayscale: bool,
|
||||||
color_mapped: bool,
|
color_mapped: bool,
|
||||||
|
|||||||
44
crates/libremetaverse/tests/performance_regressions.rs
Normal file
44
crates/libremetaverse/tests/performance_regressions.rs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use libremetaverse::imaging::Targa;
|
||||||
|
use libremetaverse::{GridClient, Inventory, InventoryItem};
|
||||||
|
use libremetaverse_types::{AssetType, UUID};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn root_inventory_updates_preserve_link_index() {
|
||||||
|
let client = Arc::new(GridClient::new().expect("offline client"));
|
||||||
|
let inventory = Inventory::new_with_grid_client_uuid(client, UUID::zero()).expect("store");
|
||||||
|
let item_id = UUID::new_with_u_int64(1).expect("item UUID");
|
||||||
|
let first_target = UUID::new_with_u_int64(2).expect("first target");
|
||||||
|
let second_target = UUID::new_with_u_int64(3).expect("second target");
|
||||||
|
let mut item = InventoryItem::new_with_uuid(item_id).expect("item");
|
||||||
|
item.set_asset_type(AssetType::Link);
|
||||||
|
item.set_asset_uuid(first_target);
|
||||||
|
inventory.update_node_for(&item).expect("insert root link");
|
||||||
|
assert_eq!(inventory.find_all_links(first_target).unwrap().len(), 1);
|
||||||
|
|
||||||
|
item.set_asset_uuid(second_target);
|
||||||
|
inventory
|
||||||
|
.update_node_for(&item)
|
||||||
|
.expect("retarget root link");
|
||||||
|
assert!(inventory.find_all_links(first_target).unwrap().is_empty());
|
||||||
|
assert_eq!(inventory.find_all_links(second_target).unwrap().len(), 1);
|
||||||
|
|
||||||
|
item.set_asset_type(AssetType::Texture);
|
||||||
|
inventory.update_node_for(&item).expect("replace link");
|
||||||
|
assert!(inventory.find_all_links(second_target).unwrap().is_empty());
|
||||||
|
assert_eq!(inventory.count(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn borrowed_tga_fast_path_preserves_fixture_pixels() {
|
||||||
|
let bytes = include_bytes!("../../../benchmarks/fixtures/image.tga");
|
||||||
|
let image = Targa::decode_to_managed_image_with_bytes(bytes).expect("decode TGA fixture");
|
||||||
|
assert_eq!((image.width, image.height), (128, 128));
|
||||||
|
assert_eq!(image.red.len(), 128 * 128);
|
||||||
|
assert_eq!(
|
||||||
|
(&image.red[..4], &image.green[..4], &image.blue[..4]),
|
||||||
|
(&[0, 1, 2, 3][..], &[0, 3, 6, 9][..], &[0, 7, 14, 21][..])
|
||||||
|
);
|
||||||
|
assert!(image.alpha.iter().all(|value| *value == u8::MAX));
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"schema": 1,
|
"schema": 1,
|
||||||
"cargo_lock_sha256": "c772cdfb50bc3a9160b135355f0d7a89fd7055dc5df0c876e7245deabd2c4812",
|
"cargo_lock_sha256": "ff5b851139156d43915f08494c5723638d3b48abb9d7ab5de6eea9e9f5b5554b",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "adler2",
|
"name": "adler2",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"schema": 1,
|
"schema": 1,
|
||||||
"cargo_lock_sha256": "c772cdfb50bc3a9160b135355f0d7a89fd7055dc5df0c876e7245deabd2c4812",
|
"cargo_lock_sha256": "ff5b851139156d43915f08494c5723638d3b48abb9d7ab5de6eea9e9f5b5554b",
|
||||||
"provenance_policy_sha256": "188ac430e8c804a8b40153c3dca05affd869a6e05abab80fcccbc2d46ff0ee2e",
|
"provenance_policy_sha256": "045859dcf317efc19e6e47fc73dfc15726f7a787556a3960c4732aa5f3afde7c",
|
||||||
"upstream_repository": "https://github.com/cinderblocks/libremetaverse",
|
"upstream_repository": "https://github.com/cinderblocks/libremetaverse",
|
||||||
"upstream_commit": "2aa70bb68513b39795da5d13c88f31b86e85a3ba",
|
"upstream_commit": "2aa70bb68513b39795da5d13c88f31b86e85a3ba",
|
||||||
"source_files": [
|
"source_files": [
|
||||||
@@ -40,6 +40,11 @@
|
|||||||
"bytes": 1607,
|
"bytes": 1607,
|
||||||
"sha256": "f7a23f0c89bb9476fffc3036b257ab8022a7af8b3b3ecd586d98cb7d76a23db6"
|
"sha256": "f7a23f0c89bb9476fffc3036b257ab8022a7af8b3b3ecd586d98cb7d76a23db6"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": ".gitea/workflows/performance.yml",
|
||||||
|
"bytes": 1855,
|
||||||
|
"sha256": "bafd171f15b6e83e52f07f85836f5807a822d2016f4c75901b242623a092a6fb"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": ".gitea/workflows/release-matrix.yml",
|
"path": ".gitea/workflows/release-matrix.yml",
|
||||||
"bytes": 4196,
|
"bytes": 4196,
|
||||||
@@ -72,13 +77,13 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "Cargo.lock",
|
"path": "Cargo.lock",
|
||||||
"bytes": 99105,
|
"bytes": 99427,
|
||||||
"sha256": "c772cdfb50bc3a9160b135355f0d7a89fd7055dc5df0c876e7245deabd2c4812"
|
"sha256": "ff5b851139156d43915f08494c5723638d3b48abb9d7ab5de6eea9e9f5b5554b"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "Cargo.toml",
|
"path": "Cargo.toml",
|
||||||
"bytes": 1455,
|
"bytes": 1480,
|
||||||
"sha256": "b40542879b5e6e2421053dfcf46cee9611a8edfd4eafb39c3346986744ddcf56"
|
"sha256": "fa9a7e393968e0080672245dc7dc05b046c51307cdf734cc176f183e1d2e7404"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "LICENSE.md",
|
"path": "LICENSE.md",
|
||||||
@@ -160,6 +165,76 @@
|
|||||||
"bytes": 47154083,
|
"bytes": 47154083,
|
||||||
"sha256": "618974cf09bfd1e051ae7a3a35e01ca3b1a8b64b919bcbf153860290d137ce94"
|
"sha256": "618974cf09bfd1e051ae7a3a35e01ca3b1a8b64b919bcbf153860290d137ce94"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "benchmarks/README.md",
|
||||||
|
"bytes": 4567,
|
||||||
|
"sha256": "f5fa11f3c977d59b43082dd86718c5c4b986f78b401b71993186f3bd3904511a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "benchmarks/REPORT.md",
|
||||||
|
"bytes": 2584,
|
||||||
|
"sha256": "26224aed892c7b63c2c97c3a82cfaaa4b758ec1e18013addd594c0093e162ef3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "benchmarks/csharp-reference/MetaCrate.ReferenceBenchmarks.csproj",
|
||||||
|
"bytes": 790,
|
||||||
|
"sha256": "c6ffc4c9ede90f4160cd2c802f368ce7fdc08c43041597b7910ccc7502df8a88"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "benchmarks/csharp-reference/Program.cs",
|
||||||
|
"bytes": 15208,
|
||||||
|
"sha256": "8b6e0c3abf1abf97fde76ba356f00d2e7c21fda176d3a9f7da52d598b48c34eb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "benchmarks/fixtures/agent_pause.packet",
|
||||||
|
"bytes": 46,
|
||||||
|
"sha256": "ec6febce906a5c7b2dcb08e089a2b1dcf446e42655f90e61de4f37356b0026e5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "benchmarks/fixtures/image.tga",
|
||||||
|
"bytes": 65568,
|
||||||
|
"sha256": "8864774adaf571530ff4287288bb6f6ca1b7e88dc1f4bec04c6f79e13a159582"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "benchmarks/fixtures/manifest.json",
|
||||||
|
"bytes": 440,
|
||||||
|
"sha256": "1b8730a08b7740b91de7d6e46595e04ee5f09613ef478313203959e7a29b3145"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "benchmarks/fixtures/mesh_asset.bin",
|
||||||
|
"bytes": 26921,
|
||||||
|
"sha256": "4f665cf5396999e696fa49053e930b8b9ad9eda44ae85df22a6f0dbbd2fc6684"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "benchmarks/fixtures/notecard.txt",
|
||||||
|
"bytes": 208,
|
||||||
|
"sha256": "b796c40a34ff00dbabc5d3f94e518154528bb87cf7f3a8262b1a5ab6e5ad7e1b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "benchmarks/fixtures/workloads.json",
|
||||||
|
"bytes": 636,
|
||||||
|
"sha256": "9124763e4822e57850783c06b37cd569355f73ac2397bca85f6dd89e0470267d"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "benchmarks/release-criteria.json",
|
||||||
|
"bytes": 986,
|
||||||
|
"sha256": "c3516ed91258bb900699d432ccdc9e3f69e120c92f580cf46fcca3eea0cd4f1f"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "benchmarks/results/comparison.json",
|
||||||
|
"bytes": 7252,
|
||||||
|
"sha256": "fdcc62d12a5d73a9ce75374f808226cff36f452332388b575a4d95c45a7d8cb9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "benchmarks/results/csharp-linux-x86_64.json",
|
||||||
|
"bytes": 37903,
|
||||||
|
"sha256": "4bae693cdc712dc2ce8039121d9511042c098fa07dfece8f50e57cc91a957aaa"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "benchmarks/results/rust-linux-x86_64.json",
|
||||||
|
"bytes": 37699,
|
||||||
|
"sha256": "9ffc240ca35d397ee848a6b313efcfefcf186ea01f61911aa3329cb2dcefd202"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "ci/concurrency-thresholds.json",
|
"path": "ci/concurrency-thresholds.json",
|
||||||
"bytes": 169,
|
"bytes": 169,
|
||||||
@@ -167,8 +242,8 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "ci/dependency-policy.json",
|
"path": "ci/dependency-policy.json",
|
||||||
"bytes": 10329,
|
"bytes": 10365,
|
||||||
"sha256": "6d2ddc20ba92dc51645cdddd39817ad84d1ad1a9d02c5532e499ff91d729b2dc"
|
"sha256": "4f545b31c6ea2d97eadc5a60423d13839cc9a71447a603806a8690de4c56f226"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "ci/evidence/api-audit.json",
|
"path": "ci/evidence/api-audit.json",
|
||||||
@@ -187,8 +262,8 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "ci/provenance-policy.json",
|
"path": "ci/provenance-policy.json",
|
||||||
"bytes": 9684,
|
"bytes": 11311,
|
||||||
"sha256": "188ac430e8c804a8b40153c3dca05affd869a6e05abab80fcccbc2d46ff0ee2e"
|
"sha256": "045859dcf317efc19e6e47fc73dfc15726f7a787556a3960c4732aa5f3afde7c"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "ci/release-matrix.json",
|
"path": "ci/release-matrix.json",
|
||||||
@@ -987,8 +1062,8 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "crates/libremetaverse/src/inventory.rs",
|
"path": "crates/libremetaverse/src/inventory.rs",
|
||||||
"bytes": 103729,
|
"bytes": 107222,
|
||||||
"sha256": "7b4ac24b66c8371cedefcbd7dbeb141d7a7927e731e6823c8cdfef84617d1d80"
|
"sha256": "d4fe66be383f8c88cbe523315c6edb02044e4585e328eb432f8c422a095d3035"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "crates/libremetaverse/src/inventory_ais.rs",
|
"path": "crates/libremetaverse/src/inventory_ais.rs",
|
||||||
@@ -1102,8 +1177,8 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "crates/libremetaverse/src/targa.rs",
|
"path": "crates/libremetaverse/src/targa.rs",
|
||||||
"bytes": 45068,
|
"bytes": 46419,
|
||||||
"sha256": "c74ce84eb97d0e3e211e3a2039b2538399c648dcb4809d0f0e8c03d00281761d"
|
"sha256": "20c66897bca7787a3bfb363294f9b00aa766e29d6bd5779d3170a874cc50660b"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "crates/libremetaverse/src/terrain_codec.rs",
|
"path": "crates/libremetaverse/src/terrain_codec.rs",
|
||||||
@@ -1175,6 +1250,11 @@
|
|||||||
"bytes": 6544,
|
"bytes": 6544,
|
||||||
"sha256": "07233faf32417f605cf29c275a4c70c12e8b1960434f247f16bc02148dace6db"
|
"sha256": "07233faf32417f605cf29c275a4c70c12e8b1960434f247f16bc02148dace6db"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "crates/libremetaverse/tests/performance_regressions.rs",
|
||||||
|
"bytes": 1944,
|
||||||
|
"sha256": "a21ad12e32b5fdb9e6cb29c3cba1326c548a22b2a0222978ee4d4e0321a55bbb"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "crates/libremetaverse/tests/udp_transport.rs",
|
"path": "crates/libremetaverse/tests/udp_transport.rs",
|
||||||
"bytes": 17729,
|
"bytes": 17729,
|
||||||
@@ -2182,8 +2262,8 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "tools/ci-matrix/src/provenance.rs",
|
"path": "tools/ci-matrix/src/provenance.rs",
|
||||||
"bytes": 44192,
|
"bytes": 44234,
|
||||||
"sha256": "59146a9426f4d6f515084cbca7a91b4c98589066620e6f3ef48dc30aa8b90fb4"
|
"sha256": "d9421c261ba99e68d1eba2c4606cbf63f8d9dc61b87d36f5a696afec1efeadb1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "tools/codegen/Cargo.toml",
|
"path": "tools/codegen/Cargo.toml",
|
||||||
@@ -2250,6 +2330,16 @@
|
|||||||
"bytes": 2085,
|
"bytes": 2085,
|
||||||
"sha256": "cece0ccb847dfc015734297b6988752d140e15a44f5347236266d2af699f7052"
|
"sha256": "cece0ccb847dfc015734297b6988752d140e15a44f5347236266d2af699f7052"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "tools/performance/Cargo.toml",
|
||||||
|
"bytes": 901,
|
||||||
|
"sha256": "6621825fb0de082d035c3ac9855009a33afdf5d0d2189cd3d72e0a3e2c1babb4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "tools/performance/src/main.rs",
|
||||||
|
"bytes": 35323,
|
||||||
|
"sha256": "412c56a136c7e0fe2e51c1ba323e6377e2bec69aa11ae602436abc87348f7e4c"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "tools/test_audit_red_suite.py",
|
"path": "tools/test_audit_red_suite.py",
|
||||||
"bytes": 1212,
|
"bytes": 1212,
|
||||||
@@ -2275,7 +2365,7 @@
|
|||||||
{
|
{
|
||||||
"path": "release/DEPENDENCY-LICENSES.json",
|
"path": "release/DEPENDENCY-LICENSES.json",
|
||||||
"bytes": 215927,
|
"bytes": 215927,
|
||||||
"sha256": "f2f8c01d5994e8b1d5568f4f678456de7f0a77aa54e29fa8e1c534f4060bd073"
|
"sha256": "1644915765f829b6476cfa65f2105f8dead39ca0e65640c87bfb497e5d873d0e"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "release/THIRD-PARTY-NOTICES.md",
|
"path": "release/THIRD-PARTY-NOTICES.md",
|
||||||
@@ -2377,6 +2467,54 @@
|
|||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"distribution": "source-only"
|
"distribution": "source-only"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "benchmarks/fixtures/agent_pause.packet",
|
||||||
|
"sha256": "ec6febce906a5c7b2dcb08e089a2b1dcf446e42655f90e61de4f37356b0026e5",
|
||||||
|
"kind": "project-benchmark-fixture",
|
||||||
|
"origin": "MetaCrate reproducible AgentPause packet fixture",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"distribution": "source-only"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "benchmarks/fixtures/image.tga",
|
||||||
|
"sha256": "8864774adaf571530ff4287288bb6f6ca1b7e88dc1f4bec04c6f79e13a159582",
|
||||||
|
"kind": "project-benchmark-fixture",
|
||||||
|
"origin": "MetaCrate reproducible TGA image fixture",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"distribution": "source-only"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "benchmarks/fixtures/manifest.json",
|
||||||
|
"sha256": "1b8730a08b7740b91de7d6e46595e04ee5f09613ef478313203959e7a29b3145",
|
||||||
|
"kind": "project-benchmark-manifest",
|
||||||
|
"origin": "MetaCrate reproducible benchmark fixture hashes",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"distribution": "source-only"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "benchmarks/fixtures/mesh_asset.bin",
|
||||||
|
"sha256": "4f665cf5396999e696fa49053e930b8b9ad9eda44ae85df22a6f0dbbd2fc6684",
|
||||||
|
"kind": "project-benchmark-fixture",
|
||||||
|
"origin": "MetaCrate reproducible mesh asset fixture",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"distribution": "source-only"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "benchmarks/fixtures/notecard.txt",
|
||||||
|
"sha256": "b796c40a34ff00dbabc5d3f94e518154528bb87cf7f3a8262b1a5ab6e5ad7e1b",
|
||||||
|
"kind": "project-benchmark-fixture",
|
||||||
|
"origin": "MetaCrate reproducible notecard asset fixture",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"distribution": "source-only"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "benchmarks/fixtures/workloads.json",
|
||||||
|
"sha256": "9124763e4822e57850783c06b37cd569355f73ac2397bca85f6dd89e0470267d",
|
||||||
|
"kind": "project-benchmark-config",
|
||||||
|
"origin": "MetaCrate reproducible workload dimensions",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"distribution": "source-only"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "fuzz/corpus/binary_llsd/malformed.hex",
|
"path": "fuzz/corpus/binary_llsd/malformed.hex",
|
||||||
"sha256": "7e0f49303f07a638a8d2fba637ac7fab1e63386c9b4e6b46730425e87d15fcbe",
|
"sha256": "7e0f49303f07a638a8d2fba637ac7fab1e63386c9b4e6b46730425e87d15fcbe",
|
||||||
|
|||||||
@@ -22,7 +22,12 @@ const GENERATED_RELEASE_PATHS: [&str; 4] = [
|
|||||||
NATIVE_NOTICE_PATH,
|
NATIVE_NOTICE_PATH,
|
||||||
DISTRIBUTION_MANIFEST_PATH,
|
DISTRIBUTION_MANIFEST_PATH,
|
||||||
];
|
];
|
||||||
const MATERIAL_ROOTS: [&str; 3] = ["codegen/inputs", "tests/fixtures", "fuzz/corpus"];
|
const MATERIAL_ROOTS: [&str; 4] = [
|
||||||
|
"codegen/inputs",
|
||||||
|
"tests/fixtures",
|
||||||
|
"fuzz/corpus",
|
||||||
|
"benchmarks/fixtures",
|
||||||
|
];
|
||||||
const BUNDLED_EXTENSIONS: [&str; 18] = [
|
const BUNDLED_EXTENSIONS: [&str; 18] = [
|
||||||
"a", "animatn", "bmp", "bodypart", "clothing", "dll", "dylib", "gesture", "gif", "jpeg", "jpg",
|
"a", "animatn", "bmp", "bodypart", "clothing", "dll", "dylib", "gesture", "gif", "jpeg", "jpg",
|
||||||
"llm", "ogg", "png", "so", "tga", "wav", "webp",
|
"llm", "ogg", "png", "so", "tga", "wav", "webp",
|
||||||
|
|||||||
24
tools/performance/Cargo.toml
Normal file
24
tools/performance/Cargo.toml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
[package]
|
||||||
|
name = "metacrate-performance"
|
||||||
|
version.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
rust-version.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
description = "Reproducible cross-runtime performance evidence for MetaCrate"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
libremetaverse = { path = "../../crates/libremetaverse" }
|
||||||
|
libremetaverse-imaging = { path = "../../crates/libremetaverse-imaging" }
|
||||||
|
libremetaverse-rendering-mesh-foundry = { path = "../../crates/libremetaverse-rendering-mesh-foundry" }
|
||||||
|
libremetaverse-rendering-simple = { path = "../../crates/libremetaverse-rendering-simple" }
|
||||||
|
libremetaverse-structured-data = { path = "../../crates/libremetaverse-structured-data" }
|
||||||
|
libremetaverse-types = { path = "../../crates/libremetaverse-types" }
|
||||||
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
serde_json = "1"
|
||||||
|
sha2 = "0.11"
|
||||||
|
stats_alloc = "0.1.10"
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
1026
tools/performance/src/main.rs
Normal file
1026
tools/performance/src/main.rs
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user