- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
您好,我正在尝试使用(缓存的)已编译的 lambda 表达式来访问属性,与使用 PropertyInfo.GetValue()/SetValue() 方法调用相比,我确实得到了更好(即更快)的结果。然而,我觉得它离“原生”属性速度还有很远的距离。是基准测试方法使结果与其他方法如此不同吗?
下面是我运行下面的代码后得到的结果:
Native: Elapsed = 00:00:00.0995876 (99.5876 ms); Step = 1.992E-005 ms
Lambda Expression: Elapsed = 00:00:00.5369273 (536.9273 ms); Step = 1.074E-004 ms
Property Info: Elapsed = 00:00:01.9187312 (1918.7312 ms); Step = 3.837E-004 ms
1.000 < 5.392 < 19.267
老实说,我觉得基于其他基准测试,编译后的 lambda 表达式应该比使用常规属性慢两倍,而不是慢 5 到 6 倍。
有什么想法吗?基准测试方法?编译后的 lambda 表达式的计算方式?
public static class Program
{
public static void Main(params string[] args)
{
var stepCount = 5000000UL;
var dummy = new Dummy();
const string propertyName = "Soother";
const bool propertyValue = true;
var propertyInfo = typeof(Dummy).GetProperty(propertyName);
var nativeBenchmark = Benchmark.Run("Native", stepCount, () => dummy.Soother = propertyValue);
var lambdaExpressionBenchmark = Benchmark.Run("Lambda Expression", stepCount, () => dummy.Set(propertyName, propertyValue));
var propertyInfoBenchmark = Benchmark.Run("Property Info", stepCount, () => propertyInfo.SetValue(dummy, propertyValue, null));
var benchmarkReports = new[] { nativeBenchmark, lambdaExpressionBenchmark, propertyInfoBenchmark }.OrderBy(item => item.ElapsedMilliseconds);
benchmarkReports.Join(Environment.NewLine).WriteLineToConsole();
var fastest = benchmarkReports.First().ElapsedMilliseconds;
benchmarkReports.Select(report => (report.ElapsedMilliseconds / fastest).ToString("0.000")).Join(" < ").WriteLineToConsole();
Console.ReadKey();
}
}
public class Dummy
{
public bool? Soother { get; set; } = true;
}
public class BenchMarkReport
{
#region Fields & Properties
public string Name { get; }
public TimeSpan ElapsedTime { get; }
public double ElapsedMilliseconds
{
get
{
return ElapsedTime.TotalMilliseconds;
}
}
public ulong StepCount { get; }
public double StepElapsedMilliseconds
{
get
{
return ElapsedMilliseconds / StepCount;
}
}
#endregion
#region Constructors
internal BenchMarkReport(string name, TimeSpan elapsedTime, ulong stepCount)
{
Name = name;
ElapsedTime = elapsedTime;
StepCount = stepCount;
}
#endregion
#region Methods
public override string ToString()
{
return $"{Name}: Elapsed = {ElapsedTime} ({ElapsedMilliseconds} ms); Step = {StepElapsedMilliseconds:0.###E+000} ms";
}
#endregion
}
public class Benchmark
{
#region Fields & Properties
private readonly Action _stepAction;
public string Name { get; }
public ulong StepCount { get; }
public Benchmark(string name, ulong stepCount, Action stepAction)
{
Name = name;
StepCount = stepCount;
_stepAction = stepAction;
}
#endregion
#region Constructors
#endregion
#region Methods
public static BenchMarkReport Run(string name, ulong stepCount, Action stepAction)
{
var benchmark = new Benchmark(name, stepCount, stepAction);
var benchmarkReport = benchmark.Run();
return benchmarkReport;
}
public BenchMarkReport Run()
{
return Run(StepCount);
}
public BenchMarkReport Run(ulong stepCountOverride)
{
var stopwatch = Stopwatch.StartNew();
for (ulong i = 0; i < StepCount; i++)
{
_stepAction();
}
stopwatch.Stop();
var benchmarkReport = new BenchMarkReport(Name, stopwatch.Elapsed, stepCountOverride);
return benchmarkReport;
}
#endregion
}
public static class ObjectExtensions
{
public static void WriteToConsole<TInstance>(this TInstance instance)
{
Console.Write(instance);
}
public static void WriteLineToConsole<TInstance>(this TInstance instance)
{
Console.WriteLine(instance);
}
// Goodies: add name inference from property lambda expression
// e.g. "instance => instance.PropertyName" redirected using "PropertyName"
public static TProperty Get<TInstance, TProperty>(this TInstance instance, string propertyName)
{
return FastPropertyRepository<TInstance, TProperty>.GetGetter(propertyName)(instance);
}
public static void Set<TInstance, TProperty>(this TInstance instance, string propertyName, TProperty propertyValue)
{
FastPropertyRepository<TInstance, TProperty>.GetSetter(propertyName)(instance, propertyValue);
}
}
public static class EnumerableExtensions
{
public static string Join<TSource>(this IEnumerable<TSource> source, string separator = ", ")
{
return string.Join(separator, source);
}
}
internal static class FastPropertyRepository<TInstance, TProperty>
{
private static readonly IDictionary<string, Action<TInstance, TProperty>> Setters;
private static readonly IDictionary<string, Func<TInstance, TProperty>> Getters;
static FastPropertyRepository()
{
Getters = new ConcurrentDictionary<string, Func<TInstance, TProperty>>();
Setters = new ConcurrentDictionary<string, Action<TInstance, TProperty>>();
}
public static Func<TInstance, TProperty> GetGetter(string propertyName)
{
Func<TInstance, TProperty> getter;
if (!Getters.TryGetValue(propertyName, out getter))
{
getter = FastPropertyFactory.GeneratePropertyGetter<TInstance, TProperty>(propertyName);
Getters[propertyName] = getter;
}
return getter;
}
public static Action<TInstance, TProperty> GetSetter(string propertyName)
{
Action<TInstance, TProperty> setter;
if (!Setters.TryGetValue(propertyName, out setter))
{
setter = FastPropertyFactory.GeneratePropertySetter<TInstance, TProperty>(propertyName);
Setters[propertyName] = setter;
}
return setter;
}
}
internal static class FastPropertyFactory
{
public static Func<TInstance, TProperty> GeneratePropertyGetter<TInstance, TProperty>(string propertyName)
{
var parameterExpression = Expression.Parameter(typeof(TInstance), "value");
var propertyValueExpression = Expression.Property(parameterExpression, propertyName);
var expression = propertyValueExpression.Type == typeof(TProperty) ? propertyValueExpression : (Expression)Expression.Convert(propertyValueExpression, typeof(TProperty));
var propertyGetter = Expression.Lambda<Func<TInstance, TProperty>>(expression, parameterExpression).Compile();
return propertyGetter;
}
public static Action<TInstance, TProperty> GeneratePropertySetter<TInstance, TProperty>(string propertyName)
{
var instanceParameterExpression = Expression.Parameter(typeof(TInstance));
var parameterExpression = Expression.Parameter(typeof(TProperty), propertyName);
var propertyValueExpression = Expression.Property(instanceParameterExpression, propertyName);
var conversionExpression = propertyValueExpression.Type == typeof(TProperty) ? parameterExpression : (Expression)Expression.Convert(parameterExpression, propertyValueExpression.Type);
var propertySetter = Expression.Lambda<Action<TInstance, TProperty>>(Expression.Assign(propertyValueExpression, conversionExpression), instanceParameterExpression, parameterExpression).Compile();
return propertySetter;
}
}
最佳答案
我将您的工作简化为更小的方法。它提高了整体性能,但也扩大了差距。
Native : 00:00:00.0029713 ( 2.9713ms) 5.9426E-07
Lambda Expression : 00:00:00.4356385 ( 435.6385ms) 8.71277E-05
Property Info : 00:00:01.3436626 ( 1343.6626ms) 0.00026873252
这里是使用的方法
public class Dummy
{
public bool? Soother { get; set; } = true;
}
public class Lab
{
Dummy _dummy = new Dummy();
ulong _iterations = 5000000UL;
const bool _propertyValue = true;
const string _propertyName = "Soother";
public BenchmarkReport RunNative()
{
Stopwatch stopwatch = Stopwatch.StartNew();
for (ulong i = 0; i < _iterations; i++)
{
_dummy.Soother = _propertyValue;
}
stopwatch.Stop();
return new BenchmarkReport("Native", stopwatch.Elapsed, _iterations);
}
public BenchmarkReport RunLambdaExpression()
{
Stopwatch stopwatch = Stopwatch.StartNew();
for (ulong i = 0; i < _iterations; i++)
{
_dummy.Set(_propertyName, _propertyValue);
}
stopwatch.Stop();
return new BenchmarkReport("Lambda Expression", stopwatch.Elapsed, _iterations);
}
public BenchmarkReport RunPropertyInfo()
{
PropertyInfo propertyInfo = typeof(Dummy).GetProperty(_propertyName);
Stopwatch stopwatch = Stopwatch.StartNew();
for (ulong i = 0; i < _iterations; i++)
{
propertyInfo.SetValue(_dummy, _propertyValue);
}
stopwatch.Stop();
return new BenchmarkReport("Property Info", stopwatch.Elapsed, _iterations);
}
}
public class BenchmarkReport
{
public string Name { get; set; }
public TimeSpan ElapsedTime { get; set; }
public ulong Iterations { get; set; }
public BenchmarkReport(string name, TimeSpan elapsedTime, ulong iterations)
{
Name = name;
ElapsedTime = elapsedTime;
Iterations = iterations;
}
}
以及运行它的程序
public static class Program
{
public static void Main(params string[] args)
{
Lab lab = new Lab();
List<BenchmarkReport> benchmarkReports = new List<BenchmarkReport>()
{
lab.RunNative(),
lab.RunLambdaExpression(),
lab.RunPropertyInfo()
};
foreach (var report in benchmarkReports)
{
Console.WriteLine("{0}: {1} ({2}ms) {3}",
report.Name.PadRight(20),
report.ElapsedTime,
report.ElapsedTime.TotalMilliseconds.ToString().PadLeft(10),
(double)report.ElapsedTime.TotalMilliseconds / report.Iterations);
}
Console.ReadKey();
}
}
关于c# - 已编译的 lambda 表达式用作属性 getter 和 setter : wrong benchmarking method or wrong lambda expression construction?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42221930/
这个问题在这里已经有了答案: java.lang.IllegalArgumentException: The servlets named [X] and [Y] are both mapped t
我无法让我的 WebRTC 代码正常工作。我相信我所做的一切都是正确的,但它仍然无法正常工作。为什么 ontrack 这么早就被调用有些奇怪,也许它应该是那样的。 该网站使用 javascript 代
使用 Mac OSX 优胜美地 (10.10.4): rails -v => Rails 4.2.3 ruby -v => ruby 2.2.2p95 遵循这些说明的组合: https://www
您好,我正在尝试使用(缓存的)已编译的 lambda 表达式来访问属性,与使用 PropertyInfo.GetValue()/SetValue() 方法调用相比,我确实得到了更好(即更快)的结果。然
我编写此代码是为了获取学生的字母成绩并计算他们的 GPA。当我运行该程序时,我可以正确获取学生的姓名和科目,但无法显示成绩或 GPA。 示例输入: Sally 1 A N 示例输出: Enter St
我一直在编写这段代码,根据这本书应该可以做到这一点: Write a script that creates and calls a stored procedure named test. This
我真的很难创建一个具有以下基本格式的有效多维 JavaScript 数组: var countries = [ { "country": "UK", "properties": {
我有一个小型 Python OOP 程序,其中 2 个类 Flan 和 Outil 继承自父类(super class) Part。 我的问题是,当我调用 Flan 时一切正常,但是当我调用 Outi
我目前正在尝试使用通用监听器来编写事件系统。 所有监听器都应添加到单个 EventSource 对象,该对象将为特定事件调用其 receiveEvent() 方法。 事件源: public class
我正在通过我的 PHP 应用程序发送电子邮件。但是,它们被 Gmail 标记为垃圾邮件。这是我发送电子邮件的方式(PHP): $headers = "From: test@bookmytakeout.
我已经正式走到了穷途末路的地步。我找不到我做错了什么。我完成的这个程序几乎与我几天前编写的另一个程序一模一样,但我在编译时遇到了问题。我不知道为什么输出线上出现错误。请帮忙: 这是正在运行的文件: p
---编辑:我不允许使用任何包或预置方法。不用担心,我不想让你做我的“作业”,我只需要一点提示!---我发现these interesting Algorithms 。我想使用按位添加方法。我的问题是
我制作了一个小程序,尝试使用 conn.getOutputStream(); 检索 URLConnection 对象输出流。当我尝试执行此操作时,我的小程序抛出异常 java.net.UnknownS
每当我尝试在 SVN 中合并时,我都会遇到成堆的树冲突。好吧,就此示例脚本而言,只有一个,但仍然如此。 #!/bin/bash svnadmin create repo svn checkout fi
我开始为 Scala 中的 X500PrincipalBuilder 类编写单元测试。这是我的测试代码: import org.junit.runner.RunWith import org.scal
我正在用 python 编写我的第一个程序,它必须模拟粒子(两种气体)的混合。我不知道我这个功能做错了什么。我不希望颗粒离开某些区域,即容器的壁。我使用 VPython。 def poruszanie
我正在尝试求解三角方程组,但我认为 Python 没有生成正确的解。我试图解决的方程: 1 − 2cosθ1 + 2cosθ2 − 2cosθ3 = −0.8 1 − 2cos5θ1 + 2cos5θ
这个问题已经有答案了: TypeError: worker() takes 0 positional arguments but 1 was given [duplicate] (11 个回答) 已关
大家好,我正在努力解决这个问题 编写一个 C 程序,计算弹丸在撞击地面之前行进的距离(即射程)、弹丸撞击地面所需的时间以及弹丸飞行中的最大高度(给定角度)它被射向空中,以及发射时的初始速度(速度)。我
我编写了代码来计算 QuickSort 中完成的比较次数。 每当对长度为 m 的数组执行快速排序时,该算法都会将比较次数增加 m-1(因为主元将与除自身以外的所有内容进行比较)。 枢轴的选择始终是数组
我是一名优秀的程序员,十分优秀!