Linq and parameters

本文关键字:parameters and Linq | 更新日期: 2023-09-27 18:21:04

我只是想知道在性能方面什么更高效。我喜欢读values,而我的同事喜欢读value2

var values = results.Select(x => new CategoryMixWidgetValueDto
{
    Dimension = x.Dimension, 
    LpeAmount = x.LpeAmount == null ? 0 : double.Parse(x.LpeAmount, CultureInfo.InvariantCulture),
    BudgetAmount = x.BudgetAmount == null ? 0 : double.Parse(x.BudgetAmount, CultureInfo.InvariantCulture),
    Variance = x.Variance == null ? 0 : double.Parse(x.Variance, CultureInfo.InvariantCulture)
})
.Select(x => new CategoryMixWidgetValueDto
{
    Dimension = x.Dimension,
    LpeAmount = x.LpeAmount,
    BudgetAmount = x.BudgetAmount,
    Variance = (x.LpeAmount.DoubleEquals(0) && x.BudgetAmount.DoubleEquals(0)) ? 0 : x.BudgetAmount.DoubleEquals(0) ? null : (x.Variance),
}).ToList();

var values2 = results.Select(x =>
{
    var lpeAmount = x.LpeAmount == null ? 0 : double.Parse(x.LpeAmount, CultureInfo.InvariantCulture);
    var budgetAmount = x.BudgetAmount == null ? 0 : double.Parse(x.BudgetAmount, CultureInfo.InvariantCulture);
    var variance = x.Variance == null ? null : (double?)double.Parse(x.Variance, CultureInfo.InvariantCulture);
    return new CategoryMixWidgetValueDto
    {
        Dimension = x.Dimension,
        LpeAmount = lpeAmount,
        BudgetAmount = budgetAmount,
        Variance = (lpeAmount.DoubleEquals(0) && budgetAmount.DoubleEquals(0)) ? 0 : budgetAmount.DoubleEquals(0) ? null : variance
    };
}).ToList();

Linq and parameters

第二个应该更高效,因为第一个为每个源元素创建两个实例,并且有两个select迭代器在运行。

但是,差异是否显著取决于您的用例。如果元素的数量很小,dto构造函数非常轻(应该是这样),并且您没有在一个循环中执行数百万次,那么您不应该看到显著的差异。

在我看来,由于转换似乎有点长,您应该将此转换转换为一个新方法,在select中使用该方法。这样,您可以更容易地维护转换。

MyDto GetDto( MyResult resultItem)
{
    ...
}
var dtos = results.Select(GetDto);

如果你查看每个版本的流程,你会发现两个版本都有共同的事情,加上彼此唯一的事情。

这里有一些你喜欢的格式的伪代码:

For each item in source:
    Call Select to perform:
        Convert input data types
        Create intermediate object from converted data
    Call Select on intermediate object to perform:
        Do calculations
        Create result object from calculation results
Gather results to new List object

第二个:

For each item in source:
    Call Select to perform:
        Convert input data types
        Do calculations
        Create result object from calculation results
Gather results to new List object

因此,您的格式有一个额外的对Select的调用,以及所需的开销,加上中间对象的额外对象实例化。因此,第二种格式在内存和时间上都更高效,因为它执行的操作更少,开销更少,创建的对象更少。

但是

然而,你必须问的真正问题是,这种低效率对你的真实用例有多大影响?这对你来说是一个问题,还是专注于它只是过早优化的又一个例子?

我对这个问题做了一个快速模拟,10000条源记录被处理了100次(总共处理了1000000个对象),对输出进行计数,而不是将它们放入List中。以下是一些统计数据:

Time per item (method A): 3.1616 μs
Time per item (method B): 2.9895 μs
Difference: 172.09 ns
Overhead: ~5.8%

这看起来很糟糕,但可能根本不会影响你的输出。每5.8条百万条输入记录,开销就会浪费1秒。

是的,通常高效地编码会更好。不,如果你所沉溺的低效率可以用纳秒来衡量,那可能并不重要。。。除非您试图处理数十亿条记录,并经常重复该过程。

第二种方法更好。忽略真实的影响,简单的数学计算表明,1op<2个操作(在您的情况下,操作是在堆上分配对象)。请注意,当查询针对IEnumerable<T>IQueryable<T>时有很大的区别-在后一种情况下,查询提供程序可以优化执行,因为所有内容都表示为表达式树,而在前一种情况中,所有内容都以您编写的方式执行。换句话说,IQueryable提供程序可以使两种变体以完全相同的方式执行。但这里的情况并非如此。

甚至还有一种特殊的LINQ语法支持第二种情况,如果结构完全等效,就不需要它:

var values2 = (from x in results
    let lpeAmount = x.LpeAmount == null ? 0 : double.Parse(x.LpeAmount, CultureInfo.InvariantCulture)
    let budgetAmount = x.BudgetAmount == null ? 0 : double.Parse(x.BudgetAmount, CultureInfo.InvariantCulture)
    let variance = x.Variance == null ? null : (double?)double.Parse(x.Variance, CultureInfo.InvariantCulture);
    select new CategoryMixWidgetValueDto
    {
        Dimension = x.Dimension,
        LpeAmount = lpeAmount,
        BudgetAmount = budgetAmount,
        Variance = (lpeAmount.DoubleEquals(0) && budgetAmount.DoubleEquals(0)) ? 0 : budgetAmount.DoubleEquals(0) ? null : variance
    }
).ToList();