我试图分析一个功能。该函数用于将结构转换为数组。我有两种不同的方法,使用编组或BitConverter。当然,编组方法使得单个函数可以在给定某些条件下几乎适用于所有结构。 BitConverter需要每个结构的自定义函数。我最初的想法是BitConverter会更快但我的测试结果不一致。
以下是基准测试的剪切和粘贴。
我已经尝试过多种不同形式的基准测试。 当我首先对BitConverter函数进行基准测试时,它往往会更快。 当我首先对Marshaling函数进行基准测试时,它往往会更快。 我错过了什么?
摘要显示流程。这不是基准流程的实际代码。
main()
{
Stopwatch watch = new Stopwatch;
// To take care of JIT
bitConverterFunction();
marshalingFunction();
//Thread.Sleep(0); // I've tried this thinking it had to do with context switching issues but the results were basically the same.
watch.Start();
for(i=0; i<iterations; i++)
{
bitConverterFunction();
}
watch.Stop();
Timespan bitConverterTime = watch.Elapsed;
//Thread.Sleep(0); // I've tried this thinking it had to do with context switching issues
watch.Restart();
for(i=0; i<iterations; i++)
{
marshalingFunction();
}
watch.Stop();
Timespan marshalingTime = watch.Elapsed;
// it seems that whichever function is run first, tends to be the quickest.
}
如果你想测试真实代码
using System;
using BenchmarkTool;
namespace BenchmarkConsole
{
class Program
{
static void Main(string[] args)
{
Benchmarks.StructToArrayConversion(100);
Benchmarks.StructToArrayConversion(1000);
Benchmarks.StructToArrayConversion(10000);
Benchmarks.StructToArrayConversion(100000);
Console.WriteLine("Press any key to continue.");
Console.ReadKey();
}
}
}
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using NUnit.Framework;
namespace BenchmarkTool
{
[TestFixture]
public static class Benchmarks
{
[TestCase(100)]
[TestCase(1000)]
[TestCase(10000)]
[TestCase(100000)]
[TestCase(1000000)]
public static void StructToArrayConversion(int iteration = 100)
{
Stopwatch watch = new Stopwatch();
EntityStatePDU6 state = new EntityStatePDU6()
{
Version = 0,
ExerciseID = 0x01,
PDUType = 0x02,
Family = 0x03,
Timestamp = 0x07060504,
Length = 0x0908,
Site = 0x0D0C,
Application = 0X0F0E,
Entity = 0X1110,
NumArticulationParams = 0X13,
VelocityX = BitConverter.ToSingle(new byte[] {0x14, 0x15, 0x16, 0x17}, 0),
VelocityY = BitConverter.ToSingle(new byte[] {0x18, 0x19, 0x1A, 0x1B}, 0),
VelocityZ = BitConverter.ToSingle(new byte[] {0x1C, 0x1D, 0x1E, 0x1F}, 0),
LocationX = BitConverter.ToSingle(new byte[] {0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27}, 0),
LocationY = BitConverter.ToSingle(new byte[] {0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F}, 0),
LocationZ = BitConverter.ToSingle(new byte[] {0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37}, 0),
Roll = BitConverter.ToSingle(new byte[] {0x38, 0x39, 0x3A, 0x3B}, 0),
Pitch = BitConverter.ToSingle(new byte[] {0x3C, 0x3D, 0x3E, 0x3F}, 0),
Heading = BitConverter.ToSingle(new byte[] {0x40, 0x41, 0x42, 0x43}, 0),
Appearance = 0X47464544
};
// To take care of JIT
ToArrayBitConverter(state);
state.ToByteArray();
Console.WriteLine("*** Benchmark Start ***");
Console.WriteLine("BitConverter Benchmark");
byte[] bitconverterArray = ToArrayBitConverter(state);
//Thread.Sleep(0);
watch.Start();
for(int i = 0; i < iteration; i++)
{
bitconverterArray = ToArrayBitConverter(state);
}
watch.Stop();
TimeSpan bitConverterTime = watch.Elapsed;
Console.WriteLine("{0} Iterations: {1}", iteration, watch.Elapsed.TotalSeconds.ToString("0.0000000"));
Console.WriteLine();
Console.WriteLine("Marshal StructToPtr Benchmark");
byte[] marshalArray = null;
//Thread.Sleep(0);
watch.Restart();
for (int i = 0; i < iteration; i++)
{
marshalArray = state.ToByteArray();
}
watch.Stop();
TimeSpan marshalTime = watch.Elapsed;
Console.WriteLine("{0} Iterations: {1}", iteration, watch.Elapsed.TotalSeconds.ToString("0.0000000"));
Console.WriteLine();
Console.WriteLine("Results");
Console.WriteLine("{0} Faster", marshalTime < bitConverterTime ? "Marshaling" : "BitConverter");
Console.WriteLine("Speed Ratio: {0}", marshalTime < bitConverterTime ? bitConverterTime.TotalSeconds / marshalTime.TotalSeconds : marshalTime.TotalSeconds / bitConverterTime.TotalSeconds);
Console.WriteLine("**********************************");
Console.WriteLine();
Assert.AreEqual(bitconverterArray.Length, marshalArray.Length);
for(int i = 0; i < bitconverterArray.Length; i++)
{
Assert.AreEqual(marshalArray[i],bitconverterArray[i], "@index " + i);
}
}
public static byte[] ToArrayBitConverter(EntityStatePDU6 entity)
{
int size = Marshal.SizeOf(typeof (EntityStatePDU6));
byte[] array = new byte[size];
array[0] = entity.Version;
array[1] = entity.ExerciseID;
array[2] = entity.PDUType;
array[3] = entity.Family;
array[4] = (byte)((0xFF & entity.Timestamp));
array[5] = (byte)((0xFF00 & entity.Timestamp) >> 8);
array[6] = (byte)((0xFF0000 & entity.Timestamp) >> 16);
array[7] = (byte)((0xFF000000 & entity.Timestamp) >> 24);
array[8] = (byte)((0xFF & entity.Length));
array[9] = (byte)((0xFF00 & entity.Length) >> 8);
// Padding1: array[10], array[11]
array[12] = (byte)((0xFF & entity.Site));
array[13] = (byte)((0xFF00 & entity.Site) >> 8);
array[14] = (byte)((0xFF & entity.Application));
array[15] = (byte)((0xFF00 & entity.Application) >> 8);
array[16] = (byte)((0xFF & entity.Entity));
array[17] = (byte)((0xFF00 & entity.Entity) >> 8);
//padding2 array[18]
array[19] = entity.NumArticulationParams;
byte[] bytes = BitConverter.GetBytes(entity.VelocityX);
array[20] = bytes[0];
array[21] = bytes[1];
array[22] = bytes[2];
array[23] = bytes[3];
bytes = BitConverter.GetBytes(entity.VelocityY);
array[24] = bytes[0];
array[25] = bytes[1];
array[26] = bytes[2];
array[27] = bytes[3];
bytes = BitConverter.GetBytes(entity.VelocityZ);
array[28] = bytes[0];
array[29] = bytes[1];
array[30] = bytes[2];
array[31] = bytes[3];
bytes = BitConverter.GetBytes(entity.LocationX);
array[32] = bytes[0];
array[33] = bytes[1];
array[34] = bytes[2];
array[35] = bytes[3];
array[36] = bytes[4];
array[37] = bytes[5];
array[38] = bytes[6];
array[39] = bytes[7];
bytes = BitConverter.GetBytes(entity.LocationY);
array[40] = bytes[0];
array[41] = bytes[1];
array[42] = bytes[2];
array[43] = bytes[3];
array[44] = bytes[4];
array[45] = bytes[5];
array[46] = bytes[6];
array[47] = bytes[7];
bytes = BitConverter.GetBytes(entity.LocationZ);
array[48] = bytes[0];
array[49] = bytes[1];
array[50] = bytes[2];
array[51] = bytes[3];
array[52] = bytes[4];
array[53] = bytes[5];
array[54] = bytes[6];
array[55] = bytes[7];
bytes = BitConverter.GetBytes(entity.Roll);
array[56] = bytes[0];
array[57] = bytes[1];
array[58] = bytes[2];
array[59] = bytes[3];
bytes = BitConverter.GetBytes(entity.Pitch);
array[60] = bytes[0];
array[61] = bytes[1];
array[62] = bytes[2];
array[63] = bytes[3];
bytes = BitConverter.GetBytes(entity.Heading);
array[64] = bytes[0];
array[65] = bytes[1];
array[66] = bytes[2];
array[67] = bytes[3];
array[68] = (byte)((0xFF & entity.Appearance));
array[69] = (byte)((0xFF00 & entity.Appearance) >> 8);
array[70] = (byte)((0xFF0000 & entity.Appearance) >> 16);
array[71] = (byte)((0xFF000000 & entity.Appearance) >> 24);
return array;
}
public static Byte[] ToByteArray<T>(this T obj) where T : struct
{
int size = Marshal.SizeOf(obj);
var arr = new byte[size];
IntPtr ptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(obj, ptr, false);
Marshal.Copy(ptr, arr, 0, size);
Marshal.FreeHGlobal(ptr);
return arr;
}
}
public struct EntityStatePDU6
{
// PDU Header 12 Bytes
public byte Version;
public byte ExerciseID;
public byte PDUType;
public byte Family;
public uint Timestamp;
public ushort Length;
public ushort Padding1;
// Entity ID 6 bytes
public ushort Site;
public ushort Application;
public ushort Entity;
public byte Padding2;
public byte NumArticulationParams;
public float VelocityX;
public float VelocityY;
public float VelocityZ;
public double LocationX;
public double LocationY;
public double LocationZ;
public float Roll;
public float Pitch;
public float Heading;
public uint Appearance;
}
}
答案 0 :(得分:1)
100000以下的任何情况都太小而无法获得一致的结果。
即使在相同代码的运行之间(> 2x时序差异),结果也非常不一致。这让我觉得生成了大量的垃圾,结果主要是垃圾收集开始和垃圾收集器的性能。
我在停止秒表后添加了一些GC.Collect
次调用,这使得结果更加一致(运行之间的差异为+/- 10%)。对于100000和1000000次迭代,Marshaling的速度通常为1.5-2倍。这是针对Release | x86编译的Mono 2.10.8.1,因此您的里程可能会有所不同。