Uma instrução LINQ é mais rápida que um loop 'foreach'?


123

Estou escrevendo um gerente de renderização de malha e pensei que seria uma boa ideia agrupar todas as malhas que usam o mesmo sombreador e renderizá-las enquanto estou nesse passe de sombreador.

Atualmente, estou usando um foreachloop, mas me perguntei se a utilização do LINQ poderia me dar um aumento de desempenho?



1
Por favor, considere configurar a resposta de @ MarcGravell para a aceita, há situações, linq para sql por exemplo, em que o linq é mais rápido que o for / foreach.
paqogomez

Respostas:


221

Por que o LINQ deve ser mais rápido? Ele também usa loops internamente.

Na maioria das vezes, o LINQ é um pouco mais lento porque apresenta sobrecarga. Não use o LINQ se você se preocupa muito com o desempenho. Use o LINQ porque você deseja um código mais curto, de melhor legibilidade e manutenção.


7
Portanto, sua experiência é que o LINQ é mais rápido e dificulta a leitura e a manutenção do código? Por favor explique.
codymanix

87
Eu acho que você teve isso ao contrário. Ele está dizendo que o LINQ é MAIS LENTO. Isto é devido à sobrecarga. Ele também está dizendo que o LINQ é mais fácil de ler e manter.
Joseph McIntyre

5
Desculpe. Enquanto isso, tínhamos muitas coisas em que comparamos o linq e o desempenho de cada um deles, e na maioria das vezes o linq era mais rápido.
Offler

34
Para ser sincero, na minha opinião, um loop foreach é mais legível que o método LINQ. Eu uso LINQ porque é legal :)
LuckyLikey

4
Sim, mas em alguns casos LINQ pode realmente melhorar a legibilidade, então esqueça o meu comentário estúpido <3
LuckyLikey

59

O LINQ-to-Objects geralmente adicionará algumas sobrecargas marginais (vários iteradores, etc.). Ele ainda precisa executar os loops e delegar chamadas, e geralmente precisará fazer uma desreferenciação extra para obter as variáveis ​​capturadas etc. Na maioria dos códigos, isso será virtualmente indetectável e mais do que o código mais simples de entender.

Com outros provedores de LINQ, como o LINQ-to-SQL, então, como a consulta pode filtrar no servidor, ela deve ser muito melhor do que uma apartamento foreach, mas provavelmente você não teria feito uma manta de "select * from foo" qualquer maneira , portanto, isso não é necessariamente justo. comparação.

Re PLINQ; o paralelismo pode reduzir o tempo decorrido , mas o tempo total da CPU geralmente aumenta um pouco devido às despesas gerais do gerenciamento de threads, etc.


Em outra resposta, você aludiu a não usar o LINQ em coleções na memória - por exemplo List<Foo>; em vez disso, devo usar um foreachbloco nessas coleções. A recomendação para usar foreachnesses contextos faz sentido. Minha preocupação: devo substituir apenas as consultas LINQ foreach se detectar um problema de desempenho? No futuro, considerarei o foreachprimeiro.
precisa saber é o seguinte


15

O LINQ está mais lento agora, mas pode ficar mais rápido em algum momento. A coisa boa sobre o LINQ é que você não precisa se preocupar com como ele funciona. Se um novo método for incrivelmente rápido, as pessoas da Microsoft poderão implementá-lo sem nem mesmo informar a você e seu código seria muito mais rápido.

Mais importante, porém, o LINQ é muito mais fácil de ler. Isso deve ser motivo suficiente.


3
Eu gosto da linha "Microsoft pode implementá-lo" é possível, quero dizer, é possível sem atualizar a estrutura?
Shrivallabh

1
O LINQ nunca será realmente mais rápido que a implementação nativa, pois no final do dia, ele se traduz na implementação nativa. Não há instruções especiais da CPU do LINQ e registros do LINQ que possam ser usados ​​para converter códigos de máquina LINQ mais rápidos - e, se houver, eles também serão usados ​​por códigos não LINQ.
mg30rg

Não é verdade, em algum momento, certas operações de link podem se tornar multithread ou até utilizar a GPU em algum momento.
John da



5

Eu estava interessado nesta questão, então fiz um teste agora. Usando o .NET Framework 4.5.2 em uma CPU Intel (R) Core i3-2328M a 2,20 GHz, 2200 Mhz, 2 Núcleos com 8 GB de RAM executando o Microsoft Windows 7 Ultimate.

Parece que o LINQ pode ser mais rápido do que para cada loop. Aqui estão os resultados que obtive:

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

Seria interessante se alguns de vocês pudessem copiar e colar esse código em um aplicativo de console e testar também. Antes de testar com um objeto (Employee), tentei o mesmo teste com números inteiros. O LINQ também foi mais rápido lá.

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

}

Isto é o que eu tenho: Existe = True Time = 274 Existe = True Time = 314
PmanAce

2
Você já pensou em fazer o LINQ primeira e foreach mais tarde, pode fazer alguma diferença bem
Muhammad Mamoor Khan

3
Interessante. Eu peguei o Exists=True Time=184 Exists=True Time=135É em um laptop Apache Gaming (Win 10, C # 7.3). Compilado e executado no modo de depuração. Se eu reverter os testes que recebo Exists=True Time=158 Exists=True Time=194. Parece que o Linq está mais otimizado, eu acho.
James Wilkins

1
Há um mal-entendido neste post sobre o teste do objeto. Embora seja definitivamente interessante que List.Exists e .Contains pareçam ter um desempenho melhor que o foreach. É importante observar que o .Exists não é um método linq para entidades e só funcionará em listas, seu método equivalente ao linq, .Any (), definitivamente executa mais lentamente que o foreach.
AbdulG 27/11/19

3

Esta é realmente uma questão bastante complexa. O Linq facilita muito algumas coisas, que se você as implementar, poderá tropeçar (por exemplo, linq .Except ()). Isso se aplica particularmente ao PLinq, e especialmente à agregação paralela, conforme implementada pelo PLinq.

Em geral, para código idêntico, o linq será mais lento, devido à sobrecarga da chamada de delegado.

No entanto, se você estiver processando uma grande variedade de dados e aplicando cálculos relativamente simples aos elementos, obterá um enorme aumento de desempenho se:

  1. Você usa uma matriz para armazenar os dados.
  2. Você usa um loop for para acessar cada elemento (ao contrário de foreach ou linq).

    • Nota: Ao fazer o benchmarking, lembre-se todos - se você usar a mesma matriz / lista por dois testes consecutivos, o cache da CPU tornará o segundo mais rápido. *
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.