Skip to content

Instantly share code, notes, and snippets.

@ksysiekj
Last active January 19, 2024 22:21
Show Gist options
  • Save ksysiekj/8f952a6cb26cf7db4185c008f696a94f to your computer and use it in GitHub Desktop.
Save ksysiekj/8f952a6cb26cf7db4185c008f696a94f to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='utf-8' />
<title>LinqBenchmarks.LinqBenchmark-20240119-231018</title>
<style type="text/css">
table { border-collapse: collapse; display: block; width: 100%; overflow: auto; }
td, th { padding: 6px 13px; border: 1px solid #ddd; text-align: right; }
tr { background-color: #fff; border-top: 1px solid #ccc; }
tr:nth-child(even) { background: #f8f8f8; }
</style>
</head>
<body>
<pre><code>
BenchmarkDotNet v0.13.12, Windows 10 (10.0.19045.3803/22H2/2022Update)
Intel Core i7-3632QM CPU 2.20GHz (Ivy Bridge), 1 CPU, 8 logical and 4 physical cores
.NET SDK 8.0.101
[Host] : .NET 8.0.1 (8.0.123.58001), X64 RyuJIT AVX [AttachedDebugger]
Job-CRZKKL : .NET 8.0.1 (8.0.123.58001), X64 RyuJIT AVX
</code></pre>
<pre><code>InvocationCount=15 IterationCount=13 RunStrategy=ColdStart
UnrollFactor=1 WarmupCount=3
</code></pre>
<table>
<thead><tr><th>Method </th><th>Count</th><th>Mean </th><th>Error </th><th>StdDev</th><th>Median </th><th>Ratio</th><th>RatioSD</th><th>Rank</th><th>Gen0</th><th>Gen1</th><th>Allocated</th><th>Alloc Ratio</th>
</tr>
</thead><tbody><tr><td>Linq</td><td>100</td><td>17.03 &mu;s</td><td>42.16 &mu;s</td><td>35.21 &mu;s</td><td>7.473 &mu;s</td><td>1.00</td><td>0.00</td><td>1</td><td>-</td><td>-</td><td>1027 B</td><td>1.00</td>
</tr><tr><td>LinqStatic</td><td>100</td><td>25.32 &mu;s</td><td>66.22 &mu;s</td><td>55.30 &mu;s</td><td>9.340 &mu;s</td><td>1.50</td><td>0.64</td><td>2</td><td>-</td><td>-</td><td>1027 B</td><td>1.00</td>
</tr><tr><td>NoAllocToSpanEnumerableStatic</td><td>100</td><td>103.84 &mu;s</td><td>315.80 &mu;s</td><td>263.71 &mu;s</td><td>31.973 &mu;s</td><td>4.88</td><td>1.98</td><td>4</td><td>-</td><td>-</td><td>1051 B</td><td>1.02</td>
</tr><tr><td>NoAllocAsSpanStatic</td><td>100</td><td>78.39 &mu;s</td><td>268.20 &mu;s</td><td>223.96 &mu;s</td><td>17.187 &mu;s</td><td>2.69</td><td>1.26</td><td>3</td><td>-</td><td>-</td><td>999 B</td><td>0.97</td>
</tr><tr><td>RefStructLinqStructStatic</td><td>100</td><td>223.90 &mu;s</td><td>877.46 &mu;s</td><td>732.72 &mu;s</td><td>20.407 &mu;s</td><td>4.53</td><td>4.91</td><td>5</td><td>-</td><td>-</td><td>809 B</td><td>0.79</td>
</tr><tr><td>StructLinqStaticZeroAlloc</td><td>100</td><td>292.21 &mu;s</td><td>1,195.45 &mu;s</td><td>998.26 &mu;s</td><td>12.613 &mu;s</td><td>4.20</td><td>6.94</td><td>6</td><td>-</td><td>-</td><td>761 B</td><td>0.74</td>
</tr><tr><td>StructLinqStaticZeroPerformanceAlloc</td><td>100</td><td>283.16 &mu;s</td><td>1,161.36 &mu;s</td><td>969.79 &mu;s</td><td>14.507 &mu;s</td><td>4.00</td><td>6.72</td><td>6</td><td>-</td><td>-</td><td>761 B</td><td>0.74</td>
</tr><tr><td>Linq</td><td>1000</td><td>77.94 &mu;s</td><td>102.03 &mu;s</td><td>85.20 &mu;s</td><td>54.960 &mu;s</td><td>1.00</td><td>0.00</td><td>1</td><td>-</td><td>-</td><td>7811 B</td><td>1.00</td>
</tr><tr><td>LinqStatic</td><td>1000</td><td>74.65 &mu;s</td><td>58.95 &mu;s</td><td>49.23 &mu;s</td><td>63.400 &mu;s</td><td>1.11</td><td>0.33</td><td>1</td><td>-</td><td>-</td><td>7811 B</td><td>1.00</td>
</tr><tr><td>NoAllocToSpanEnumerableStatic</td><td>1000</td><td>333.49 &mu;s</td><td>277.78 &mu;s</td><td>231.96 &mu;s</td><td>302.393 &mu;s</td><td>4.93</td><td>1.45</td><td>5</td><td>-</td><td>-</td><td>8675 B</td><td>1.11</td>
</tr><tr><td>NoAllocAsSpanStatic</td><td>1000</td><td>191.13 &mu;s</td><td>288.30 &mu;s</td><td>240.74 &mu;s</td><td>133.520 &mu;s</td><td>2.38</td><td>0.65</td><td>2</td><td>-</td><td>-</td><td>8643 B</td><td>1.11</td>
</tr><tr><td>RefStructLinqStructStatic</td><td>1000</td><td>314.11 &mu;s</td><td>747.47 &mu;s</td><td>624.17 &mu;s</td><td>151.407 &mu;s</td><td>2.93</td><td>1.28</td><td>4</td><td>-</td><td>-</td><td>6569 B</td><td>0.84</td>
</tr><tr><td>StructLinqStaticZeroAlloc</td><td>1000</td><td>303.04 &mu;s</td><td>954.71 &mu;s</td><td>797.22 &mu;s</td><td>93.773 &mu;s</td><td>2.06</td><td>1.90</td><td>3</td><td>-</td><td>-</td><td>6521 B</td><td>0.83</td>
</tr><tr><td>StructLinqStaticZeroPerformanceAlloc</td><td>1000</td><td>270.19 &mu;s</td><td>834.84 &mu;s</td><td>697.13 &mu;s</td><td>75.280 &mu;s</td><td>1.89</td><td>1.68</td><td>3</td><td>-</td><td>-</td><td>6521 B</td><td>0.83</td>
</tr><tr><td>Linq</td><td>10000</td><td>487.01 &mu;s</td><td>274.97 &mu;s</td><td>229.61 &mu;s</td><td>549.547 &mu;s</td><td>1.00</td><td>0.00</td><td>1</td><td>-</td><td>-</td><td>72739 B</td><td>1.00</td>
</tr><tr><td>LinqStatic</td><td>10000</td><td>434.23 &mu;s</td><td>234.29 &mu;s</td><td>195.64 &mu;s</td><td>494.447 &mu;s</td><td>1.14</td><td>0.86</td><td>1</td><td>-</td><td>-</td><td>72739 B</td><td>1.00</td>
</tr><tr><td>NoAllocToSpanEnumerableStatic</td><td>10000</td><td>1,919.30 &mu;s</td><td>1,250.77 &mu;s</td><td>1,044.45 &mu;s</td><td>2,247.220 &mu;s</td><td>4.56</td><td>2.62</td><td>3</td><td>-</td><td>-</td><td>80683 B</td><td>1.11</td>
</tr><tr><td>NoAllocAsSpanStatic</td><td>10000</td><td>1,147.08 &mu;s</td><td>574.89 &mu;s</td><td>480.06 &mu;s</td><td>1,178.180 &mu;s</td><td>2.59</td><td>1.02</td><td>3</td><td>-</td><td>-</td><td>80651 B</td><td>1.11</td>
</tr><tr><td>RefStructLinqStructStatic</td><td>10000</td><td>1,289.87 &mu;s</td><td>824.93 &mu;s</td><td>688.85 &mu;s</td><td>1,048.067 &mu;s</td><td>3.23</td><td>1.76</td><td>3</td><td>-</td><td>-</td><td>64169 B</td><td>0.88</td>
</tr><tr><td>StructLinqStaticZeroAlloc</td><td>10000</td><td>924.29 &mu;s</td><td>825.47 &mu;s</td><td>689.31 &mu;s</td><td>754.093 &mu;s</td><td>2.12</td><td>1.11</td><td>2</td><td>-</td><td>-</td><td>64121 B</td><td>0.88</td>
</tr><tr><td>StructLinqStaticZeroPerformanceAlloc</td><td>10000</td><td>835.45 &mu;s</td><td>832.44 &mu;s</td><td>695.13 &mu;s</td><td>651.660 &mu;s</td><td>1.82</td><td>1.00</td><td>2</td><td>-</td><td>-</td><td>64121 B</td><td>0.88</td>
</tr><tr><td>Linq</td><td>1000000</td><td>50,612.51 &mu;s</td><td>24,954.45 &mu;s</td><td>20,838.10 &mu;s</td><td>41,931.267 &mu;s</td><td>1.00</td><td>0.00</td><td>2</td><td>933.3333</td><td>466.6667</td><td>7449462 B</td><td>1.00</td>
</tr><tr><td>LinqStatic</td><td>1000000</td><td>50,721.75 &mu;s</td><td>28,266.20 &mu;s</td><td>23,603.55 &mu;s</td><td>43,100.287 &mu;s</td><td>1.01</td><td>0.24</td><td>2</td><td>933.3333</td><td>466.6667</td><td>7449826 B</td><td>1.00</td>
</tr><tr><td>NoAllocToSpanEnumerableStatic</td><td>1000000</td><td>71,570.22 &mu;s</td><td>18,859.43 &mu;s</td><td>15,748.48 &mu;s</td><td>66,139.707 &mu;s</td><td>1.52</td><td>0.40</td><td>3</td><td>933.3333</td><td>466.6667</td><td>8498018 B</td><td>1.14</td>
</tr><tr><td>NoAllocAsSpanStatic</td><td>1000000</td><td>46,823.22 &mu;s</td><td>8,652.38 &mu;s</td><td>7,225.13 &mu;s</td><td>45,998.967 &mu;s</td><td>1.03</td><td>0.31</td><td>2</td><td>933.3333</td><td>466.6667</td><td>8497970 B</td><td>1.14</td>
</tr><tr><td>RefStructLinqStructStatic</td><td>1000000</td><td>61,907.58 &mu;s</td><td>11,337.82 &mu;s</td><td>9,467.59 &mu;s</td><td>56,572.453 &mu;s</td><td>1.34</td><td>0.33</td><td>3</td><td>866.6667</td><td>400.0000</td><td>6400552 B</td><td>0.86</td>
</tr><tr><td>StructLinqStaticZeroAlloc</td><td>1000000</td><td>41,577.96 &mu;s</td><td>7,242.12 &mu;s</td><td>6,047.50 &mu;s</td><td>38,881.953 &mu;s</td><td>0.93</td><td>0.30</td><td>1</td><td>866.6667</td><td>400.0000</td><td>6400504 B</td><td>0.86</td>
</tr><tr><td>StructLinqStaticZeroPerformanceAlloc</td><td>1000000</td><td>39,547.39 &mu;s</td><td>7,171.81 &mu;s</td><td>5,988.78 &mu;s</td><td>36,427.280 &mu;s</td><td>0.87</td><td>0.26</td><td>1</td><td>866.6667</td><td>400.0000</td><td>6400523 B</td><td>0.86</td>
</tr></tbody></table>
</body>
</html>
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Engines;
using BenchmarkDotNet.Running;
using NoAlloq;
using StructLinq;
namespace LinqBenchmarks
{
internal class Program
{
static void Main(string[] args)
{
BenchmarkRunner.Run<LinqBenchmark>();
}
}
[MemoryDiagnoser]
[RankColumn]
[SimpleJob(RunStrategy.ColdStart,warmupCount:3,iterationCount:13, invocationCount:15)]
public class LinqBenchmark
{
private User[] _users;
[Params(100, 1_000, 10_000, 1_000_000)]
public int Count { get; set; }
[GlobalSetup]
public void Setup()
{
_users = Enumerable.Range(0, Count).Select(static i => new User
{
Age = i % 38,
Id = Guid.NewGuid(),
LastName = $"Smith_{i}",
Name = $"John",
InternalId = i
}).ToArray();
}
[Benchmark(Baseline =true)]
public void Linq()
{
var dtos = _users.Where(q => q.InternalId % 10 == 7).Select(u => u.MapToDto()).ToArray();
}
[Benchmark]
public void LinqStatic()
{
var dtos = _users.Where(static q => q.InternalId % 10 == 7).Select(static u => u.MapToDto()).ToArray();
}
[Benchmark]
public void NoAllocToSpanEnumerableStatic()
{
var dtos = _users.ToSpanEnumerable().Where(static q => q.InternalId % 10 == 7).Select(static u => u.MapToDto()).ToArray();
}
[Benchmark]
public void NoAllocAsSpanStatic()
{
var dtos = _users.AsSpan().Where(static q => q.InternalId % 10 == 7).Select(static u => u.MapToDto()).ToArray();
}
[Benchmark]
public void RefStructLinqStructStatic()
{
var dtos = _users.ToRefStructEnumerable().Where((in User q) => q.InternalId % 10 == 7).Select((in User u) => u.MapToDto()).ToArray();
}
[Benchmark]
public void StructLinqStaticZeroAlloc()
{
var dtos = _users.ToStructEnumerable().Where(static q => q.InternalId % 10 == 7, w=>w).Select(static u => u.MapToDto(), v=>v).ToArray();
}
[Benchmark]
public void StructLinqStaticZeroPerformanceAlloc()
{
var @where = new UserWhereFunc();
var @select = new UserDtoSelectFunc();
var dtos = _users.ToStructEnumerable()
.Where(ref @where, w => w)
.Select(ref @select, v => v, n => n)
.ToArray();
}
}
struct RefUserDtoSelectFunc : IFunction<User, UserDto>
{
public UserDto Eval(User user)
{
return user.MapToDto();
}
}
struct RefUserWhereFunc : IFunction<User, bool>
{
public bool Eval(User user)
{
return user.InternalId % 10 == 7;
}
}
struct UserDtoSelectFunc : IFunction<User, UserDto>
{
public readonly UserDto Eval(User user)
{
return user.MapToDto();
}
}
struct UserWhereFunc : IFunction<User, bool>
{
public readonly bool Eval(User user)
{
return user.InternalId % 10 == 7;
}
}
public class User
{
public int Age { get; init; }
public string Name { get; init; }
public string LastName { get; init; }
public Guid Id { get; init; }
public int InternalId { get; init; }
}
static class UserExtensions
{
internal static UserDto MapToDto(this User user)
{
return new UserDto
{
Age = user.Age,
LastName = user.LastName,
Name = user.Name,
Id = user.Id
};
}
}
public class UserDto
{
public int Age { get; init; }
public string Name { get; init; }
public string LastName { get; init; }
public Guid Id { get; init; }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment