Jak zbadać wydajność krótkiego kodu?

Odpowiedz Nowy wątek
2019-06-16 14:49
0

Chcialem się z ciekawości dowiedzieć czy np:

var c = g.Where(o => o > 2 && o < 5).ToList();

jest szybsze od:

var c = g.Where(o => o > 2).Where(o=> o < 5).ToList();

Użyłem klasy Stopwatch, ale co uruchomienie programu to dostaje inne wyniki. Przy milionie wywołań widoczna jest różnica na korzyść pojedynczego where.
Wkleiłem kod na dotnetfiddle, podejrzałem kod IL. Za wiele on mi nie powiedział poza tym ze wynikowy kod z pojedynczym Where ma 20 linii kodu mniej. Czy można uznać ze ten pojedynczy Where jest szybszy?
Jak inaczej można to zbadać?

Patrząc w IL zwróć uwagę, że ciało lambdy jest osobną metodą i nie ma jej kodu w ciele głównej metody - mad_penguin 2019-06-16 15:54

Pozostało 580 znaków

2019-06-16 14:58
0

To też zależy od kontekstu. Gdy uruchamiasz LINQ na obiektach w pamięci rzeczywiście pojedynczy Where może być szybszy, ponieważ każde wywołanie Where powoduje iterację po całej kolekcji. W przypadku gdybyś uruchamiał LINQ na przykład używając Entity Frameworka to pewnie data provider przetłumaczyłby to na jednego WHERE w SQLu zamiast jakiegoś podzapytania i różnicy by nie było.

Według mnie te milion wywołań, które sprawdziłeś to wystarczające potwierdzenie w tym przypadku.

edytowany 1x, ostatnio: Fuffu, 2019-06-16 15:00
Mylisz się. W przypadku tłumaczenia lambd na sql nie będziesz po drodze mieć optymalizacji, takie samo zapytanie zapisane inaczej da inny sql. - ŁF 2019-06-18 15:15

Pozostało 580 znaków

2019-06-16 15:07

Tylko i wyłącznie za pomocą : https://benchmarkdotnet.org/

 [Config(typeof(BenchmarkDotNetConfig))]
    public class ArraySum
    {
        private long[] data;
        private long expectedResult; 

        [Params(1000, 10_000, 100_000)]
        public int Size
        {
            get;
            set;
        }

        [GlobalSetup]
        public void GlobalSetup()
        {
            data = new long[Size];
            for (int i = 0; i < Size; ++i)
            {
                data[i] = i % 100;
            }
            expectedResult = data.Sum();
        }

        [Benchmark]
        public IEnumerable<long> Case1()
        {
            var c = data.Where(o => o > 2 && o < 5).ToList();
            return c;
        }  
        [Benchmark]
        public IEnumerable<long> Case2()
        {
            var c = data.Where(o => o > 2).Where(o => o < 5).ToList();           
            return c;
        } 
        [Benchmark]
        public IEnumerable<long> Case3()
        {
            var result = new List<long>(data.Length/10);

            foreach(long i in data)
            {
                if ((i > 2) && (i < 5))
                {
                    result.Add(i);
                }
            }

            return result;
        }         
    }
Method Size Mean Error StdDev Gen 0 Gen 1 Allocated
Case1 1000 6.122 us 5.413 us 0.3058 us 0.4196 - 672 B
Case2 1000 10.937 us 6.525 us 0.3687 us 0.5188 - 824 B
Case3 1000 2.469 us 1.083 us 0.0612 us 0.5455 - 864 B
Case1 10000 66.598 us 151.150 us 8.5403 us 2.7466 - 4330 B
Case2 10000 230.682 us 501.066 us 28.3111 us 2.4414 - 4482 B
Case3 10000 23.826 us 16.956 us 0.9581 us 5.0964 - 8066 B
Case1 100000 980.513 us 873.224 us 49.3388 us 19.5313 - 33136 B
Case2 100000 1,119.897 us 1,055.583 us 59.6424 us 19.5313 - 33304 B
Case3 100000 225.888 us 144.071 us 8.1403 us 35.8887 8.7891 80344 B
Fuffu napisał(a):

ponieważ każde wywołanie Where powoduje iterację po całej kolekcji.

Guzik prawda, wywołanie where w ogóle nie powoduje itreracji po kolekcji, całe linqu jest zbudowane na yieldach, iteracja następuję dopiero wtedy gdy użyjemy jakiegoś operatora powodującego materializację wyników.


#Dżunior React Devloper wanna be#
edytowany 2x, ostatnio: neves, 2019-06-16 15:14
To prawda, że potrzebujemy materializacji wyników, ale czy nie jest tak, że aby je obliczyć potrzebne będą dwie iteracje przy dwóch Where'ach? Być może żyję w błędzie więc wyprowadź mnie z niego jeżeli się mylę. - Fuffu 2019-06-16 15:59
linqu przetwarza dane strumieniowo, najpierw jest brany item1, przepuszczany przez where1, jeśli przejdzie go jest przepuszczany przez where2 i dalej pchany aż do materializacji, dopiero jak item1 zostanie przetworzony do końca, zaczyna się przetwarzanie item2 - neves 2019-06-16 16:18
Tutaj możesz zobaczyć kod źródłowy na przykładzie Where https://github.com/dotnet/cor[...]Linq/src/System/Linq/Where.cs Jak wywołasz Where drugi raz to predykaty są łączone przez &&. - some_ONE 2019-06-16 16:22
Rozumiem. Dzięki za wyjaśnienie :) - Fuffu 2019-06-16 16:25

Pozostało 580 znaków

2019-06-16 16:27
0

Dzięki. Tylko coś mi ten kod powyżej nie chce działać. Nie mam klasy BenchmarkDotNetConfig - wykomentowałem to. Ale przy uruchomieniu dostaje:

Benchmark ArraySum.Case1 returns a deferred execution result (IEnumerable<int64>). You need to either change the method declaration to return a materialized result or consume it on your own. You can use .Consume() extension method to do that.

Zmieniłem IEnumarable na List i działa.

A do czego ta zmienna expectedResult ma służyć?

taki kod miałem pod ręką, więc trochę za dużo mi się skopiowało do przykładu, ta zmienna służyła do sprawdzania czy wszystkie casy zwracały poprawny wynik gdy testowałem w tej klasie różne sposoby na zrównoleglenie wykonywania kodu - neves 2019-06-16 16:33

Pozostało 580 znaków

2019-06-16 16:59
0

Próbowałeś kiedyś używać NCruncha? Głównie jest używany do pokazywania code coverage kodu i łatwiejszego wykrywania błędów, ale ma też funkcję pokazywania ile dokładnie czasu zajmuje poszczególna linijka kodu. Nawet ładnie to koloruje:
NCrunch
Co prawda jest płatny, ale można sobie potestować za darmo przez jakiś czas.

Pozostało 580 znaków

2019-06-17 23:32
0

A czy to przypadkiem nie wygląda tak, że w przypadku EF linq buduje zapytanie dla jednego where:
Select ... Where exp1 && exp2
A dla dwóch where:
Select .. from (select... Where exp1) where exp2

Nie wiem czy dobrze kombinuję.


codebility.pl
Możliwe, w sumie zależy od providera. Ale tutaj raczej chodziło o wydajność czysto po stronie C#, czyli LINQ to Objects. - some_ONE 2019-06-18 10:03
A dlaczego miałby go po prostu nie złączyć? - WeiXiao 2019-06-18 18:20

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0