I have a List<(Guid, int)>
(a list of value-tuples), and I want to increment the Item2
field of an element at a specified index. Based on the answers in this question, there are two ways to do it:
The first is to get a copy of the existing
(Guid, int)
at the specified index, increment theItem2
field of the copy, and replace the existing element with the copy.The second is to use the
CollectionsMarshal.AsSpan
API (.NET 5), get theSpan<(Guid, int)>
representation of the backing array of the list, and update in-place theItem2
of the desirable element.
static void Increment1(List<(Guid, int)> list, int index)
{
(Guid, int) copy = list[index];
copy.Item2++;
list[index] = copy;
}
static void Increment2(List<(Guid, int)> list, int index)
{
Span<(Guid, int)> span = CollectionsMarshal.AsSpan(list);
span[index].Item2++;
}
Which of these two approaches is the most performant on the latest .NET platform (.NET 7)?
I'm going to treat this as a more general question about benchmarking so this answer is more generally useful. I'll use your proposed algorithms as an example though.
A naive approach to benchmarking in .NET would be to execute your algorithm wrapped in a
StopWatch
, however, except for the most crude of estimations, (talk order of magnitude levels of accuracy), this is pretty useless. So please don't do this. UseBenchmarkDotNet
instead.For simple tests such as this, it's very simple.
[Benchmark]
[GlobalSetup]
or[IterationSetup]
depending on whether or not you want it resetting between each test.Then run your console app and wait for the results.
In this case, the span-based implementation is an order of magnitude faster.
Given the massive improvements made in .NET 7 to LINQ, you might be able to get further improvements by replacing my loop with something that can make use of those improvements, but that's beyond the scope of this answer.