Created
November 21, 2019 17:20
-
-
Save Const-me/5434ced8c37f91af56f358871d92bef5 to your computer and use it in GitHub Desktop.
.NET Core SSE Test, with static readonly constants
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
using System; | |
using System.Diagnostics; | |
using System.Runtime.Intrinsics; | |
using System.Runtime.Intrinsics.X86; | |
namespace SimdBrightness | |
{ | |
static class Program | |
{ | |
/// <summary>Load 4 pixels of RGB</summary> | |
static unsafe Vector128<int> load4( byte* src ) | |
{ | |
return Sse2.LoadVector128( (int*)src ); | |
} | |
static readonly Vector128<int> lowByte = Vector128.Create( 0xFF ); | |
/// <summary>Pack red channel of 8 pixels into ushort values in [ 0xFF00 .. 0 ] interval</summary> | |
static Vector128<ushort> packRed( Vector128<int> a, Vector128<int> b ) | |
{ | |
Vector128<int> mask = lowByte; | |
a = Sse2.And( a, mask ); | |
b = Sse2.And( b, mask ); | |
return Sse2.ShiftLeftLogical128BitLane( Sse41.PackUnsignedSaturate( a, b ), 1 ); | |
} | |
static readonly Vector128<int> secondByte = Vector128.Create( 0xFF00 ); | |
/// <summary>Pack green channel of 8 pixels into ushort values in [ 0xFF00 .. 0 ] interval</summary> | |
static Vector128<ushort> packGreen( Vector128<int> a, Vector128<int> b ) | |
{ | |
Vector128<int> mask = secondByte; | |
a = Sse2.And( a, mask ); | |
b = Sse2.And( b, mask ); | |
return Sse41.PackUnsignedSaturate( a, b ); | |
} | |
/// <summary>Pack blue channel of 8 pixels into ushort values in [ 0xFF00 .. 0 ] interval</summary> | |
static Vector128<ushort> packBlue( Vector128<int> a, Vector128<int> b ) | |
{ | |
a = Sse2.ShiftRightLogical128BitLane( a, 1 ); | |
b = Sse2.ShiftRightLogical128BitLane( b, 1 ); | |
Vector128<int> mask = secondByte; | |
a = Sse2.And( a, mask ); | |
b = Sse2.And( b, mask ); | |
return Sse41.PackUnsignedSaturate( a, b ); | |
} | |
/// <summary>Load 8 pixels, split into RGB channels.</summary> | |
static unsafe void loadRgb( byte* src, out Vector128<ushort> red, out Vector128<ushort> green, out Vector128<ushort> blue ) | |
{ | |
var a = load4( src ); | |
var b = load4( src + 16 ); | |
red = packRed( a, b ); | |
green = packGreen( a, b ); | |
blue = packBlue( a, b ); | |
} | |
const ushort mulRed = (ushort)( 0.29891 * 0x10000 ); | |
const ushort mulGreen = (ushort)( 0.58661 * 0x10000 ); | |
const ushort mulBlue = (ushort)( 0.11448 * 0x10000 ); | |
static readonly Vector128<ushort> vecRed = Vector128.Create( mulRed ); | |
static readonly Vector128<ushort> vecGreen = Vector128.Create( mulGreen ); | |
static readonly Vector128<ushort> vecBlue = Vector128.Create( mulBlue ); | |
/// <summary>Compute brightness of 8 pixels</summary> | |
static Vector128<short> brightness( Vector128<ushort> r, Vector128<ushort> g, Vector128<ushort> b ) | |
{ | |
r = Sse2.MultiplyHigh( r, vecRed ); | |
g = Sse2.MultiplyHigh( g, vecGreen ); | |
b = Sse2.MultiplyHigh( b, vecBlue ); | |
var result = Sse2.AddSaturate( Sse2.AddSaturate( r, g ), b ); | |
return Vector128.AsInt16( Sse2.ShiftRightLogical( result, 8 ) ); | |
} | |
/// <summary>Convert buffer from RGBA to grayscale.</summary> | |
/// <remarks> | |
/// <para>If your image has line paddings, you'll want to call this once per line, not for the complete image.</para> | |
/// <para>If width of the image is not multiple of 16 pixels, you'll need to do more work to handle the last few pixels of every line.</para> | |
/// </remarks> | |
static unsafe void convertToGrayscale( byte* src, byte* dst, int count ) | |
{ | |
byte* srcEnd = src + count * 4; | |
while( src < srcEnd ) | |
{ | |
loadRgb( src, out var r, out var g, out var b ); | |
var low = brightness( r, g, b ); | |
loadRgb( src + 32, out r, out g, out b ); | |
var hi = brightness( r, g, b ); | |
var bytes = Sse2.PackUnsignedSaturate( low, hi ); | |
Sse2.Store( dst, bytes ); | |
src += 64; | |
dst += 16; | |
} | |
} | |
const int count = 1024 * 1024; | |
static unsafe void Main( string[] args ) | |
{ | |
byte[] source = new byte[ 4 * count ]; | |
new Random( 11 ).NextBytes( source ); | |
byte[] dest = new byte[ count ]; | |
Stopwatch sw = Stopwatch.StartNew(); | |
fixed( byte* pSource = source ) | |
fixed( byte* pDest = dest ) | |
convertToGrayscale( pSource, pDest, source.Length / 4 ); | |
sw.Stop(); | |
Console.WriteLine( "{0} ms", sw.Elapsed.TotalMilliseconds ); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment