Reputation: 69
I have an array of bytes
byte[0]=00;
byte[1]=01;
byte[2]=02;
byte[3]=03;
...
and I want to get the four bytes in order ex)00010203 and convert them to an int32 array, so int[0] would be 66051
int[] result = new int[byteArray.Length / 4];
Buffer.BlockCopy(byteArray, 0, result, 0, byteArray.Length);
I've tried using the code above but the results come out way different.
Upvotes: 1
Views: 258
Reputation: 81493
Although johns answer is correct, its extremely slow
This uses BufferCopy
and a bitwise swap and is many many factors faster
Note i had to remove johns as it took away to long to test
----------------------------------------------------------------------------
Mode : Release (64Bit)
Test Framework : .NET Framework 4.7.1 (CLR 4.0.30319.42000)
----------------------------------------------------------------------------
Operating System : Microsoft Windows 10 Pro
Version : 10.0.17134
----------------------------------------------------------------------------
CPU Name : Intel(R) Core(TM) i7-3770K CPU @ 3.50GHz
Description : Intel64 Family 6 Model 58 Stepping 9
Cores (Threads) : 4 (8) : Architecture : x64
Clock Speed : 3901 MHz : Bus Speed : 100 MHz
L2Cache : 1 MB : L3Cache : 8 MB
----------------------------------------------------------------------------
Total Benchmarks : Inputs (1) * Scales (5) * Benchmarks (3) * Runs (100) = 1,500
Test 1
--- Standard input ------------------------------------------------------
| Value | Average | Fastest | Cycles | Garbage | Test | Gain |
--- Scale 100 -------------------------------------------- Time 0.164 ---
| Mine2 | 0.012 ms | 0.004 ms | 43.913 K | 8.000 KB | N/A | 55.16 % |
| MineR | 0.014 ms | 0.004 ms | 52.617 K | 8.000 KB | N/A | 46.35 % |
| Mine | 0.026 ms | 0.006 ms | 94.853 K | 7.461 KB | Base | 0.00 % |
--- Scale 1,000 ------------------------------------------ Time 0.114 ---
| Mine2 | 0.005 ms | 0.004 ms | 19.006 K | 8.000 KB | N/A | 48.82 % |
| MineR | 0.005 ms | 0.004 ms | 19.763 K | 8.000 KB | N/A | 46.95 % |
| Mine | 0.009 ms | 0.008 ms | 34.456 K | 8.000 KB | Base | 0.00 % |
--- Scale 10,000 ----------------------------------------- Time 0.126 ---
| Mine2 | 0.009 ms | 0.008 ms | 33.715 K | 17.813 KB | N/A | 72.23 % |
| MineR | 0.016 ms | 0.014 ms | 59.171 K | 17.813 KB | N/A | 49.12 % |
| Mine | 0.032 ms | 0.030 ms | 113.567 K | 59.719 KB | Base | 0.00 % |
--- Scale 100,000 ---------------------------------------- Time 0.180 ---
| Mine2 | 0.053 ms | 0.051 ms | 188.890 K | 105.680 KB | N/A | 83.65 % |
| MineR | 0.109 ms | 0.103 ms | 384.946 K | 105.680 KB | N/A | 66.26 % |
| Mine | 0.323 ms | 0.277 ms | 1.135 M | 459.633 KB | Base | 0.00 % |
--- Scale 1,000,000 -------------------------------------- Time 0.709 ---
| Mine2 | 0.509 ms | 0.485 ms | 1.784 M | 984.586 KB | N/A | 86.00 % |
| MineR | 1.155 ms | 1.049 ms | 4.048 M | 984.586 KB | N/A | 68.22 % |
| Mine | 3.636 ms | 3.269 ms | 12.711 M | 3.916 MB | Base | 0.00 % |
-------------------------------------------------------------------------
Mine
public static uint SwapBytes(uint x)
{
// swap adjacent 16-bit blocks
x = (x >> 16) | (x << 16);
// swap adjacent 8-bit blocks
return ((x & 0xFF00FF00) >> 8) | ((x & 0x00FF00FF) << 8);
}
[Test("Mine", "", true)]
public int[] Test1(byte[] input, int scale)
{
var result = new int[input.Length / 4];
Buffer.BlockCopy(input, 0, result, 0, input.Length);
return result.Select(x => (int)SwapBytes((uint)x))
.ToArray();
}
Mine2
[Test("Mine2", "", false)]
public unsafe int[] Test2(byte[] input, int scale)
{
var result = new int[input.Length / 4];
Buffer.BlockCopy(input, 0, result, 0, input.Length);
fixed (int* pResult = result)
{
var len = pResult + result.Length;
for (var p = pResult; p < len; p++)
{
var x = (*p >> 16) | (*p << 16);
*p = (int)(((x & 0xFF00FF00) >> 8) | ((x & 0x00FF00FF) << 8));
}
}
return result;
}
john
[Test("john", "", false)]
public unsafe int[] Test4(byte[] input, int scale)
{
var result = new int[input.Length / 4];
for (int i = 0; i < result.Length; ++i)
{
var srcBytes = input.Skip(i * 4).Take(4);
if (System.BitConverter.IsLittleEndian)
{
srcBytes = srcBytes.Reverse();
}
result[i] = System.BitConverter.ToInt32(srcBytes.ToArray(), 0);
}
return result;
}
MineR
[Test("MineR", "", false)]
public int[] Test3(byte[] input, int scale)
{
int numBytes = input.Length;
int numInts = numBytes / 4;
if (numBytes / 4d != numInts)
throw new Exception();
if (numInts == 0)
return new int[0];
var ints = new int[numInts];
unsafe
{
fixed (byte* pBytes = input)
fixed (int* pInts = ints)
{
byte* rawInt = (byte*)pInts;
for (int i = 0; i < numBytes; i += 4)
{
for (int j = 0; j < 4; j++)
{
rawInt[i + j] = pBytes[3 - j + i];
}
}
}
}
return ints;
}
public static class MyExtension
{
public static uint SwapBytes(uint x)
{
// swap adjacent 16-bit blocks
x = (x >> 16) | (x << 16);
// swap adjacent 8-bit blocks
return ((x & 0xFF00FF00) >> 8) | ((x & 0x00FF00FF) << 8);
}
public static int[] GetInts(this byte[] source)
{
var result = new int[source.Length / 4];
Buffer.BlockCopy(source, 0, result, 0, source.Length);
return result.Select(x => (int)SwapBytes((uint)x)).ToArray();
}
}
Usage
var byteArray = new byte[4*100000];
var ints = byteArray.GetInts();
Update
This is about 10 times faster again, using fixed and unsafe
public unsafe static int[] GetInts3(this byte[] source)
{
var result = new int[source.Length / 4];
Buffer.BlockCopy(source, 0, result, 0, source.Length);
fixed (int* pResult = result)
{
var len = pResult + result.Length;
for (var p = pResult; p < len; p++)
{
var x = (*p >> 16) | (*p << 16);
*p = (int)(((x & 0xFF00FF00) >> 8) | ((x & 0x00FF00FF) << 8));
}
}
return result;
}
Upvotes: 3
Reputation: 2194
As an unsafe alternative, you can try this:
public static int[] GetIntsUnsafe(byte[] bytes)
{
int numBytes = bytes.Length;
int numInts = numBytes / 4;
if (numBytes / 4d != numInts) throw new Exception();
if (numInts == 0) return new int[0];
var ints = new int[numInts];
unsafe
{
fixed (byte* pBytes = bytes)
fixed (int* pInts = ints)
{
byte* rawInt = (byte*)pInts;
for (int i = 0; i < numBytes; i += 4)
{
for (int j = 0; j < 4; j++)
{
rawInt[i + j] = pBytes[3 - j + i];
}
}
}
}
return ints;
}
I performance tested the three answers (at time of writing), in Release, AnyCPU, no debugger attached to get:
Unsafe Small: 22
Unsafe Large: 5
John Small: 816
John Large: 23628
General Small: 49 // 266 with Linq ToArray
General Large 4 // 29 with Linq ToArray
The testing code:
Random r = new Random(0);
var byteSmall = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x03, 0x05, 0x07, 0x23 };
var byteLarge = new byte[10000];
for (int i = 0; i < byteLarge.Length; i++) byteLarge[i] = (byte)r.Next(255);
int countSmall = 2_000_000;
int countLarge = 1_000;
Stopwatch swUnsafeSmall = Stopwatch.StartNew();
for (int i = 0; i < countSmall; i++) GetIntsUnsafe(byteSmall);
swUnsafeSmall.Stop();
Console.WriteLine("Unsafe Small: " + swUnsafeSmall.ElapsedMilliseconds);
Stopwatch swUnsafeLarge = Stopwatch.StartNew();
for (int i = 0; i < countLarge; i++) GetIntsUnsafe(byteLarge);
swUnsafeLarge.Stop();
Console.WriteLine("Unsafe Large: " + swUnsafeLarge.ElapsedMilliseconds);
Stopwatch swJohnSmall = Stopwatch.StartNew();
for(int i = 0; i < countSmall;i++)
GetIntsJohn(byteSmall);
swJohnSmall.Stop();
Console.WriteLine("John Small: " + swJohnSmall.ElapsedMilliseconds);
Stopwatch swJohnLarge = Stopwatch.StartNew();
for(int i = 0; i < countLarge;i++)
GetIntsJohn(byteLarge);
swJohnLarge.Stop();
Console.WriteLine("John Large: " + swJohnLarge.ElapsedMilliseconds);
Stopwatch swGeneralSmall = Stopwatch.StartNew();
for(int i = 0; i < countSmall;i++)
GetIntsGeneral(byteSmall);
swGeneralSmall.Stop();
Console.WriteLine("General Small: " + swGeneralSmall.ElapsedMilliseconds);
Stopwatch swGeneralLarge = Stopwatch.StartNew();
for(int i = 0; i < countLarge;i++)
GetIntsGeneral(byteLarge);
swGeneralLarge.Stop();
Console.WriteLine("General Large: " + swGeneralLarge.ElapsedMilliseconds);
Console.ReadLine();
And my version's of John and General's code. (I edited TheGeneral's code to not copy the array):
public static int[] GetIntsJohn(byte[] byteArray)
{
int[] result = new int[byteArray.Length / 4];
for (int i = 0; i < result.Length; ++i)
{
var srcBytes = byteArray.Skip(i * 4).Take(4);
if (BitConverter.IsLittleEndian)
{
srcBytes = srcBytes.Reverse();
}
result[i] = BitConverter.ToInt32(srcBytes.ToArray(), 0);
}
return result;
}
public static uint SwapBytes(uint x)
{
// swap adjacent 16-bit blocks
x = (x >> 16) | (x << 16);
// swap adjacent 8-bit blocks
return ((x & 0xFF00FF00) >> 8) | ((x & 0x00FF00FF) << 8);
}
public static int[] GetIntsGeneral(byte[] source)
{
var result = new int[source.Length / 4];
Buffer.BlockCopy(source, 0, result, 0, source.Length);
for(int i= 0;i<result.Length;i++)
{
result[i] = (int) SwapBytes((uint) result[i]);
}
return result;
}
Upvotes: 3
Reputation: 38757
I think you want BitConverter.ToInt32()
:
var byteArray = new byte[] { 0x00, 0x01, 0x02, 0x03 };
int[] result = new int[byteArray.Length / 4];
for (int i = 0; i < result.Length; ++i)
{
var srcBytes = byteArray.Skip(i * 4).Take(4);
if (BitConverter.IsLittleEndian)
{
srcBytes = srcBytes.Reverse();
}
result[i] = BitConverter.ToInt32(srcBytes.ToArray(), 0);
}
Upvotes: 3