“关闭变量会使性能稍差”。 怎么样?

在给出SO问题的答案时,我被告知我的解决方案将引入一个变量闭包,因此性能稍差。 所以我的问题是:

  1. 如何关闭?
  2. 它将如何影响性能?

这是个问题

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; } }