Skip to content

Commit

Permalink
Remove some boxing from tuples with >= 8 elements
Browse files Browse the repository at this point in the history
Take advantage of dotnet#14698 to avoid boxing the TRest argument and improve devirtualization.
  • Loading branch information
stephentoub committed Sep 8, 2019
1 parent 456afea commit 0bb34e3
Showing 1 changed file with 24 additions and 20 deletions.
44 changes: 24 additions & 20 deletions src/System.Private.CoreLib/shared/System/ValueTuple.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2054,8 +2054,8 @@ int IStructuralComparable.CompareTo(object? other, IComparer comparer)
/// <returns>A 32-bit signed integer hash code.</returns>
public override int GetHashCode()
{
// We want to have a limited hash in this case. We'll use the last 8 elements of the tuple
if (!(Rest is IValueTupleInternal rest))
// We want to have a limited hash in this case. We'll use the first 7 elements of the tuple
if (!(Rest is IValueTupleInternal))
{
return ValueTuple.CombineHashCodes(Item1?.GetHashCode() ?? 0,
Item2?.GetHashCode() ?? 0,
Expand All @@ -2066,46 +2066,50 @@ public override int GetHashCode()
Item7?.GetHashCode() ?? 0);
}

int size = rest.Length;
if (size >= 8) { return rest.GetHashCode(); }
int size = ((IValueTupleInternal)Rest).Length;
int restHashCode = Rest.GetHashCode();
if (size >= 8)
{
return restHashCode;
}

// In this case, the rest member has less than 8 elements so we need to combine some our elements with the elements in rest
// In this case, the rest member has less than 8 elements so we need to combine some of our elements with the elements in rest
int k = 8 - size;
switch (k)
{
case 1:
return ValueTuple.CombineHashCodes(Item7?.GetHashCode() ?? 0,
rest.GetHashCode());
restHashCode);
case 2:
return ValueTuple.CombineHashCodes(Item6?.GetHashCode() ?? 0,
Item7?.GetHashCode() ?? 0,
rest.GetHashCode());
restHashCode);
case 3:
return ValueTuple.CombineHashCodes(Item5?.GetHashCode() ?? 0,
Item6?.GetHashCode() ?? 0,
Item7?.GetHashCode() ?? 0,
rest.GetHashCode());
restHashCode);
case 4:
return ValueTuple.CombineHashCodes(Item4?.GetHashCode() ?? 0,
Item5?.GetHashCode() ?? 0,
Item6?.GetHashCode() ?? 0,
Item7?.GetHashCode() ?? 0,
rest.GetHashCode());
restHashCode);
case 5:
return ValueTuple.CombineHashCodes(Item3?.GetHashCode() ?? 0,
Item4?.GetHashCode() ?? 0,
Item5?.GetHashCode() ?? 0,
Item6?.GetHashCode() ?? 0,
Item7?.GetHashCode() ?? 0,
rest.GetHashCode());
restHashCode);
case 6:
return ValueTuple.CombineHashCodes(Item2?.GetHashCode() ?? 0,
Item3?.GetHashCode() ?? 0,
Item4?.GetHashCode() ?? 0,
Item5?.GetHashCode() ?? 0,
Item6?.GetHashCode() ?? 0,
Item7?.GetHashCode() ?? 0,
rest.GetHashCode());
restHashCode);
case 7:
case 8:
return ValueTuple.CombineHashCodes(Item1?.GetHashCode() ?? 0,
Expand All @@ -2115,7 +2119,7 @@ public override int GetHashCode()
Item5?.GetHashCode() ?? 0,
Item6?.GetHashCode() ?? 0,
Item7?.GetHashCode() ?? 0,
rest.GetHashCode());
restHashCode);
}

Debug.Fail("Missed all cases for computing ValueTuple hash code");
Expand All @@ -2129,7 +2133,7 @@ int IStructuralEquatable.GetHashCode(IEqualityComparer comparer)

private int GetHashCodeCore(IEqualityComparer comparer)
{
// We want to have a limited hash in this case. We'll use the last 8 elements of the tuple
// We want to have a limited hash in this case. We'll use the first 7 elements of the tuple
if (!(Rest is IValueTupleInternal rest))
{
return ValueTuple.CombineHashCodes(comparer.GetHashCode(Item1!), comparer.GetHashCode(Item2!), comparer.GetHashCode(Item3!),
Expand Down Expand Up @@ -2187,19 +2191,19 @@ int IValueTupleInternal.GetHashCode(IEqualityComparer comparer)
/// </remarks>
public override string ToString()
{
if (Rest is IValueTupleInternal rest)
if (Rest is IValueTupleInternal)
{
return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ", " + Item7?.ToString() + ", " + rest.ToStringEnd();
return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ", " + Item7?.ToString() + ", " + ((IValueTupleInternal)Rest).ToStringEnd();
}

return "(" + Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ", " + Item7?.ToString() + ", " + Rest.ToString() + ")";
}

string IValueTupleInternal.ToStringEnd()
{
if (Rest is IValueTupleInternal rest)
if (Rest is IValueTupleInternal)
{
return Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ", " + Item7?.ToString() + ", " + rest.ToStringEnd();
return Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ", " + Item7?.ToString() + ", " + ((IValueTupleInternal)Rest).ToStringEnd();
}

return Item1?.ToString() + ", " + Item2?.ToString() + ", " + Item3?.ToString() + ", " + Item4?.ToString() + ", " + Item5?.ToString() + ", " + Item6?.ToString() + ", " + Item7?.ToString() + ", " + Rest.ToString() + ")";
Expand All @@ -2208,7 +2212,7 @@ string IValueTupleInternal.ToStringEnd()
/// <summary>
/// The number of positions in this data structure.
/// </summary>
int ITuple.Length => Rest is IValueTupleInternal rest ? 7 + rest.Length : 8;
int ITuple.Length => Rest is IValueTupleInternal ? 7 + ((IValueTupleInternal)Rest).Length : 8;

/// <summary>
/// Get the element at position <param name="index"/>.
Expand All @@ -2235,9 +2239,9 @@ string IValueTupleInternal.ToStringEnd()
return Item7;
}

if (Rest is IValueTupleInternal rest)
if (Rest is IValueTupleInternal)
{
return rest[index - 7];
return ((IValueTupleInternal)Rest)[index - 7];
}


Expand Down

0 comments on commit 0bb34e3

Please sign in to comment.