Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions DuckDB.NET.Benchmarks/AppenderBenchmark.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using BenchmarkDotNet.Attributes;
using DuckDB.NET.Data;

namespace DuckDB.NET.Benchmarks;

[MemoryDiagnoser]
public class AppenderBenchmark
{
private DuckDBConnection connection = null!;

[Params(1_000_000)]
public int RowCount { get; set; }

[GlobalSetup]
public void Setup()
{
connection = new DuckDBConnection("DataSource=:memory:");
connection.Open();
}

[GlobalCleanup]
public void Cleanup()
{
connection.Dispose();
}

[IterationSetup]
public void IterationSetup()
{
using var command = connection.CreateCommand();
command.CommandText = "DROP TABLE IF EXISTS bench; CREATE TABLE bench (a INTEGER, b BIGINT, c DOUBLE, d BOOLEAN);";
command.ExecuteNonQuery();
}

[Benchmark(Baseline = true)]
public void AppendRowsWithCreateRow()
{
using var appender = connection.CreateAppender("bench");

for (var i = 0; i < RowCount; i++)
{
appender.CreateRow()
.AppendValue(i)
.AppendValue((long)i)
.AppendValue((double)i)
.AppendValue(i % 2 == 0)
.EndRow();
}
}

[Benchmark]
public void AppendRowsWithAppendRow()
{
using var appender = connection.CreateAppender("bench");

for (var i = 0; i < RowCount; i++)
{
appender.AppendRow(i, static (row, value) =>
{
row.AppendValue(value)
.AppendValue((long)value)
.AppendValue((double)value)
.AppendValue(value % 2 == 0);
});
}
}
}
33 changes: 33 additions & 0 deletions DuckDB.NET.Benchmarks/Benchmarks.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net10.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Optimize>true</Optimize>
<BuildType>Full</BuildType>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>..\keyPair.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.15.4" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\DuckDB.NET.Data\Data.csproj" />
<ProjectReference Include="..\DuckDB.NET.Bindings\Bindings.csproj" />
</ItemGroup>

<!-- Copy the already-downloaded native DuckDB library into the benchmark output so it can be
loaded via runtimes/{rid}/native/. -->
<ItemGroup>
<None Include="..\DuckDB.NET.Bindings\obj\runtimes\**\*.dll;..\DuckDB.NET.Bindings\obj\runtimes\**\*.so;..\DuckDB.NET.Bindings\obj\runtimes\**\*.dylib;">
<Visible>false</Visible>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Link>runtimes\%(RecursiveDir)\%(FileName)%(Extension)</Link>
</None>
</ItemGroup>

</Project>
96 changes: 96 additions & 0 deletions DuckDB.NET.Benchmarks/MappedAppenderBenchmark.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
using BenchmarkDotNet.Attributes;
using DuckDB.NET.Data;
using DuckDB.NET.Data.Mapping;

namespace DuckDB.NET.Benchmarks;

[MemoryDiagnoser]
public class MappedAppenderBenchmark
{
private DuckDBConnection connection = null!;
private BenchRow[] rows = null!;
private IPropertyMapping<BenchRow>[] mappings = null!;

[Params(1_000_000)]
public int RowCount { get; set; }

public sealed class BenchRow
{
public int Id { get; init; }
public long Score { get; init; }
public double Value { get; init; }
public bool Active { get; init; }
}

public sealed class BenchRowMap : DuckDBAppenderMap<BenchRow>
{
public BenchRowMap()
{
Map(row => row.Id);
Map(row => row.Score);
Map(row => row.Value);
Map(row => row.Active);
}
}

[GlobalSetup]
public void Setup()
{
connection = new DuckDBConnection("DataSource=:memory:");
connection.Open();

rows = new BenchRow[RowCount];
for (var i = 0; i < RowCount; i++)
{
rows[i] = new BenchRow { Id = i, Score = i, Value = i, Active = i % 2 == 0 };
}

mappings = new BenchRowMap().PropertyMappings.ToArray();
}

[GlobalCleanup]
public void Cleanup()
{
connection.Dispose();
}

[IterationSetup]
public void IterationSetup()
{
using var command = connection.CreateCommand();
command.CommandText = "DROP TABLE IF EXISTS bench_mapped; CREATE TABLE bench_mapped (a INTEGER, b BIGINT, c DOUBLE, d BOOLEAN);";
command.ExecuteNonQuery();
}

// Mirrors the mapped appender loop before it adopted AppendRow.
[Benchmark(Baseline = true)]
public void AppendMappedRowsWithCreateRow()
{
using var appender = connection.CreateAppender("bench_mapped");

foreach (var record in rows)
{
AppendRecordWithCreateRow(appender, record);
}
}

[Benchmark]
public void AppendMappedRowsWithAppendRow()
{
using var appender = connection.CreateAppender<BenchRow, BenchRowMap>("bench_mapped");
appender.AppendRecords(rows);
}

private void AppendRecordWithCreateRow(DuckDBAppender appender, BenchRow record)
{
ArgumentNullException.ThrowIfNull(record);

var row = appender.CreateRow();
foreach (var mapping in mappings)
{
mapping.AppendToRow(row, record);
}

row.EndRow();
}
}
48 changes: 48 additions & 0 deletions DuckDB.NET.Benchmarks/NativeLibraryLoader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace DuckDB.NET.Benchmarks;

