Skip to content

Instantly share code, notes, and snippets.

@john-h-k
Created September 3, 2019 19:02
Show Gist options
  • Select an option

  • Save john-h-k/f9117284e34ff980c0f95cff9b405aac to your computer and use it in GitHub Desktop.

Select an option

Save john-h-k/f9117284e34ff980c0f95cff9b405aac to your computer and use it in GitHub Desktop.
[CoreJob]
public class CopySignDBenchmark
{
public enum SignType
{
Random,
Same,
Different,
Alternating
}
private const int Seed = 98412310;
private const int Count = 32_768;
private double[] _dest;
public double[] Source1 { get; private set; }
public double[] Source2 { get; private set; }
[Params(SignType.Same, SignType.Different, SignType.Alternating, SignType.Random)]
public SignType Scenario { get; set; }
[GlobalSetup]
public void Setup()
{
Source1 = new double[Count];
Source2 = new double[Count];
_dest = new double[Count];
var rng = new Random(Seed);
for (int i = 0; i < Count; i++)
{
Source1[i] = rng.NextDouble();
Source2[i] = rng.NextDouble();
if (Scenario == SignType.Different)
{
Source2[i] = -Source2[i];
}
else if (Scenario == SignType.Alternating)
{
// Every other element should be inverted
if ((i % 2) == 0)
{
Source2[i] = -Source2[i];
}
}
else if (Scenario == SignType.Random)
{
var bits = BitConverter.DoubleToInt64Bits(Source2[i]);
// Value generated is already random, so do a parity check
// to determine if we should invert the value or not
if ((BitOperations.PopCount((ulong)bits) % 2) == 0)
{
Source2[i] = -Source2[i];
}
}
}
}
[Benchmark]
//[Arguments(1f, 1f)]
//[Arguments(1f, -1f)]
//[Arguments(-1f, -1f)]
public void Standard()
{
for (var i = 0; i < Count; i++)
{
double x = Source1[i];
double y = Source2[i];
// This method is required to work for all inputs,
// including NaN, so we operate on the raw bits.
long xbits = BitConverter.DoubleToInt64Bits(x);
long ybits = BitConverter.DoubleToInt64Bits(y);
// If the sign bits of x and y are not the same,
// flip the sign bit of x and return the new value;
// otherwise, just return x
if ((xbits ^ ybits) < 0)
{
x = BitConverter.Int64BitsToDouble(xbits ^ long.MinValue);
}
_dest[i] = x;
}
}
[Benchmark]
//[Arguments(1f, 1f)]
//[Arguments(1f, -1f)]
//[Arguments(-1f, -1f)]
public void John()
{
for (var i = 0; i < Count; i++)
{
double x = Source1[i];
double y = Source2[i];
// This method is required to work for all inputs,
// including NaN, so we operate on the raw bits.
long xbits = BitConverter.DoubleToInt64Bits(x);
long ybits = BitConverter.DoubleToInt64Bits(y);
// Remove the sign from x, and remove everything but the sign from y
const long signMask
= unchecked((long)0b_1000_0000__0000_0000__0000_0000__0000_0000__0000_0000__0000_0000__0000_0000__0000_0000);
xbits &= ~signMask;
ybits &= signMask;
// Simply OR them to get the correct sign
x = BitConverter.Int64BitsToDouble(xbits | ybits);
_dest[i] = x;
}
}
[Benchmark]
//[Arguments(1f, 1f)]
//[Arguments(1f, -1f)]
//[Arguments(-1f, -1f)]
public void John_Intrinsic()
{
for (var i = 0; i < Count; i++)
{
double x = Source1[i];
double y = Source2[i];
const long signMask
= unchecked((long)0b_1000_0000__0000_0000__0000_0000__0000_0000__0000_0000__0000_0000__0000_0000__0000_0000);
if (Sse.IsSupported)
{
var xvec = Vector128.CreateScalarUnsafe(x).AsSingle();
var yvec = Vector128.CreateScalarUnsafe(y).AsSingle();
xvec = Sse.And(xvec, Vector128.CreateScalarUnsafe(~signMask).AsSingle());
yvec = Sse.And(yvec, Vector128.CreateScalarUnsafe(signMask).AsSingle());
x = Sse.Or(xvec, yvec).AsDouble().ToScalar();
}
else
{
// This method is required to work for all inputs,
// including NaN, so we operate on the raw bits.
long xbits = BitConverter.DoubleToInt64Bits(x);
long ybits = BitConverter.DoubleToInt64Bits(y);
// Remove the sign from x, and remove everything but the sign from y
xbits &= ~signMask;
ybits &= signMask;
// Simply OR them to get the correct sign
x = BitConverter.Int64BitsToDouble(xbits | ybits);
}
_dest[i] = x;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment