LINQ ifadesi 'foreach' döngüsünden daha mı hızlıdır?


124

Bir Mesh Rendering yöneticisi yazıyorum ve aynı gölgelendiriciyi kullanan tüm ağları gruplamanın ve sonra bunları o gölgelendirici geçişindeyken oluşturmanın iyi bir fikir olacağını düşündüm.

Şu anda bir foreachdöngü kullanıyorum , ancak LINQ kullanmanın bana bir performans artışı sağlayıp sağlamayacağını merak ediyor musunuz?



1
Lütfen @ MarcGravell'in yanıtını kabul edilene ayarlamayı düşünün, örneğin linq'den sql'ye, linq'in for / foreach'den daha hızlı olduğu durumlar vardır.
paqogomez

Yanıtlar:


222

LINQ neden daha hızlı olmalı? Ayrıca dahili olarak döngüler kullanır.

Çoğu zaman, LINQ biraz daha yavaş olacaktır çünkü ek yük getirir. Performansı çok önemsiyorsanız LINQ kullanmayın. Daha kısa, daha okunaklı ve bakımı yapılabilir bir kod istediğiniz için LINQ kullanın.


7
Öyleyse deneyiminize göre, LINQ daha hızlıdır ve kodun okunmasını ve bakımını zorlaştırır? Lütfen açıkla.
codymanix

87
Sanırım geri almışsın. LINQ'nun YAVAŞ olduğunu söylüyor. Bu aşırı baştan kaynaklanıyor. Ayrıca LINQ'nun okunması ve bakımı daha kolay olduğunu söylüyor.
Joseph McIntyre

5
Afedersiniz. Bu arada, linq'i karşılaştırdığımız birçok şey vardı ve her performans için veya her performans için ve çoğu zaman linq daha hızlıydı.
Offler

34
Dürüst olmak gerekirse, foreach döngüsü, LINQ Metodundan daha okunabilir. LINQ kullanıyorum çünkü havalı :)
LuckyLikey

4
Evet, ancak bazı durumlarda LINQ okunabilirliği gerçekten artırabilir, bu yüzden akılsız
yorumumu

59

LINQ-to-Objects genel olarak bazı marjinal genel giderler (çoklu yineleyiciler, vb.) Ekleyecektir. Hala döngüleri yapmak zorundadır ve delege çağrıları vardır ve yakalanan değişkenlere ulaşmak için genellikle fazladan referans alma yapmak zorunda kalacaktır. Çoğu kodda, bu neredeyse tespit edilemez ve kodun anlaşılması daha kolay olanın sağlayabileceğinden daha fazlasını sağlar .

LINQ-to-SQL gibi diğer LINQ sağlayıcıları söz konusu olduğunda, sorgu sunucuda filtreleme yapabildiğinden , düz olandan çok daha iyi olmalıdır foreach, ancak büyük olasılıkla "select * from foo" zaten bir battaniye yapmazdınız , bu yüzden bu mutlaka adil değil karşılaştırması.

Yeniden PLINQ; paralellik geçen süreyi azaltabilir , ancak toplam CPU süresi genellikle iş parçacığı yönetiminin genel giderleri vb. nedeniyle biraz artacaktır.


Başka cevap size ima değil bellek içi koleksiyonları LINQ kullanarak - örneğin List<Foo>; bunun yerine foreachbu koleksiyonlarda bir blok kullanmalıyım . foreachBu bağlamlarda kullanılması önerisi mantıklı. Endişem: LINQ sorgularını yalnızca bir performans sorunu tespit foreach edersem değiştirmeli miyim? İleride, foreachilkini ele alacağım .
IAbstract


15

LINQ şimdi daha yavaş, ancak bir noktada daha hızlı olabilir. LINQ ile ilgili iyi olan şey, nasıl çalıştığını umursamanıza gerek olmamasıdır. İnanılmaz derecede hızlı yeni bir yöntem düşünülürse, Microsoft'taki insanlar bunu size söylemeden uygulayabilir ve kodunuz çok daha hızlı olur.

Daha da önemlisi, LINQ'nun okunması çok daha kolaydır. Bu yeterli bir sebep olmalı.


3
"Microsoft bunu uygulayabilir" satırını beğendim mümkün mü, yani çerçeveyi yükseltmeden bu mümkün mü?
Shrivallabh

1
LINQ hiçbir zaman yerel uygulamadan daha hızlı olmayacak, çünkü günün sonunda yerel uygulamaya dönüşecek. Daha hızlı LINQ makine kodunu çevirmek için kullanılabilecek özel LINQ CPU talimatları ve LINQ kayıtları yoktur - ve olsaydı, bunlar LINQ olmayan kodlar tarafından da kullanılırdı.
mg30rg

Doğru değil, bir noktada belirli bağlantı işlemleri çok iş parçacıklı hale gelebilir veya bir noktada GPU'yu kullanabilir.
John Stok



5

Bu soruyla ilgilendiğim için şimdi bir test yaptım. Microsoft Windows 7 Ultimate çalıştıran 8 GB ram ile Intel (R) Core (TM) i3-2328M CPU @ 2.20GHz, 2200 Mhz, 2 Core (s) üzerinde .NET Framework 4.5.2'yi kullanma.

Görünüşe göre LINQ her döngüden daha hızlı olabilir. İşte aldığım sonuçlar:

Exists = True
Time   = 174
Exists = True
Time   = 149

Bazılarınızın bu kodu bir konsol uygulamasına kopyalayıp yapıştırması ve test etmesi ilginç olurdu. Bir nesne (Çalışan) ile test etmeden önce aynı testi tamsayılarla denedim. LINQ orada da daha hızlıydı.

public class Program
{
    public class Employee
    {
        public int id;
        public string name;
        public string lastname;
        public DateTime dateOfBirth;

        public Employee(int id,string name,string lastname,DateTime dateOfBirth)
        {
            this.id = id;
            this.name = name;
            this.lastname = lastname;
            this.dateOfBirth = dateOfBirth;

        }
    }

    public static void Main() => StartObjTest();

    #region object test

    public static void StartObjTest()
    {
        List<Employee> items = new List<Employee>();

        for (int i = 0; i < 10000000; i++)
        {
            items.Add(new Employee(i,"name" + i,"lastname" + i,DateTime.Today));
        }

        Test3(items, items.Count-100);
        Test4(items, items.Count - 100);

        Console.Read();
    }


    public static void Test3(List<Employee> items, int idToCheck)
    {

        Stopwatch s = new Stopwatch();
        s.Start();

        bool exists = false;
        foreach (var item in items)
        {
            if (item.id == idToCheck)
            {
                exists = true;
                break;
            }
        }

        Console.WriteLine("Exists=" + exists);
        Console.WriteLine("Time=" + s.ElapsedMilliseconds);

    }

    public static void Test4(List<Employee> items, int idToCheck)
    {

        Stopwatch s = new Stopwatch();
        s.Start();

        bool exists = items.Exists(e => e.id == idToCheck);

        Console.WriteLine("Exists=" + exists);
        Console.WriteLine("Time=" + s.ElapsedMilliseconds);

    }

    #endregion


    #region int test
    public static void StartIntTest()
    {
        List<int> items = new List<int>();

        for (int i = 0; i < 10000000; i++)
        {
            items.Add(i);
        }

        Test1(items, -100);
        Test2(items, -100);

        Console.Read();
    }

    public static void Test1(List<int> items,int itemToCheck)
    {

        Stopwatch s = new Stopwatch();
        s.Start();

        bool exists = false;
        foreach (var item in items)
        {
            if (item == itemToCheck)
            {
                exists = true;
                break;
            }
        }

        Console.WriteLine("Exists=" + exists);
        Console.WriteLine("Time=" + s.ElapsedMilliseconds);

    }

    public static void Test2(List<int> items, int itemToCheck)
    {

        Stopwatch s = new Stopwatch();
        s.Start();

        bool exists = items.Contains(itemToCheck);

        Console.WriteLine("Exists=" + exists);
        Console.WriteLine("Time=" + s.ElapsedMilliseconds);

    }

    #endregion

}

Elimde olan şu: Var = Gerçek Zaman = 274 Var = Gerçek Zaman = 314
PmanAce

2
Linq'i

3
İlginç. Exists=True Time=184 Exists=True Time=135Bir Apache Gaming dizüstü bilgisayarında aldım (Win 10, C # 7.3). Derlendi ve hata ayıklama modunda çalıştırıldı. Aldığım testleri tersine çevirirsem Exists=True Time=158 Exists=True Time=194. Görünüşe göre Linq daha optimize edilmiş.
James Wilkins

1
Bu yazıda nesne testi ile ilgili bir yanlış anlama var. List.Exists ve .Contains'in foreach'tan daha iyi performans göstermesi kesinlikle ilginç olsa da. Exists'in varlıklar için linq yöntemi olmadığını ve sadece listelerde çalışacağını unutmamak önemlidir, linq eşdeğer yöntemi .Any (), kesinlikle foreach'ten daha yavaş performans gösterir.
AbdulG

3

Bu aslında oldukça karmaşık bir sorudur. Linq bazı şeyleri yapmayı çok kolaylaştırır, eğer bunları kendiniz uygularsanız, yanıltıcı olabilirsiniz (örneğin linq .Except ()). Bu özellikle PLinq için ve özellikle PLinq tarafından uygulandığı şekliyle paralel toplama için geçerlidir.

Genel olarak, özdeş kod için, delege çağrısının ek yükü nedeniyle linq daha yavaş olacaktır.

Bununla birlikte, geniş bir veri dizisi işliyorsanız ve öğelere nispeten basit hesaplamalar uyguluyorsanız, aşağıdaki durumlarda büyük bir performans artışı elde edersiniz:

  1. Verileri depolamak için bir dizi kullanırsınız.
  2. Her elemana erişmek için bir for döngüsü kullanırsınız (foreach veya linq'in aksine).

    • Not: Kıyaslama yaparken lütfen herkesi hatırlayın - ardışık iki test için aynı diziyi / listeyi kullanırsanız, CPU önbelleği ikincisini daha hızlı hale getirecektir. *
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.