为什么访问静态数组引用比访问非静态数组引用慢
本文关键字:引用 静态 数组 访问 为什么 | 更新日期: 2023-09-27 17:58:53
看看这个片段:
public class StringToggler
{
static readonly bool[] ToggleableLatinChars = new[]
{
// 256 bools here
};
readonly bool[] LocalToggleableLatinChars = ToggleableLatinChars;
public string Toggle(string s)
{
// blah blah
if (LocalToggleableLatinChars[(byte) ch])
{
// blah blah
}
// blah blah
}
// blah blah
}
这段代码在测试中明显比我直接使用ToggleableLatinChars更快(7%)。(在该方法中使用对ToggleableLatinChars的本地引用也同样快)。
只有在为.NET 4进行编译时才会注意到这种效果。当编译.NET 3.5时,我看到了相反的效果——使用静态数组明显更快。(我的机器是一台运行Windows7 64位的英特尔i5,正在为x86进行编译)
知道为什么吗?
更新:下面是一个完整的代码示例,它更类似于Marc的测试示例。注意,我现在使用的是静态和局部变量版本(不再是成员变量)。虽然我看到的差异比我在原始测试代码中看到的要小,但当为.NET4编译时,本地版本总是更快。你可以交换运行顺序,但Local总是为我赢得胜利。(编译.NET 3.5并不能做到这一点:它总体上比.NET 4快得多,静态要么更快,要么相同)
using System;
using System.Diagnostics;
using System.Globalization;
internal class Program
{
const int RepeatCount = 500000;
const string TestString1_Unicode = @"?=3.1415926?!! ?a??!#!%# ÜBERGRößEN!!?????? ??????@!e=2.71828182?#!!$@'^i^/!@$";
const string TestString2_Numbers = @"p=3.14159265358979323846264338327950288419716939937510....!!!!";
const string TestString3_LowerCase = @"nevr un-den-erstimate ze pauer of stoopid piplz in larg grupp!'*^*/";
const string TestString4_UpperCase = @"DUDE, WHY U R HERE?? U SHOULDA BE IN THE MEETING (BLAH-BLAH) $'*o*/$!";
static void Main()
{
RunTestsStaticAccess();
RunTestsLocalAccess();
Console.ReadLine();
}
public static void RunTestsLocalAccess()
{
StringToggler st = new StringToggler();
var watch = Stopwatch.StartNew();
for (int i = 0; i < RepeatCount; i++)
{
st.ToggleCase_LocalAccess(TestString1_Unicode);
st.ToggleCase_LocalAccess(TestString2_Numbers);
st.ToggleCase_LocalAccess(TestString3_LowerCase);
st.ToggleCase_LocalAccess(TestString4_UpperCase);
}
watch.Stop();
Console.WriteLine("{0}: {1}ms", "RunTestsLocalAccess", watch.ElapsedMilliseconds);
}
public static void RunTestsStaticAccess()
{
StringToggler st = new StringToggler();
var watch = Stopwatch.StartNew();
for (int i = 0; i < RepeatCount; i++)
{
st.ToggleCase_StaticAccess(TestString1_Unicode);
st.ToggleCase_StaticAccess(TestString2_Numbers);
st.ToggleCase_StaticAccess(TestString3_LowerCase);
st.ToggleCase_StaticAccess(TestString4_UpperCase);
}
watch.Stop();
Console.WriteLine("{0}: {1}ms", "RunTestsStaticAccess", watch.ElapsedMilliseconds);
}
public class StringToggler
{
static readonly bool[] ToggleableLatinChars = new[]
{
false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
true, true, true, true, true, true, true, true, true, true, true, false, false, false, false, false,
false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
true, true, true, true, true, true, true, true, true, true, true, false, false, false, false, false,
false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false,
true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
true, true, true, true, true, true, true, false, true, true, true, true, true, true, true, false,
true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true,
true, true, true, true, true, true, true, false, true, true, true, true, true, true, true, false
};
readonly TextInfo textInfo;
public StringToggler()
{
textInfo = CultureInfo.CurrentCulture.TextInfo;
}
public StringToggler(CultureInfo cultureInfo)
{
textInfo = cultureInfo.TextInfo;
}
public unsafe string ToggleCase_StaticAccess(string s)
{
s = string.Copy(s);
fixed(char* p = s)
{
for (int i = 0; i < s.Length; i++)
{
char ch = p[i];
if (ch <= 0xff)
{
if (ToggleableLatinChars[(byte) ch])
{
p[i] = (char) (ch ^ 0x20);
}
}
else
{
switch (CharUnicodeInfo.GetUnicodeCategory(ch))
{
case UnicodeCategory.UppercaseLetter:
p[i] = textInfo.ToLower(ch);
break;
case UnicodeCategory.LowercaseLetter:
p[i] = textInfo.ToUpper(ch);
break;
}
}
}
}
return s;
}
public unsafe string ToggleCase_LocalAccess(string s)
{
s = string.Copy(s);
var toggleableLatinChars = ToggleableLatinChars;
fixed(char* p = s)
{
for (int i = 0; i < s.Length; i++)
{
char ch = p[i];
if (ch <= 0xff)
{
if (toggleableLatinChars[(byte) ch])
{
p[i] = (char) (ch ^ 0x20);
}
}
else
{
switch (CharUnicodeInfo.GetUnicodeCategory(ch))
{
case UnicodeCategory.UppercaseLetter:
p[i] = textInfo.ToLower(ch);
break;
case UnicodeCategory.LowercaseLetter:
p[i] = textInfo.ToUpper(ch);
break;
}
}
}
}
return s;
}
}
}
简单地说:不是。我不相信你(未提供)的测试:
我的结果:
InstanceField: 6035ms
LocalVariable: 5373ms
StaticFieldStaticInitializer: 5364ms
StaticFieldNoInitializer: 5388ms
这与我对额外的ldarg0和ldfld(从实例字段获取值)的期望相关联,而不是更简单的ldsfld(从静态字段获得值)或ldloc0(从局部变量获得值)。
我的代码:
class Program
{
static void Main()
{
new InstanceField().RunTests();
new LocalVariable().RunTests();
new StaticFieldStaticInitializer().RunTests();
new StaticFieldNoInitializer().RunTests();
Console.ReadLine();
}
class InstanceField
{
public bool[] arr= new bool[1024];
public void RunTests()
{
var watch = Stopwatch.StartNew();
int count = 0;
for (int i = 0; i < 500000; i++)
{
for (int j = 0; j < arr.Length; j++)
{
if (arr[j]) count++;
}
}
watch.Stop();
Console.WriteLine("{0}: {1}ms", GetType().Name, watch.ElapsedMilliseconds);
}
}
class LocalVariable
{
public void RunTests()
{
bool[] arr = new bool[1024];
var watch = Stopwatch.StartNew();
int count = 0;
for (int i = 0; i < 500000; i++)
{
for (int j = 0; j < arr.Length; j++)
{
if (arr[j]) count++;
}
}
watch.Stop();
Console.WriteLine("{0}: {1}ms", GetType().Name, watch.ElapsedMilliseconds);
}
}
class StaticFieldStaticInitializer
{
public static bool[] arr = new bool[1024];
public void RunTests()
{
var watch = Stopwatch.StartNew();
int count = 0;
for (int i = 0; i < 500000; i++)
{
for (int j = 0; j < arr.Length; j++)
{
if (arr[j]) count++;
}
}
watch.Stop();
Console.WriteLine("{0}: {1}ms", GetType().Name, watch.ElapsedMilliseconds);
}
}
class StaticFieldNoInitializer
{
public static bool[] arr;
public void RunTests()
{
arr = new bool[1024];
var watch = Stopwatch.StartNew();
int count = 0;
for (int i = 0; i < 500000; i++)
{
for (int j = 0; j < arr.Length; j++)
{
if (arr[j]) count++;
}
}
watch.Stop();
Console.WriteLine("{0}: {1}ms", GetType().Name, watch.ElapsedMilliseconds);
}
}
}