Created
September 4, 2019 10:20
-
-
Save john-h-k/6afa22a904ed81c8e1c0e82b3e5b7600 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[CoreJob] | |
public class CopySignSingleBenchmark | |
{ | |
public enum SignType | |
{ | |
Random, | |
Same, | |
Different, | |
Alternating | |
} | |
private const int Seed = 98412310; | |
private const int Count = 32_768; | |
private float[] _dest; | |
public float[] Source1 { get; private set; } | |
public float[] Source2 { get; private set; } | |
[Params(SignType.Same, SignType.Different, SignType.Alternating, SignType.Random)] | |
public SignType Scenario { get; set; } | |
[GlobalSetup] | |
public void Setup() | |
{ | |
Source1 = new float[Count]; | |
Source2 = new float[Count]; | |
_dest = new float[Count]; | |
var rng = new Random(Seed); | |
for (int i = 0; i < Count; i++) | |
{ | |
Source1[i] = (float)rng.NextDouble(); | |
Source2[i] = (float)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.SingleToInt32Bits(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((uint)bits) % 2) == 0) | |
{ | |
Source2[i] = -Source2[i]; | |
} | |
} | |
} | |
} | |
[Benchmark] | |
public void Standard() | |
{ | |
for (var i = 0; i < Count; i++) | |
{ | |
float x = Source1[i]; | |
float y = Source2[i]; | |
// This method is required to work for all inputs, | |
// including NaN, so we operate on the raw bits. | |
int xbits = BitConverter.SingleToInt32Bits(x); | |
int ybits = BitConverter.SingleToInt32Bits(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.Int32BitsToSingle(xbits ^ int.MinValue); | |
} | |
_dest[i] = x; | |
} | |
} | |
[Benchmark] | |
public void John() | |
{ | |
for (var i = 0; i < Count; i++) | |
{ | |
float x = Source1[i]; | |
float y = Source2[i]; | |
// This method is required to work for all inputs, | |
// including NaN, so we operate on the raw bits. | |
int xbits = BitConverter.SingleToInt32Bits(x); | |
int ybits = BitConverter.SingleToInt32Bits(y); | |
// Remove the sign from x, and remove everything but the sign from y | |
const int signMask = unchecked((int)0b_1000_0000__0000_0000__0000_0000__0000_0000); | |
xbits &= ~signMask; | |
ybits &= signMask; | |
// Simply OR them to get the correct sign | |
x = BitConverter.Int32BitsToSingle(xbits | ybits); | |
_dest[i] = x; | |
} | |
} | |
[Benchmark] | |
public void John_Intrinsic() | |
{ | |
for (var i = 0; i < Count; i++) | |
{ | |
float x = Source1[i]; | |
float y = Source2[i]; | |
const int signMask = unchecked((int)0b_1000_0000__0000_0000__0000_0000__0000_0000); | |
if (Sse.IsSupported) | |
{ | |
var xvec = Vector128.CreateScalarUnsafe(x); | |
var yvec = Vector128.CreateScalarUnsafe(y); | |
xvec = Sse.And(xvec, Vector128.CreateScalarUnsafe(~signMask).AsSingle()); | |
yvec = Sse.And(yvec, Vector128.CreateScalarUnsafe(signMask).AsSingle()); | |
x = Sse.Or(xvec, yvec).ToScalar(); | |
} | |
else | |
{ | |
// This method is required to work for all inputs, | |
// including NaN, so we operate on the raw bits. | |
int xbits = BitConverter.SingleToInt32Bits(x); | |
int ybits = BitConverter.SingleToInt32Bits(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.Int32BitsToSingle(xbits | ybits); | |
} | |
_dest[i] = x; | |
} | |
} | |
} | |
[CoreJob] | |
public class CopySignDoubleBenchmark | |
{ | |
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] | |
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] | |
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] | |
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