Skip to content

Commit

Permalink
Merge pull request #10 from cklutz/array-sampling
Browse files Browse the repository at this point in the history
Array sampling, object pooling & more
  • Loading branch information
cklutz authored Jan 8, 2024
2 parents a4202e0 + 6aef32f commit 48d0e8e
Show file tree
Hide file tree
Showing 23 changed files with 2,103 additions and 530 deletions.
2 changes: 1 addition & 1 deletion LICENSE.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2023 Christian Klutz
Copyright (c) 2024 Christian Klutz

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
41 changes: 41 additions & 0 deletions src/ManagedObjectSize.Benchmarks/ArraySamplingBenchmarks.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Diagnostics.Windows.Configs;

namespace ManagedObjectSize.Benchmarks
{
[MemoryDiagnoser, EtwProfiler]
public class ArraySamplingBenchmarks
{
[Params(20, 100)] public int N;

private GraphObject m_graphData = null!;
private int[] m_intData = null!;
private string[] m_stringData = null!;

private ObjectSizeOptions m_samplingOptions = null!;

[GlobalSetup]
public void GlobalSetup()
{
m_graphData = GraphObject.CreateObjectGraph(N);
m_intData = new int[N];
m_stringData = new string[N];

for (int i = 0; i < N; i++)
{
m_intData[i] = i;
m_stringData[i] = "string#" + i;
}

m_samplingOptions = new() { ArraySampleCount = N / 10 };
}

[Benchmark] public long NoSampling_Int32() => ObjectSize.GetObjectInclusiveSize(m_intData);
[Benchmark] public long NoSampling_String() => ObjectSize.GetObjectInclusiveSize(m_stringData);
[Benchmark] public long NoSampling_Graph() => ObjectSize.GetObjectInclusiveSize(m_graphData);

[Benchmark] public long Sampling_Int32() => ObjectSize.GetObjectInclusiveSize(m_intData, m_samplingOptions);
[Benchmark] public long Sampling_String() => ObjectSize.GetObjectInclusiveSize(m_stringData, m_samplingOptions);
[Benchmark] public long Sampling_Graph() => ObjectSize.GetObjectInclusiveSize(m_graphData, m_samplingOptions);
}
}
50 changes: 50 additions & 0 deletions src/ManagedObjectSize.Benchmarks/GraphData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
namespace ManagedObjectSize.Benchmarks
{
internal class GraphObject
{
public static GraphObject CreateObjectGraph(int num, bool inner = false)
{
var graph = new GraphObject
{
ListField = new List<GraphNodeObject>(num)
};

int digits = (int)Math.Log10(num) + 1;
var options = new ParallelOptions { MaxDegreeOfParallelism = (inner || num < 100) ? 1 : Environment.ProcessorCount };
Parallel.For(0, num, options,
() => new List<GraphNodeObject>(),
(i, state, local) =>
{
var node = new GraphNodeObject { StringField = "Node#" };
if (!inner)
{
node.ObjectField = CreateObjectGraph(100, true);
}
local.Add(node);
return local;
},
local =>
{
lock (graph.ListField)
{
graph.ListField.AddRange(local);
}
});

return graph;
}

#pragma warning disable CS0649

public int IntField;
public List<GraphNodeObject> ListField = null!;

public class GraphNodeObject
{
public double DoubleField;
public int IntField;
public string StringField = null!;
public GraphObject ObjectField = null!;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="BenchmarkDotNet.Diagnostics.Windows" Version="0.13.12" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\ManagedObjectSize.ObjectPool\ManagedObjectSize.ObjectPool.csproj" />
<ProjectReference Include="..\ManagedObjectSize\ManagedObjectSize.csproj" />
</ItemGroup>

</Project>
25 changes: 25 additions & 0 deletions src/ManagedObjectSize.Benchmarks/ObjectPoolBenchmarks.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using BenchmarkDotNet.Attributes;
using ManagedObjectSize.ObjectPool;

namespace ManagedObjectSize.Benchmarks
{
[MemoryDiagnoser]
public class ObjectPoolBenchmarks
{
[Params(100, 1000)] public int N;

private GraphObject m_graphData = null!;
private ObjectSizeOptions m_options = null!;

[GlobalSetup]
public void GlobalSetup()
{
m_graphData = GraphObject.CreateObjectGraph(N);
m_options = new ObjectSizeOptions().UseMicrosoftExtensionsObjectPool();
}

[Benchmark] public long NoPool() => ObjectSize.GetObjectInclusiveSize(m_graphData);

[Benchmark] public long Pool() => ObjectSize.GetObjectInclusiveSize(m_graphData, m_options);
}
}
15 changes: 15 additions & 0 deletions src/ManagedObjectSize.Benchmarks/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Running;

namespace ManagedObjectSize.Benchmarks
{
internal class Program
{
public static void Main(string[] args)
{
var xargs = new List<string>(args);
var config = ManualConfig.Create(DefaultConfig.Instance);
BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(xargs.ToArray(), config);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>

<IsPackable>true</IsPackable>
<PackageId>ManagedObjectSize.ObjectPool</PackageId>
<Version>0.0.6</Version>
<Authors>Christian Klutz</Authors>
<Description>
Adapts Microsoft.Extensions.ObjectPool to be used with ManagedObjectSize package.
</Description>
<InformationalVersion>$(Version).0</InformationalVersion>
<FileVersion>$(Version).0</FileVersion>
<PackageLicenseFile>LICENSE.txt</PackageLicenseFile>
<RepositoryUrl>https://github.com/cklutz/ManagedObjectSize</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>

<ItemGroup>
<None Include="..\..\LICENSE.txt" Link="LICENSE.txt">
<PackagePath>\</PackagePath>
<Pack>true</Pack>
</None>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\ManagedObjectSize\ManagedObjectSize.csproj" />
</ItemGroup>


<ItemGroup>
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="8.0.0" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using ManagedObjectSize.Pooling;
using Microsoft.Extensions.ObjectPool;

namespace ManagedObjectSize.ObjectPool
{
/// <summary>
/// Adapts a <see cref="Microsoft.Extensions.ObjectPool.ObjectPool"/> to be used
/// as <see cref="ObjectSizeOptions.PoolProvider"/>.
/// </summary>
public class MicrosoftExtensionsObjectPoolPoolProvider : PoolProvider
{
private class PolicyAdapter<T> : IPooledObjectPolicy<T> where T : notnull
{
private readonly IPoolPolicy<T> m_policy;
public PolicyAdapter(IPoolPolicy<T> policy) => m_policy = policy;
public T Create() => m_policy.Create();
public bool Return(T obj) => m_policy.Return(obj);
}

private class PoolAdapter<T> : Pool<T> where T : class
{
private readonly ObjectPool<T> m_pool;
public PoolAdapter(ObjectPool<T> pool) => m_pool = pool;
public override T Get() => m_pool.Get();
public override void Return(T obj) => m_pool.Return(obj);
}

private readonly ObjectPoolProvider m_provider;

public MicrosoftExtensionsObjectPoolPoolProvider()
: this(new DefaultObjectPoolProvider())
{
}

public MicrosoftExtensionsObjectPoolPoolProvider(ObjectPoolProvider objectPoolProvider)
{
m_provider = objectPoolProvider ?? throw new ArgumentNullException(nameof(objectPoolProvider));
}

public override Pool<T> Create<T>(IPoolPolicy<T> policy)
{
return new PoolAdapter<T>(m_provider.Create(new PolicyAdapter<T>(policy)));
}
}
}
28 changes: 28 additions & 0 deletions src/ManagedObjectSize.ObjectPool/ObjectPoolExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using Microsoft.Extensions.ObjectPool;

namespace ManagedObjectSize.ObjectPool
{
public static class ObjectPoolExtensions
{
/// <summary>
/// Configures <see cref="ObjectSizeOptions"/> to use an object pool based on <see cref="ObjectPoolProvider"/>.
/// </summary>
/// <param name="options">The options instance.</param>
/// <param name="provider">
/// The <see cref="ObjectPoolProvider"/> to be used. If <c>null</c>, an instance of the <see cref="DefaultObjectPoolProvider"/> will be used.</param>
/// <returns>The options instanced given as <paramref name="options"/>.</returns>
public static ObjectSizeOptions UseMicrosoftExtensionsObjectPool(this ObjectSizeOptions options, ObjectPoolProvider? provider = null)
{
if (provider == null)
{
options.PoolProvider = new MicrosoftExtensionsObjectPoolPoolProvider();
}
else
{
options.PoolProvider = new MicrosoftExtensionsObjectPoolPoolProvider(provider);
}

return options;
}
}
}
3 changes: 2 additions & 1 deletion src/ManagedObjectSize.Tests/ManagedObjectSize.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Diagnostics.Runtime.Utilities" Version="2.3.405501" />
<PackageReference Include="Microsoft.Diagnostics.Runtime.Utilities" Version="3.0.442202" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
<PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />
<PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
Expand All @@ -19,6 +19,7 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\ManagedObjectSize.ObjectPool\ManagedObjectSize.ObjectPool.csproj" />
<ProjectReference Include="..\ManagedObjectSize\ManagedObjectSize.csproj" />
</ItemGroup>

Expand Down
19 changes: 19 additions & 0 deletions src/ManagedObjectSize.Tests/ObjectSizeOptionsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System;

namespace ManagedObjectSize.Tests
{
[TestClass]
public class ObjectSizeOptionsTests
{
[DataTestMethod]
[DataRow(0.95, 5, 100, 80)]
[DataRow(0.99, 5, 100, 87)]
[DataRow(0.95, 5, 100_000_000, 384)]
[DataRow(0.99, 5, 100_000_000, 663)]
public void CalculateSampleCount(double confidenceLevel, int confidenceInterval, int populationSize, int expectedSampleSize)
{
int actualSampleSize = Utils.CalculateSampleCount(confidenceLevel, confidenceInterval, populationSize);
Assert.AreEqual(expectedSampleSize, actualSampleSize);
}
}
}
Loading

0 comments on commit 48d0e8e

Please sign in to comment.