I use the following structure in a web service where performance is critical. It is used for making joins between domain data objects, the key is made of two integers. The idea is to fit the two 32 bits integer in a long (64 bits).
Before using that structure, I used Tuple<int, int>
which was really slower.
Is there any additional optimization to be made? (e.g.: addtional override, unneeded cast etc...)
public struct CombinedKey : IEquatable<CombinedKey>
{
private readonly long value;
public CombinedKey(int item1, int item2)
{
unchecked
{
value = (long)item1 << 32 | (uint)item2;
}
}
public override int GetHashCode()
{
return (int)value ^ (int)(value >> 32);
}
public bool Equals(CombinedKey other)
{
return this.value == other.value;
}
public int Item1
{
get
{
return (int)(this.value >> 32);
}
}
public int Item2
{
get
{
return (int)(this.value & 0xffffffff);
}
}
}
1 Answer 1
You could use explicit layout of the structure to omit values calculation.
It can act similar to the union
in C/C++:
[StructLayout(LayoutKind.Explicit)]
public struct CombinedKey : IEquatable<CombinedKey>
{
[FieldOffset(0)]
private readonly long value;
[FieldOffset(0)]
public readonly int Item1;
[FieldOffset(sizeof(int))]
public readonly int Item2;
public CombinedKey(int item1, int item2)
{
value = 0; // We need to init all the fields
Item1 = item1;
Item2 = item2;
}
public bool Equals(CombinedKey other)
{
return value == other.value;
}
public override int GetHashCode()
{
return Item1 ^ Item2;
}
}
You could also wrap readonly fields into get-only properties.
Additional link: [StructLayout attribute magic][2]
-
\$\begingroup\$ @Dmitry : Thanks for this. I tested performance of your solution against the original one. It is very similar if not identical. However I prefer your solution as it is shorter. \$\endgroup\$tigrou– tigrou2015年09月11日 13:27:37 +00:00Commented Sep 11, 2015 at 13:27
-
\$\begingroup\$ I needed a while to get what's going on here and read the links a few times but now I find it's a pretty cool trick :-) I also think it would be easier to understand if there was
sizeof(int)
instead of the4
like in the second link. \$\endgroup\$t3chb0t– t3chb0t2015年09月11日 19:47:51 +00:00Commented Sep 11, 2015 at 19:47 -
1\$\begingroup\$ Slick. I'll be back to award a bounty for teaching me something new. ++ \$\endgroup\$RubberDuck– RubberDuck2015年09月12日 11:49:52 +00:00Commented Sep 12, 2015 at 11:49
-
\$\begingroup\$
IEquatable<T>
Reminder from MSDN: "If you implement IEquatable<T>, you should also override the base class implementations of Object.Equals(Object) and GetHashCode so that their behavior is consistent with that of the IEquatable<T>.Equals method." \$\endgroup\$radarbob– radarbob2015年09月13日 22:40:03 +00:00Commented Sep 13, 2015 at 22:40 -
1\$\begingroup\$ I'd rather avoid low level hacks like this. For example one issue with this is that the generated long depends on host endianness. \$\endgroup\$CodesInChaos– CodesInChaos2015年09月14日 08:29:58 +00:00Commented Sep 14, 2015 at 8:29
item1
anditem2
as two fields? \$\endgroup\$long
? \$\endgroup\$long
in code it can mean lot of things,CombinedKey
is obvious. \$\endgroup\$