From 1024599e6687a408375fc38fbabdb1edcd7ef6fb Mon Sep 17 00:00:00 2001 From: Skuirrels Date: Fri, 10 Jul 2026 23:25:16 +0100 Subject: [PATCH] Avoid LINQ and boxed-enumerator allocations in parameter binding PreparedStatement.BindParameters ran a LINQ OfType<>().Any(...) and iterated the parameters with foreach over the non-generic collection (which boxed the List struct enumerator) on every command execution. Replace both with index-based loops over the typed indexer. This removes ~128 bytes and 2-3 allocations per command execution, which matters in tight parameterized-query loops. Execution time is unchanged, as the native prepare/execute dominates. --- .../PreparedStatement/PreparedStatement.cs | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/DuckDB.NET.Data/PreparedStatement/PreparedStatement.cs b/DuckDB.NET.Data/PreparedStatement/PreparedStatement.cs index d074baab..4f7d559d 100644 --- a/DuckDB.NET.Data/PreparedStatement/PreparedStatement.cs +++ b/DuckDB.NET.Data/PreparedStatement/PreparedStatement.cs @@ -1,4 +1,3 @@ -using System.Linq; using DuckDB.NET.Data.Connection; namespace DuckDB.NET.Data.PreparedStatement; @@ -89,10 +88,26 @@ private static void BindParameters(DuckDBPreparedStatement preparedStatement, Du throw new InvalidOperationException($"Invalid number of parameters. Expected {expectedParameters}, got {parameterCollection.Count}"); } - if (parameterCollection.OfType().Any(p => !string.IsNullOrEmpty(p.ParameterName))) + // Index-based iteration over the typed collection avoids the per-execution allocations of + // OfType<>().Any(...) and of the boxed List enumerator that `foreach (DuckDBParameter ...)` + // over the non-generic collection would produce. BindParameters runs on every execution. + var count = parameterCollection.Count; + + var hasNamedParameters = false; + for (var i = 0; i < count; i++) { - foreach (DuckDBParameter param in parameterCollection) + if (!string.IsNullOrEmpty(parameterCollection[i].ParameterName)) { + hasNamedParameters = true; + break; + } + } + + if (hasNamedParameters) + { + for (var i = 0; i < count; i++) + { + var param = parameterCollection[i]; var state = NativeMethods.PreparedStatements.DuckDBBindParameterIndex(preparedStatement, out var index, param.ParameterName); if (state.IsSuccess()) {