2015-11-19 15 views
6

Sprawdziłem kod współpracowników i powiedziałem mu, aby ponownie uporządkował porównania boolowskie w następującym predykacie Linq Any ze względu na wydajność. Tak więc biorąc pod uwagęZamówienie ewaluacji C#

public class JobResult 
{ 
    public JobResult(); 

    public string Id{ get; set; } 
    public StatusEnum Status{ get; set; } 
    public string JobType{ get; set; } 
} 

i

IList<JobResult> jobsList = _jobRepository.FetchJobs() 

Zaproponowałem zmianę następujących czynności:

//Exit if there is already a job of type "PurgeData" running 
if (jobsList.Any(job => job.Status == JobStatus.Running //1 
        && job.Id != currentJobId   //2 
        && job.JobType == "PurgeData")) //3 
    return false; 

stać

//Exit if there is already a job of type "PurgeData" running 
if (jobsList.Any(job => job.JobType == "PurgeData"  //3 
        && job.Status == JobStatus.Running //1 
        && job.Id != currentJobId))    //2 
    return false; 

Moje rozumowanie było, że większość miejsc pracy w jobsList zawieść th e test dla JobType, tylko kilka zakończy się niepowodzeniem testu dla Running i tylko jeden zakończy się niepowodzeniem testu dla Id. Jeśli dopasowanie nie powiedzie się, nie ma punktu, który oceniałby pozostałe, a z powodu punktów sekwencyjnych nie wystąpi.

Moje trzy częściowe pytanie brzmi: czy to prawda, czy to jest do udowodnienia prawda i czy istnieje lepsze wyjaśnienie, które mogę przekazać mojemu koledze, dlaczego zamawianie jest dobrym pomysłem?

+3

Należy pamiętać, że porównanie "int" lub "wyliczanie" obejmuje jedną instrukcję maszyny, podczas gdy porównywanie ciągów jest znacznie bardziej skomplikowane. –

+1

Możesz to udowodnić, przenosząc każdą klauzulę do funkcji i wewnątrz tej funkcji do logu lub 'Console.WriteLine' dla examlpe. – DavidG

+0

Położyłabym porównanie łańcuchów na końcu. Myślenie o tym trudniej byłoby stratą czasu. Porównanie dwóch intów raz lub dwa razy nie będzie wąskim gardłem. – SimpleVar

Odpowiedz

7

Moje rozumowanie było, że większość miejsc pracy w jobsList zdać egzaminu na JobType, tylko niewielu będzie zdać egzaminu na bieganie i tylko jeden nie powiedzie się test na Id. Jeśli dopasowanie nie powiedzie się, nie ma punktu, który oceniałby pozostałe, a z powodu punktów sekwencyjnych nie wystąpi. Czy to prawda?

Czy to prawda, że ​​drugi i trzeci predykat nie będą oceniane, gdy pierwszy jest fałszywy? Tak. Twoje rozumowanie jest tam poprawne.

Czy to prawda, że ​​unikanie oceny drugiego i trzeciego orzecznika, gdy pierwszy jest fałszywy, jest wyraźną wygraną? Niekoniecznie, z dwóch powodów.

Po pierwsze, nie wszystkie porównania są równie drogie. Porównywanie ciągów "PurgeData" i "PurgeDatz" wymaga porównania ośmiu znaków przed wycofaniem się; porównywanie liczb całkowitych jest tańsze. Może być w przeciętnym przypadku tańsze, aby uniknąć porównania ciąg, nawet jeśli jest bardziej prawdopodobne, że będzie fałszywe.

Po drugie, pamiętaj, unikanie uruchamiania kodu eliminuje koszt tego kodu, ale trzeba było napisać kod, aby sprawdzić, czy należy unikać drugiego kodu:. Test jest tani, ale nie jest darmowy! Są sytuacje, w których unikanie kodu jest faktycznie droższe niż zwykłe jego uruchamianie.

Zobacz mój ostatni artykuł na ten temat:

http://ericlippert.com/2015/11/02/when-would-you-use-on-a-bool/

jest tam lepsze wyjaśnienie mogę dać moim kolegą, dlaczego zmiana kolejności jest dobrym pomysłem?

Tak. Możesz ustawić metrykę wydajności i realistyczny, ważny dla klienta cel wydajności, możesz wykazać empirycznie, że kod w jedną stronę nie spełnia swojego celu, mierzony zgodnie z metryką, i możesz pokazać empirycznie, że kod spełnia twój cel, gdy piszesz to w inny sposób.

