编辑:我意识到我的答案没有完全回答问题,虽然没有真正的缺点,但从我的计时结果来看,也没有真正的缺点。内联属性获取器之间的差异在5亿次迭代中为0.002秒。由于使用结构,我的测试用例也可能不是100%准确,因为对结构的抖动和内联有一些警告。
与往常一样,真正知道的唯一方法是编写测试并弄清楚。这是使用以下配置的结果:
Windows 7 Home
8GB ram
64bit os
i5-2300 2.8ghz
具有以下设置的空项目:
.NET 4.5
Release mode
Start without debugger attached - CRUCIAL
Unchecked "Prefer 32-bit" under project build settings
结果
struct get property : 0.3097832 seconds
struct inline get property : 0.3079076 seconds
struct method call with params : 1.0925033 seconds
struct inline method call with params : 1.0930666 seconds
struct method call without params : 1.5211852 seconds
struct intline method call without params : 1.2235001 seconds
用以下代码测试:
class Program
{
const int SAMPLES = 5;
const int ITERATIONS = 100000;
const int DATASIZE = 1000;
static Random random = new Random();
static Stopwatch timer = new Stopwatch();
static Dictionary<string, TimeSpan> timings = new Dictionary<string, TimeSpan>();
class SimpleTimer : IDisposable
{
private string name;
public SimpleTimer(string name)
{
this.name = name;
timer.Restart();
}
public void Dispose()
{
timer.Stop();
TimeSpan ts = TimeSpan.Zero;
if (timings.ContainsKey(name))
ts = timings[name];
ts += timer.Elapsed;
timings[name] = ts;
}
}
[StructLayout(LayoutKind.Sequential, Size = 4)]
struct TestStruct
{
private int x;
public int X { get { return x; } set { x = value; } }
}
[StructLayout(LayoutKind.Sequential, Size = 4)]
struct TestStruct2
{
private int x;
public int X
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return x; }
set { x = value; }
}
}
[StructLayout(LayoutKind.Sequential, Size = 8)]
struct TestStruct3
{
private int x;
private int y;
public void Update(int _x, int _y)
{
x += _x;
y += _y;
}
}
[StructLayout(LayoutKind.Sequential, Size = 8)]
struct TestStruct4
{
private int x;
private int y;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Update(int _x, int _y)
{
x += _x;
y += _y;
}
}
[StructLayout(LayoutKind.Sequential, Size = 8)]
struct TestStruct5
{
private int x;
private int y;
public void Update()
{
x *= x;
y *= y;
}
}
[StructLayout(LayoutKind.Sequential, Size = 8)]
struct TestStruct6
{
private int x;
private int y;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Update()
{
x *= x;
y *= y;
}
}
static void RunTests()
{
for (var i = 0; i < SAMPLES; ++i)
{
Console.Write("Sample {0} ... ", i);
RunTest1();
RunTest2();
RunTest3();
RunTest4();
RunTest5();
RunTest6();
Console.WriteLine(" complate");
}
}
static int RunTest1()
{
var data = new TestStruct[DATASIZE];
var temp = 0;
unchecked
{
//init the data, just so jitter can't make assumptions
for (var j = 0; j < DATASIZE; ++j)
data[j].X = random.Next();
using (new SimpleTimer("struct get property"))
{
for (var j = 0; j < DATASIZE; ++j)
{
for (var i = 0; i < ITERATIONS; ++i)
{
//use some math to make sure its not optimized out (aka don't use an incrementor)
temp += data[j].X;
}
}
}
}
//again need variables to cross scopes to make sure the jitter doesn't do crazy optimizations
return temp;
}
static int RunTest2()
{
var data = new TestStruct2[DATASIZE];
var temp = 0;
unchecked
{
//init the data, just so jitter can't make assumptions
for (var j = 0; j < DATASIZE; ++j)
data[j].X = random.Next();
using (new SimpleTimer("struct inline get property"))
{
for (var j = 0; j < DATASIZE; ++j)
{
for (var i = 0; i < ITERATIONS; ++i)
{
//use some math to make sure its not optimized out (aka don't use an incrementor)
temp += data[j].X;
}
}
}
}
//again need variables to cross scopes to make sure the jitter doesn't do crazy optimizations
return temp;
}
static void RunTest3()
{
var data = new TestStruct3[DATASIZE];
unchecked
{
using (new SimpleTimer("struct method call with params"))
{
for (var j = 0; j < DATASIZE; ++j)
{
for (var i = 0; i < ITERATIONS; ++i)
{
//use some math to make sure its not optimized out (aka don't use an incrementor)
data[j].Update(j, i);
}
}
}
}
}
static void RunTest4()
{
var data = new TestStruct4[DATASIZE];
unchecked
{
using (new SimpleTimer("struct inline method call with params"))
{
for (var j = 0; j < DATASIZE; ++j)
{
for (var i = 0; i < ITERATIONS; ++i)
{
//use some math to make sure its not optimized out (aka don't use an incrementor)
data[j].Update(j, i);
}
}
}
}
}
static void RunTest5()
{
var data = new TestStruct5[DATASIZE];
unchecked
{
using (new SimpleTimer("struct method call without params"))
{
for (var j = 0; j < DATASIZE; ++j)
{
for (var i = 0; i < ITERATIONS; ++i)
{
//use some math to make sure its not optimized out (aka don't use an incrementor)
data[j].Update();
}
}
}
}
}
static void RunTest6()
{
var data = new TestStruct6[DATASIZE];
unchecked
{
using (new SimpleTimer("struct intline method call without params"))
{
for (var j = 0; j < DATASIZE; ++j)
{
for (var i = 0; i < ITERATIONS; ++i)
{
//use some math to make sure its not optimized out (aka don't use an incrementor)
data[j].Update();
}
}
}
}
}
static void Main(string[] args)
{
RunTests();
DumpResults();
Console.Read();
}
static void DumpResults()
{
foreach (var kvp in timings)
{
Console.WriteLine("{0,-50}: {1} seconds", kvp.Key, kvp.Value.TotalSeconds);
}
}
}