allisterb / jemalloc.NET

A native memory manager for .NET
MIT License
332 stars 16 forks source link

benchmarks shows many gen0,1,2 collections for a LOH allocation #3

Closed tmds closed 5 years ago

tmds commented 6 years ago

From README:

|                                                               Method | Parameter |     Mean |    Error |   StdDev |    Gen 0 |    Gen 1 |    Gen 2 |   Allocated |
|--------------------------------------------------------------------- |---------- |---------:|---------:|---------:|---------:|---------:|---------:|------------:|
|                          'Fill a managed array with a single value.' | 100000000 | 327.4 ms | 3.102 ms | 2.902 ms | 937.5000 | 937.5000 | 937.5000 | 800000192 B |
| 'Fill a SafeArray on the system unmanaged heap with a single value.' | 100000000 | 126.1 ms | 1.220 ms | 1.081 ms |        - |        - |        - |       264 B |

It's surprising to see so many gen0,1,2 collections for a LOH allocation. Any idea what is being collected?

CC @mattwarren

tmds commented 6 years ago

@allisterb I really wonder where all those allocations come from:

using System;
class Program
{
    public static readonly int MaxManagedArraySize = 2146435071;
    static void Main(string[] args)
    {
        var managedArray = new byte[MaxManagedArraySize];
        for (int i = 0; i < managedArray.Length; i++)
        {
            managedArray[i] = 0;
        }
        for (int i = 0; i <= GC.MaxGeneration; i++)
        {
            Console.WriteLine($"# gen {i} collections: {GC.CollectionCount(i)}");
        }
    }
}

outputs:

# gen 0 collections: 0
# gen 1 collections: 0
# gen 2 collections: 0
allisterb commented 6 years ago

Hi, I separated the fill benchmarks into a "fill" and "create and fill" category. "Create and fill" benchmarks test how long it takes to instantiate a managed array and fill it so I'd expect there would be lots of allocations there. There shouldn't be any allocations in the "fill" category: jembench safe --fill 10000 -i --cold-start


BenchmarkDotNet=v0.10.11, OS=Windows 10 Redstone 2 [1703, Creators Update] (10.0.15063.726)
Processor=Intel Core i7-6700HQ CPU 2.60GHz (Skylake), ProcessorCount=8
Frequency=2531251 Hz, Resolution=395.0616 ns, Timer=TSC
.NET Core SDK=2.1.2
  [Host] : .NET Core 2.0.3 (Framework 4.6.25815.02), 64bit RyuJIT

Job=JemBenchmark  Jit=RyuJit  Platform=X64  
Runtime=Core  AllowVeryLargeObjects=True  Toolchain=InProcessToolchain  
RunStrategy=ColdStart  
Method Parameter Mean Error StdDev Allocated
'Fill a managed array with a single value.' 10000 39.75 us 80.83 us 238.33 us 240 B
'Fill a SafeArray on the system unmanaged heap with a single value.' 10000 39.62 us 73.77 us 217.50 us 240 B
'Create and Fill a managed array with a single value.' 10000 22.81 us 22.00 us 64.88 us 40136 B
'Create and Fill a SafeArray on the system unmanaged heap with a single value.' 10000 220.75 us 47.79 us 140.90 us 208 B
tmds commented 6 years ago

40136 B

This number makes sense. If you allocate 10000 ints, this will be about 40KB.

22.81 us

This is significantly faster than the unmanaged heap, contrary to the results on the README.md.

937.5000 | 937.5000 | 937.5000

These are the numbers I am asking about: the number of collections for 'Create and Fill a managed array with a single value'. Any thoughts on what is allocated?

allisterb commented 6 years ago

It seems there was a severe bug in doing deallocation for the unmanaged buffers. This should be resolved now in the latest release. For the Fill benchmarks for SafeArrays these are the current results on my machine:


BenchmarkDotNet=v0.10.11, OS=Windows 10 Redstone 2 [1703, Creators Update] (10.0.15063.726)
Processor=Intel Core i7-6700HQ CPU 2.60GHz (Skylake), ProcessorCount=8
Frequency=2531251 Hz, Resolution=395.0616 ns, Timer=TSC
.NET Core SDK=2.1.2
  [Host] : .NET Core 2.0.3 (Framework 4.6.25815.02), 64bit RyuJIT

Job=JemBenchmark  Jit=RyuJit  Platform=X64  
Runtime=Core  AllowVeryLargeObjects=True  Toolchain=InProcessToolchain  
RunStrategy=Throughput  
Method Parameter Mean Error StdDev Gen 0 Gen 1 Gen 2 Allocated
'Fill a managed array with a single value.' 1000000 626.7 us 5.281 us 4.940 us - - - 256 B
'Fill a SafeArray on the system unmanaged heap with a single value.' 1000000 480.3 us 6.787 us 6.348 us - - - 248 B
'Create and Fill a managed array with a single value.' 1000000 1,938.7 us 35.826 us 33.511 us 330.8594 330.8594 330.8594 4001616 B
'Create and Fill a SafeArray on the system unmanaged heap with a single value.' 1000000 607.3 us 5.247 us 4.908 us - - - 216 B

You can try the latest release on your machine and let me know the results. Thanks for reporting this.

allisterb commented 6 years ago

It seems there was a severe bug in doing deallocation for the unmanaged buffers. This should be resolved now in the latest release. For the Fill benchmarks for SafeArrays these are the current results on my machine: jembench safe --fill -i 1000000


BenchmarkDotNet=v0.10.11, OS=Windows 10 Redstone 2 [1703, Creators Update] (10.0.15063.726)
Processor=Intel Core i7-6700HQ CPU 2.60GHz (Skylake), ProcessorCount=8
Frequency=2531251 Hz, Resolution=395.0616 ns, Timer=TSC
.NET Core SDK=2.1.2
  [Host] : .NET Core 2.0.3 (Framework 4.6.25815.02), 64bit RyuJIT

Job=JemBenchmark  Jit=RyuJit  Platform=X64  
Runtime=Core  AllowVeryLargeObjects=True  Toolchain=InProcessToolchain  
RunStrategy=Throughput  
Method Parameter Mean Error StdDev Gen 0 Gen 1 Gen 2 Allocated
'Fill a managed array with a single value.' 1000000 626.7 us 5.281 us 4.940 us - - - 256 B
'Fill a SafeArray on the system unmanaged heap with a single value.' 1000000 480.3 us 6.787 us 6.348 us - - - 248 B
'Create and Fill a managed array with a single value.' 1000000 1,938.7 us 35.826 us 33.511 us 330.8594 330.8594 330.8594 4001616 B
'Create and Fill a SafeArray on the system unmanaged heap with a single value.' 1000000 607.3 us 5.247 us 4.908 us - - - 216 B

You can try the latest release on your machine and let me know the results. Thanks for reporting this

tmds commented 5 years ago

I'm no longer looking into this, closing.