Jeśli tego nie zrobisz, to, co opisujesz, to różnica, której nie możesz zmierzyć i nikt się tym nie przejmuje; Twój kolega miałby całkowitą rację, mówiąc ci, żebyś przestał marnować czas na zmianę kodu, żeby zrobić różnicę, której nie możesz zmierzyć, której nikt nie dba.

+0

Chciałbym, aby więcej ludzi zrozumiało, że próba optymalizacji kodu bez pomiaru/profilowania/celu docelowego jest bezowocnym ćwiczeniem. Gdybym miał pensa za każdym razem, gdy ktoś sugerowałby zmianę tego rodzaju kodu, mogę z łatwością przejść na emeryturę. – SolutionYogi

+0

Istotne punkty @Eric, ale nie napisałem żadnego kodu, aby przetestować cokolwiek. Gdybym wiedział, że optymalizacja jest prawdą w ogólnym przypadku, to mogę tak zawsze układać mój kod w taki sposób. Brakowało mi kosztów porównania strun, ale to dlatego tu jestem ... aby być wykształconym na ogólnie prawidłowy sposób robienia rzeczy. Mogę teraz zastosować to rozumowanie w przyszłości i wiem, że mogę nie oszczędzać żadnych cykli procesora, ale nie marnuję żadnego z nich. – cl0h

4

Moje rozumowanie było, że większość miejsc pracy w jobsList zdać egzaminu na JobType, tylko niewielu będzie zdać egzaminu na bieganie i tylko jeden nie powiedzie się test na Id. Jeśli dopasowanie nie powiedzie się, nie ma punktu, który oceniałby pozostałe, a z powodu punktów sekwencyjnych nie wystąpi.

Tak, || i && operatorzy short-circut w C#. Oznacza to, że nie będą kontynuować oceny innych wyrażeń, jeśli warunek został już spełniony. Biorąc pod uwagę, że JobType ma wartość false, pozostałe predykaty nie zostaną obliczone. Zauważ, że twój kod może mieć dodatkowy narzut, który przeważa nad ponownym uporządkowaniem tych predykatów, takich jak porównanie int vs string (gdzie ten drugi najprawdopodobniej będzie droższy).

Ta kategoria należy do kategorii mikro-optymalizacji, a oprócz wyjaśniania, jakie są zwarcia dla współpracownika, sugeruję przetestowanie tego kodu, aby sprawdzić, czy zapewnia on jakąkolwiek korzyść z wydajności.

+0

Zwarcie jest pojęciem, o którym myślałem. Dzięki – cl0h

+1

Sądzę, że udało ci się uchwycić koncepcję zwarcia kontrastującą z kosztem porównań w miarę dobrze i zwróciłeś uwagę na to, że mogłem skupić się na niewłaściwej sprawie. – cl0h

4

Osobiście nie miałbym nic przeciwko temu. Obciążenie związane z używaniem LINQ to znacznie więcej niż samo porównanie. A jeśli pierwsze orzeczenie okaże się fałszywe, wszystkie inne są ignorowane, więc testowanie najmniej prawdopodobnego pierwszego jest dobrym pomysłem.

Idąc dalej kosztowności oceny, wyliczenia są liczbami całkowitymi, więc porównanie wyliczenia jest tańsze niż ocenianie ciągu znaków jako całości. Druga ocena jest również liczbą całkowitą, więc mniej niż porównywanie ciągu znaków. Myślałem, że string najpierw porównywał długość, więc byłoby to również porównanie całkowite, porównywanie każdej postaci kosztuje więcej. Jeśli więc ciąg jest równy długiemu, w którym jest porównywany, ocena łańcucha jest cięższa niż liczba całkowita. Inaczej nie ma to większego znaczenia.

+0

Kilka przydatnych optymalizacji tutaj. Sugeruję zmianę JobType z ciągu na enum. Wygląda na to, że zapewni to większy wzrost wydajności niż zmiana kolejności. – cl0h

+0

Rzeczywiście. Jeśli to możliwe, to będzie opcja. –

0

Bardzo łatwo jest marnować godziny, optymalizując rzeczy, które nie są wolne.

Jeśli trzeba zapytać, która metoda jest szybsza, nie można stwierdzić różnicy (np. Obie są wystarczająco szybkie).

Kwerendy Linq czasami są konwertowane na SQL, więc prędkość może zależeć od wewnętrznej implementacji bazy danych, która jest poza kontrolą użytkownika.