【问题标题】:Efficiency of LINQ in simple operationsLINQ 在简单操作中的效率
【发布时间】:2012-05-17 18:06:27
【问题描述】:

我最近发现了 LINQ,我发现使用它非常有趣。目前我有以下功能,我不确定它是否会更高效,毕竟产生相同的输出。

你能告诉我你对此的看法吗?

该函数以非常简单的方式简单地删除标点符号:

private static byte[] FilterText(byte[] arr)
    {
        List<byte> filteredBytes = new List<byte>();
        int j = 0; //index for filteredArray

        for (int i = 0; i < arr.Length; i++)
        {
            if ((arr[i] >= 65 && arr[i] <= 90) || (arr[i] >= 97 && arr[i] <= 122) || arr[i] == 10 || arr[i] == 13 || arr[i] == 32)
            {
                filteredBytes.Insert(j, arr[i]) ;
                j++;
            }
        }

        //return the filtered content of the buffer
        return filteredBytes.ToArray();
    }

LINQ 替代方案:

    private static byte [] FilterText2(byte[] arr)
    {
        var x = from a in arr
                where ((a >= 65 && a <= 90) || (a >= 97 && a <= 122) || a == 10 || a == 13 || a == 32)
                select a;

        return x.ToArray();
    }

【问题讨论】:

  • 你为什么有j?只需使用Add 而不是Insert,您就可以省去那个计数器。
  • 您可能应该将.Insert(j, 部分替换为.Add( 并完全删除j 计数器。
  • 是的,谢谢,我只是修改了代码,甚至没有安排!
  • 另外,由于大多数字符会被保留,您应该将new List&lt;byte&gt;() 替换为new List&lt;byte&gt;(arr.Length)。这将避免在列表变大时重新创建列表的内部结构。
  • 你不需要。传递给List&lt;T&gt;(int) 构造函数的参数仅指示容量(在不调整大小的情况下可以 容纳多少),而不是实际容纳多少。

标签: c# performance linq


【解决方案1】:

LINQ 通常比简单循环和过程代码的效率略低,但差异通常很小,而且简洁易读通常值得将简单的投影和过滤转换为 LINQ。

如果性能真的很重要,请对其进行衡量并自行决定 LINQ 代码的性能是否足够。

【讨论】:

  • 同意@Mark。使用 linq 会比调试代码花费更多时间。
  • 除非你会来调试复杂的 linq 查询,这也可能很棘手——但这并不意味着我不会使用它
【解决方案2】:

LinQ 非常适合让事情变得简单。就性能而言,如果您开始对列表、数组等进行大量转换,这确实会成为一个问题。

MyObject.where(...).ToList().something().ToList().somethingelse.ToList();

这是众所周知的杀手锏,尽量晚点转换成最终名单。

【讨论】:

  • +1:好点。如果您需要对返回的序列进行进一步的过滤/排序/操作,那么最好将其返回为 Enumerable&lt;byte&gt; 而没有 ToArray()
  • @Douglas 我想你的意思是IEnumerable&lt;byte&gt;Enumerable 是一个静态类),但另有约定。
【解决方案3】:

性能太棒了,LINQ 是因为这个原因:

private static bool IsAccepted(byte b)
{
    return (65 <= b && b <= 90) || 
           (97 <= b && b <= 122) || 
           b == 10 || b == 13 || b == 32;
}

arr.Where(IsAccepted).ToArray(); // equivalent to FilterText(arr)

即你不写如何,而只是写什么。此外,它与您提出的其他方法一样快(慢):Where(..)ToArray() 中被延迟评估,它在内部创建一个 List 并将其转换为 Array iirc。

顺便说一句,字符串在 C# 中是 Unicode,所以不要用它来做一些简单的字符串格式化(有更好的替代方案)。

【讨论】:

    【解决方案4】:

    在大多数情况下,我同意@MarkByers。 Linq 的效率将低于过程代码。一般来说,不足之处可以追溯到表达式树的编译。尽管如此,在 99% 的情况下,可读性和时间改进是值得的。当您遇到性能问题时,进行基准测试、修改和重新基准测试。

    话虽如此,LINQ 与 lambda 和匿名委托密切相关。这些功能经常被谈论,好像它们是同一个东西。在某些案例中,这些构造可以比过程代码更快看起来您的示例可能就是其中一种情况。我会重写你的代码如下:

    private static byte [] FilterText2(byte[] arr) {
    
       return arr.Where( a=> (a >= 65 && a <= 90)  || 
                             (a >= 97 && a <= 122) || 
                              a == 10 || a == 13   || a == 32
                      ).ToArray();
    }
    

    再次,为您的特定场景做一些基准测试,例如 YMMV。在什么场景下哪个速度更快,已经泼了很多墨水。这是一些墨水:

    【讨论】:

      【解决方案5】:

      Many LINQ statements are easily parallelizable. 只需将AsParallel() 添加到查询的开头即可。如果您希望以牺牲一些性能为代价来保留原始订单,也可以添加AsOrdered()。例如,以下 LINQ 语句:

      arr.Where(IsAccepted).ToArray();
      

      可以写成:

      arr.AsParallel().AsOrdered().Where(IsAccepted).ToArray();
      

      你只需要确保its overhead doesn't outweigh its benefits:

      var queryA = from num in numberList.AsParallel()
                   select ExpensiveFunction(num); //good for PLINQ
      
      var queryB = from num in numberList.AsParallel()
                   where num % 2 > 0
                   select num; //not as good for PLINQ
      

      【讨论】:

        【解决方案6】:

        每个好的书面命令式代码都会比好的书面声明式代码更节省时间和空间,因为必须将声明式代码转换为命令式代码(除非你拥有一台 Prolog 机器......你可能没有,因为你正在询问.Net :-))。

        但是,如果您可以使用 LINQ 以比使用循环更简单、更易读的方式解决问题,那就值得了。当你看到类似的东西

        var actualPrices = allPrices
            .Where(price => price.ValidFrom <= today && price.ValidTo >= today)
            .Select(price => price.PriceInUSD)
            .ToList();
        

        它是“一条线”,第一眼就能看出它在做什么。声明一个新集合,遍历旧集合,写 if 并向新集合添加一些东西不是。因此,如果您不想保存每一毫秒(您可能不想保存,因为您使用的是.Net 而不是带有嵌入式 ASM 的 C),这是一个胜利。而且 LINQ 是高度优化的 - 有更多的代码库 - 一个用于集合,一个用于 XML,一个用于 SQL ...,所以它通常不会慢很多。没有理由不使用它。

        使用 Parallel LINQ 可以轻松地并行化一些 LINQ 表达式,几乎是“免费”的(= 没有更多代码,但并行开销仍然存在,所以算一算)。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-05-15
          相关资源
          最近更新 更多