Skip to content

Go through ImmutableArray and switch manual looping to Array.Copy - #32373

Closed
Therzok wants to merge 7 commits into
dotnet:masterfrom
Therzok:immutable-array-copy
Closed

Go through ImmutableArray and switch manual looping to Array.Copy#32373
Therzok wants to merge 7 commits into
dotnet:masterfrom
Therzok:immutable-array-copy

Conversation

@Therzok

@TherzokTherzok commented Feb 15, 2020

Copy link
Copy Markdown
Contributor

Make the array copying consistent and use the optimized Array.Copy API in each case.

Remove the only 'CreateDefensiveCopy' call and the method, and
just forward to another overload
Passing explicit parameters should avoid calling GetLowerBound
array[i] = items[start + i];
}

Array.Copy(items, start, array, 0, length);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What is the break-even point for this? This will regress small sizes.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I'll setup a benchmark for this.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Answered in a comment below

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

So it makes some cases slower and some faster. I think we would need to additional data to tell whether this is an improvement on average. E.g. we would need data about how this method is used by Roslyn that is a heavy user of ImmutableArrays to make sure that this won't regress Roslyn performance.

The rest of the PR looks good to me. Could you please revert this change so that we can take the rest?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I can definitely split these across multiple atomic PRs so we tackle each different problem individually

@jkotas

Copy link
Copy Markdown
Member

Note that 3-argument version of Array.Copy is more efficient than the versions with more arguments in current master. The GetLowerBound problem does not exist in .NET Core after dotnet/coreclr#27634

System.Collections.Immutable runs on older runtimes too. So it is a interesting question whether to optimize it for latest .NET Core or for old runtimes. Ifdefs maybe the best answer.

@Therzok

Copy link
Copy Markdown
ContributorAuthor

Interesting. Well, I saw the extra call happening in code and most of the other code was using the 5 argument version. Also, there's a mention of this kind of optimization here, at the Peanut Butter section.

Most notably for this PR is consistency. Even List<T> uses Array.Copy, even in its CopyTo implementation.

I imagine if List<T> uses this mechanism everywhere, it should be okay to use it everywhere in ImmutableArray. Regardless, I'll setup a benchmark to see which array sizes this will regress performance on.

@jkotas

Copy link
Copy Markdown
Member

there's a mention of this kind of optimization here

That was the best way for .NET Core 3. It is no longer the best way for .NET 5.

In current master, we should be using the fewer-argument overload of Array.Copy where possible after dotnet/coreclr#27641 .

@Therzok

Copy link
Copy Markdown
ContributorAuthor

I've setup some benchmarks for the behaviour. Int32 and Object results.

@Therzok

Copy link
Copy Markdown
ContributorAuthor

Not sure how to test with .NET 5 though. It seems there's a small regression for small arrays of int, Count < 8, but on the object scenario it's an improvement.

@Therzok

Copy link
Copy Markdown
ContributorAuthor

Added another result for small int32 arrays. Cut-off seems to be at Count == 16.

@Therzok

Copy link
Copy Markdown
ContributorAuthor

Now I've just made myself curious of perf characteristics of:

vararr=newT[Count];varsrc=items.AsSpan(start,length);vardest=newSpan<T>(arr,offset);src.CopyTo(dest);

@Therzok

Copy link
Copy Markdown
ContributorAuthor

Hah, would you look at that:

BenchmarkDotNet=v0.12.0, OS=macOS 10.15.3 (19D76) [Darwin 19.3.0]
Intel Core i7-4980HQ CPU 2.80GHz (Haswell), 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=3.1.101
[Host] : .NET Core 3.1.1 (CoreCLR 4.700.19.60701, CoreFX 4.700.19.60801), X64 RyuJIT
DefaultJob : .NET Core 3.1.1 (CoreCLR 4.700.19.60701, CoreFX 4.700.19.60801), X64 RyuJIT
MethodCountMeanErrorStdDevRatioRatioSD
Loop15.039 ns0.0326 ns0.0305 ns1.000.00
Copy3Arg121.679 ns0.0681 ns0.0637 ns4.300.03
Copy5Arg115.476 ns0.0810 ns0.0632 ns3.070.02
Span18.150 ns0.0546 ns0.0484 ns1.620.01
Loop1616.076 ns0.0962 ns0.0853 ns1.000.00
Copy3Arg1623.715 ns0.1901 ns0.1778 ns1.480.01
Copy5Arg1618.222 ns0.0824 ns0.0771 ns1.130.01
Span1611.623 ns0.1140 ns0.1010 ns0.720.01
Loop3230.684 ns0.1605 ns0.1423 ns1.000.00
Copy3Arg3228.539 ns0.1637 ns0.1451 ns0.930.00
Copy5Arg3222.666 ns0.1230 ns0.1151 ns0.740.00
Span3216.398 ns0.1793 ns0.1677 ns0.530.01

@jkotas

Copy link
Copy Markdown
Member

System/Collections/Immutable/ImmutableArray.cs(157,31): error CS0103: The name 'length' does not exist in the current context

@Therzok

Copy link
Copy Markdown
ContributorAuthor

Sorry for that!

if (items == null)
if (items == null || items.Length == 0)
{
return Create<T>();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can this just return ImmutableArray<T>.Empty?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Yeah, I was thinking about that in the initial variant. I went for consistency, but I can change this, and if needed, all the others

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think it would be a good idea to avoid the indirection via Create method.

@Therzok

Copy link
Copy Markdown
ContributorAuthor

Superseded by #32386 and #32387

@TherzokTherzok closed this Feb 16, 2020
@Therzok
Therzok deleted the immutable-array-copy branch February 16, 2020 08:36
@ghostghost locked as resolved and limited conversation to collaborators Dec 10, 2020
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@Therzok@jkotas@Dotnet-GitSync-Bot