Skip to content

Conversation

@SergeiPavlov
Copy link
Collaborator

@SergeiPavlov SergeiPavlov commented Dec 18, 2025

Remove isNullable parameter. It can be calculated at compile time and compile can apply some optimizations knowing it.

Also:

  • Remove PackedFieldAccessor.Rank field as unused
  • Change type of PackedFieldAccessor.Index to byte to reduce this class size
  • Use PooedArray in VisitSelect()

@SergeiPavlov SergeiPavlov requested a review from botinko December 18, 2025 22:51
var columnIndexes = provider.ColumnIndexes;

var n = columnIndexes.Count;
using PooledArray<SqlColumn> pooledArray = new(n);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PooledArray has own drawbacks and not a silver bullet. Need to bench before apply.

Copy link
Collaborator Author

@SergeiPavlov SergeiPavlov Dec 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I consider ArrayPool<> to be always faster than temporary array allocation/collection.
This was the intention of the ArrayPool<> design.

No need to benchmark every appearance of it

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is questionable concideration, moreover It was not true when we tried it 3-4 years ago in ST.
I couldn't approve this PR without bench.

@botinko botinko self-requested a review December 19, 2025 20:09
Copy link

@botinko botinko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PooledArray is slower.

@botinko
Copy link

botinko commented Dec 19, 2025

    using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using System;
using System.Buffers;

[MemoryDiagnoser]
[SimpleJob(warmupCount: 3, iterationCount: 5)]
public class PooledArrayBenchmark
{
    [Params(1, 2, 5, 11, 27)]
    public int Size { get; set; }

    [Benchmark(Baseline = true)]
    public object StandardArray()
    {
        var arr = new object[Size];
        for (int i = 0; i < Size; i++)
            arr[i] = obj;
        return arr[0];
    }
    
    public static object obj = new object(); 

    [Benchmark]
    public object PooledArray_NoClear()
    {
        using var pooled = new PooledArray<object>(Size, clearArray: false);
        object[] arr = pooled.Array;
        for (int i = 0; i < Size; i++)
            arr[i] = obj;
        return arr[0];
    }

    [Benchmark]
    public object ManualArrayPool_NoClear()
    {
        var arr = ArrayPool<object>.Shared.Rent(Size);
        try
        {
            for (int i = 0; i < Size; i++)
                arr[i] = obj;
            return arr[0];
        }
        finally
        {
            ArrayPool<object>.Shared.Return(arr, clearArray: false);
        }
    }
}
internal readonly struct PooledArray<T>(int length, bool clearArray = false) : IDisposable
{
    public T[] Array { get; } = length > 0 ? ArrayPool<T>.Shared.Rent(length) : [];

    public void Dispose()
    {
        if (Array.Length > 0) {
            ArrayPool<T>.Shared.Return(Array, clearArray);
        }
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        var summary = BenchmarkRunner.Run<PooledArrayBenchmark>();
    }
}

BenchmarkDotNet v0.15.8, Windows 11 (10.0.26200.7462/25H2/2025Update/HudsonValley2)
AMD RYZEN AI MAX+ 395 w/ Radeon 8060S 3.00GHz, 1 CPU, 32 logical and 16 physical cores
.NET SDK 10.0.101
[Host] : .NET 10.0.1 (10.0.1, 10.0.125.57005), X64 RyuJIT x86-64-v4
Job-NTRUNJ : .NET 10.0.1 (10.0.1, 10.0.125.57005), X64 RyuJIT x86-64-v4

IterationCount=5 WarmupCount=3

Method Size Mean Error StdDev Ratio RatioSD Gen0 Allocated Alloc Ratio
StandardArray 1 2.589 ns 0.2660 ns 0.0691 ns 1.00 0.03 0.0019 32 B 1.00
PooledArray_NoClear 1 6.113 ns 0.3034 ns 0.0470 ns 2.36 0.06 - - 0.00
ManualArrayPool_NoClear 1 6.302 ns 2.6086 ns 0.4037 ns 2.44 0.15 - - 0.00
StandardArray 2 3.730 ns 0.3727 ns 0.0968 ns 1.00 0.03 0.0024 40 B 1.00
PooledArray_NoClear 2 7.746 ns 0.3783 ns 0.0982 ns 2.08 0.05 - - 0.00
ManualArrayPool_NoClear 2 7.448 ns 0.1628 ns 0.0252 ns 2.00 0.05 - - 0.00
StandardArray 5 6.945 ns 0.4988 ns 0.1295 ns 1.00 0.02 0.0038 64 B 1.00
PooledArray_NoClear 5 13.320 ns 0.2674 ns 0.0694 ns 1.92 0.03 - - 0.00
ManualArrayPool_NoClear 5 12.595 ns 0.3267 ns 0.0848 ns 1.81 0.03 - - 0.00
StandardArray 11 12.130 ns 0.7497 ns 0.1947 ns 1.00 0.02 0.0067 112 B 1.00
PooledArray_NoClear 11 24.136 ns 0.4003 ns 0.1040 ns 1.99 0.03 - - 0.00
ManualArrayPool_NoClear 11 22.921 ns 1.2040 ns 0.3127 ns 1.89 0.04 - - 0.00
StandardArray 27 27.320 ns 2.8750 ns 0.7466 ns 1.00 0.04 0.0143 240 B 1.00
PooledArray_NoClear 27 59.255 ns 6.2171 ns 1.6146 ns 2.17 0.08 - - 0.00
ManualArrayPool_NoClear 27 56.807 ns 2.8183 ns 0.4361 ns 2.08 0.05 - - 0.00

@SergeiPavlov
Copy link
Collaborator Author

SergeiPavlov commented Dec 20, 2025

PooledArray is slower.

The benchmark ignores GC time. that is why it looks as StandardArray is faster .
PooledArray has iny ovewrhead over manual ArrayPool<T>.Shared.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants