“关闭变量会使性能稍差”。 怎么样?
在给出SO问题的答案时,我被告知我的解决方案将引入一个变量闭包,因此性能稍差。 所以我的问题是:
- 如何关闭?
- 它将如何影响性能?
这是个问题
List.Where(s => s.ValidDate.Date == DateTime.Today.Year).ToList();
这是我的解决方案 。 我将变量yr
引入存储年份。
int yr = DateTime.Now.Year; List.Where(s => s.ValidDate.Year == yr).ToList();
这是答案的评论
首先,这两个解决方案在function上并不相同(如果您修复了将日期与int进行比较( .Date == .Today.Year
)):
-
第一个片段为列表的每个值重新评估
DateTime.Today.Year
,当迭代期间当前年份更改时,可以给出不同的结果 -
第二个片段存储当前年份并重新使用它,因此结果列表中的所有项目将具有相同的年份。 (我个人采取这种方法,因为我想确保结果是合理的 )。
引入闭包是因为lambda从其外部作用域访问变量,它会关闭yr
的值。 C#compile将生成一个新类,其中包含一个保存yr
的字段。 对yr
所有引用都将替换为new字段,并且原始yr
甚至不会存在于已编译的代码中
我怀疑通过引入一个闭包会有性能损失。 如果有的话,使用闭包的代码会更快,因为它不必为每个列表项创建新的DateTime
实例,然后取消引用两个属性。 它只需要访问编译器生成的闭包类的字段,该闭包类保存当前年份的int值。 (任何想要比较生成的IL代码或配置两个片段的人?:))
除了knittl的答案,我想尝试和测量有和没有闭合的性能,这是我的测试看起来像:
internal class SomeData { public DateTime ValidDate { get; set; } // other data ... } class Program { static void Main(string[] args) { var stopWatch = new Stopwatch(); // Test with closure IEnumerable data1 = CreateTestData(100000); stopWatch.Start(); int yr = DateTime.Now.Year; List results1 = data1.Where(x => x.ValidDate.Year == yr).ToList(); stopWatch.Stop(); Console.WriteLine("With a closure - {0} ms", stopWatch.Elapsed.Milliseconds); // ### Output on my machine (consistently): With a closure - 16 ms stopWatch.Reset(); // Test without a closure IEnumerable data2 = CreateTestData(100000); stopWatch.Start(); List results2 = data2.Where(x => x.ValidDate.Year == DateTime.Today.Year).ToList(); stopWatch.Stop(); Console.WriteLine("Without a closure - {0} ms", stopWatch.Elapsed.Milliseconds); // ### Output on my machine: Without a closure - 33 ms } private static IEnumerable CreateTestData(int numberOfItems) { var dt = DateTime.Today; for (int i = 0; i < numberOfItems; i++) { yield return new SomeData {ValidDate = dt}; } } }
我的测试的底线 - 正如我所预期的那样,带闭合的版本要快得多。
这是一个天真的时间测量,仅仅是为了补充knittl的答案。
结果是,每次评估DateTime.Now
的版本比您的代码慢 10倍。
在我的机器上的结果:T1:8878毫秒; T2:589毫秒。 (最大化优化,没有调试器等)。
class Program { static void Main(string[] args) { var things = new List(); var random = new Random(111); for (int i = 0; i < 100000; ++i) { things.Add(new Something(random.Next(2010, 2016))); } // to avoid measuring the JIT compilation and optimization time T1(things); T2(things); var sw = Stopwatch.StartNew(); for (int i = 0; i < 100; ++i) { T1(things); } Console.WriteLine(sw.ElapsedMilliseconds); sw.Restart(); for (int i = 0; i < 100; ++i) { T2(things); } Console.WriteLine(sw.ElapsedMilliseconds); Console.ReadLine(); } private static void T1(List list) { var result = list.Where(x => x.ValidDate.Year == DateTime.Now.Year).ToList(); } private static void T2(List list) { var yr = DateTime.Now.Year; var result = list.Where(x => x.ValidDate.Year == yr).ToList(); } } class Something { public Something(int year) { this.ValidDate = new DateTime(year, 1, 1); } public DateTime ValidDate { get; private set; } }