/// <summary>
/// Loads the platform-native DuckDB library for the benchmark process.
/// </summary>
internal static class NativeLibraryLoader
{
[ModuleInitializer]
public static void Init()
{
if (GetRid() is not { } rid)
{
return;
}

_ = NativeLibrary.TryLoad(Path.Join("runtimes", rid, "native", "duckdb"), Assembly.GetExecutingAssembly(), DllImportSearchPath.AssemblyDirectory, out _) ||
NativeLibrary.TryLoad(Path.Join("runtimes", rid, "native", "libduckdb"), Assembly.GetExecutingAssembly(), DllImportSearchPath.AssemblyDirectory, out _);
}

private static string? GetRid()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return Environment.Is64BitProcess ? "win-x64" : "win-x86";
}

if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
return RuntimeInformation.ProcessArchitecture switch
{
Architecture.X64 => "linux-x64",
Architecture.Arm64 => "linux-arm64",
_ => null,
};
}

if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
return "osx";
}

return null;
}
}
15 changes: 15 additions & 0 deletions DuckDB.NET.Benchmarks/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Running;
using BenchmarkDotNet.Toolchains.InProcess.Emit;
using DuckDB.NET.Benchmarks;

// The repo's Directory.Build.props renames the output assembly to DuckDB.NET.Benchmarks
// while the project file stays Benchmarks.csproj, so BenchmarkDotNet's default toolchain
// can't locate the csproj. Run in-process to avoid the separate build/spawn step.
var config = DefaultConfig.Instance
.AddJob(Job.Default.WithToolchain(InProcessEmitToolchain.Instance));

BenchmarkSwitcher
.FromTypes([typeof(AppenderBenchmark), typeof(MappedAppenderBenchmark)])
.Run(args, config);
2 changes: 2 additions & 0 deletions DuckDB.NET.Data/Data.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ Fixes:
</ItemGroup>
<ItemGroup>
<InternalsVisibleTo Include="DuckDB.NET.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100852ebcffb69a0dfb906bc0377ec8608f4a00ba9af2e29300d1ed77dcb2583f08116b1a1006d202d53a48ec0561c1816738368378f7c5d335fec3daa63ba1b5413298153f886aafc75304e7653715f2395ad370fe3b2f4bc44a36a2f6b958fd500a2f7eea9a69c6ab5819e0933db962630c56c1610c7c87ed6a3c2b36e1ca4ed2" />
<!-- The signed benchmark assembly needs internal mappings to reproduce the pre-AppendRow baseline. -->
<InternalsVisibleTo Include="DuckDB.NET.Benchmarks, PublicKey=0024000004800000940000000602000000240000525341310004000001000100852ebcffb69a0dfb906bc0377ec8608f4a00ba9af2e29300d1ed77dcb2583f08116b1a1006d202d53a48ec0561c1816738368378f7c5d335fec3daa63ba1b5413298153f886aafc75304e7653715f2395ad370fe3b2f4bc44a36a2f6b958fd500a2f7eea9a69c6ab5819e0933db962630c56c1610c7c87ed6a3c2b36e1ca4ed2" />
</ItemGroup>

<ItemGroup>
Expand Down
Loading
Loading