[英]Performance of reflection method call vs delegate call
我的目標是編寫一個弱類型的 TryParse
方法,該方法基本上支持所有可用的struct類型(int,long,float ...)
public static bool TryParse(Type type, string s, out object obj)
該實現將調用所提供type
參數的TryParse
方法(如果type為int
,則將調用int.TryPase
並將out值作為對象返回)。
我通過反射實現了它,但是有一個主要的性能損失(正如我預期的那樣)
反思實施:
public static class ParserHelper
{
public delegate bool TryParseDl(string str, out object obj);
private static readonly HashSet<Type> ParsableStructs = new HashSet<Type>
{
typeof(int),
typeof(uint),
typeof(decimal),
typeof(short),
typeof(ushort),
typeof(double),
typeof(long),
typeof(ulong),
typeof(float),
typeof(byte),
typeof(sbyte)
};
public static readonly ReadOnlyDictionary<Type, TryParseDl> StructParsers;
static ParserHelper()
{
StructParsers = new ReadOnlyDictionary<Type, TryParseDl>(CreateParsersForStructs());
}
/// Creates parsers for structs
private static Dictionary<Type, TryParseDl> CreateParsersForStructs()
{
var parsers = new Dictionary<Type, TryParseDl>();
foreach (var t in ParsableStructs)
{
parsers[t] = GetParserForStruct(t);
}
return parsers;
}
private static TryParseDl GetParserForStruct(Type targetType)
{
var methodInfo = targetType.GetMethod(
"TryParse",
BindingFlags.Public | BindingFlags.Static,
Type.DefaultBinder,
new[] { typeof(string), targetType.MakeByRefType() },
null);
return (string str, out object obj) =>
{
if (string.IsNullOrEmpty(str))
{
obj = targetType.IsValueType ? Activator.CreateInstance(targetType) : null;
return true;
}
var inputParameters = new object[] { str, null };
var tryParseResult = (bool)methodInfo.Invoke(null, inputParameters);
obj = inputParameters[1];
return tryParseResult;
};
}
}
這是性能測試:
public class Program
{
public static void Main()
{
Stopwatch s = new Stopwatch();
string str = "100";
s.Start();
for(int j = 0;j<100;j++)
{
int i;
int.TryParse(str,out i);
}
s.Stop();
Console.WriteLine(s.Elapsed);
s.Reset();
s.Start();
var parser = ParserHelper.StructParsers[typeof(int)];
for(int j = 0;j<100;j++)
{
object o;
parser(str, out o);
}
s.Stop();
Console.WriteLine(s.Elapsed);
}
}
平均結果是反射調用比直接調用慢了200倍(在100次重復上)。 演示反射測試的小提琴
我試圖通過使用緩存的委托來提高性能:
public static class StructParserExtensions
{
public static bool IntToObjParse(string str, out object obj)
{
int i;
var result = int.TryParse(str, out i);
obj = result ? (object)i : null;
return result;
}
public static bool LongToObjParse(string str, out object obj)
{
long i;
var result = long.TryParse(str, out i);
obj = result ? (object)i : null;
return result;
}
//implementations for other types goes here
}
public static class ParserHelper
{
public delegate bool TryParseDl(string str, out object obj);
public static readonly ReadOnlyDictionary<Type, TryParseDl> StructParsers;
static ParserHelper()
{
StructParsers = new ReadOnlyDictionary<Type, TryParseDl>(CreateParsersForStructs());
}
/// Creates parsers for structs
/// </summary>
/// <returns>Dictionary</returns>
private static Dictionary<Type, TryParseDl> CreateParsersForStructs()
{
var parsers = new Dictionary<Type, TryParseDl>();
parsers[typeof(int)] = StructParserExtensions.IntToObjParse;
parsers[typeof(long)] = StructParserExtensions.LongToObjParse;
return parsers;
}
}
我假設使用委托將大大提高性能,因此它將接近直接調用,但我錯了它仍然慢約100倍(在100 reties) 這里是小提琴
我的問題是:
你只是測試100次迭代。 您主要測試一次性啟動開銷。 增加迭代次數,直到每次測試需要1秒鍾。 這樣,開銷就會消失在噪音中。
目前,您的代碼運行時間為.5毫秒。 這遠遠在噪音范圍內。 修好后我得到:
00:00:00.9711365
00:00:01.0958751 //Slightly slower
該基准使用1e7次迭代,而前一次使用1e2。 另外,請確保在Release模式下進行測試,而不需要調試器附加您關心的位數。
有什么不對:
private object Converter(object inVal, Type t)
{
return Convert.ChangeType(inVal, t);
}
如前所述。 通過100次迭代,您最有可能只測量開銷。
我進一步測試了一下。 我將您的代碼加入到一個並運行450次迭代以獲得一些統計數據。 我把它設置為解析10萬次(10 ^ 7)
這是代碼: http : //pastebin.com/65dhdX9t
這是最后幾次迭代的結果:
m-method,d-delegate,r-reflection 把它們加起來:
- 代表團是aprox。 直接呼叫速度慢1.195倍
- 反思是aprox。 比直接呼叫慢6.105倍
希望能幫助到你! 它確實幫助我說服我從反思轉移到代表團。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.