Strona używa cookies (ciasteczek). Dowiedz się więcej o celu ich używania i zmianach ustawień. Korzystając ze strony wyrażasz zgodę na używanie cookies, zgodnie z aktualnymi ustawieniami przeglądarki.    X

O wydajności w aplikacjach .NET

Coraz częściej użytkownicy vortalu dobreprogramy opisują ulubione narzędzia programistyczne i wykorzystywane do różnorakich celów języki programowania. Takie wpisy budzą całkiem spore zainteresowanie wśród czytelników dobrychprogramów. Niemniej czytając komentarze pod wybranymi wpisami odnosi się wrażenie nieustannej bitwy ideologicznej, której stronami są zwolennicy tradycyjnych rozwiązań tj. programowania natywnego z wykorzystaniem języków pokroju C/C++ oraz zwolennicy kodu zarządzanego przez maszyny wirtualne (np. JVM dla Javy, CLR dla C# i pozostałych).

Nierzadko podejmowaną kwestią w tych burzliwych dyskusjach jest wydajność tworzonych aplikacji w zależności od wybranej technologii. Ech... ta wydajność... termin określający niepotrzebną bolączkę programistów naszych czasów.

W codziennej pracy wykorzystuję kilka narzędzi programistycznych z .NET'em na czele. Zaryzykuję stwierdzenie, że w dobie komputerów o tak znaczących mocach obliczeniowych i doskonale zoptymalizowanych procesów kompilacji i wykonywania kodu zarządzanego aplikacje działające w ramach platformy .NET nie ustępują tradycyjnym rozwiązaniom. Przynajmniej w przeważającej większości przypadków zastosowań.

Musicie bowiem zgodzić się z tym, że - biorąc pod uwagę wszelkie udogodnienia wprowadzone w ramach kodu zarządzanego - responsywność oprogramowania mierzona subiektywnym odczuciem użytkownika programu nie różni się od tej, którą oferuje nam świat "niebezpiecznych wskaźników i wycieków pamięci" (celowo w cudzysłowie).

Jedno ale

Taki stan rzeczy uzyskamy tylko pod warunkiem bezwzględnego przestrzegania zasad, zaleceń czy też inaczej wzorców i praktyk określonych przez producenta danej platformy programistycznej - tu Microsoftu. Potocznie konkludując, jeśli bardziej niż treści oferowane przez MSDN cenisz niby-programistyczne podejście typu google-kopiuj-wklej-it works! to sorry, ale demonami szybkości twoje aplikacje nigdy nie będą, a i przekonań się złych nabawisz.

Studia, te wyższe i te niższe, z całą swą przebojowością mają z natury nie uczyć, lecz wskazać drogę właściwego korzystania ze źródeł, wszelakich, nie tylko tych określonych w podstawie programowej. Jako programiści popełniamy błędy wielokrotnie, ale błędem kardynalnym jest niechęć do korzystania z ogólnodostępnej bazy wiedzy oferowanej przez producenta lub uznawanie jej za zbędną.

Niniejszy tekst rozpoczyna cykl wpisów traktujących o wydajności w aplikacjach .NET. Bardzo skromnie i na przykładach postaram się przybliżyć wam wybrane, lecz z całą pewnością nie wszystkie, zagadnienia wpływające na wydajność kodu zarządzanego. Jeśli gdziekolwiek się pomylę, poprawcie mnie. A jeśli wykorzystujecie inne technologie (np. Java), spróbujcie przenieść stosowną część przedstawionych tu treści na swoje ulubione platformy i poinformujcie nas o swoich rezultatach.

Na wstępie rozważań warto by określić na czym, pod pojęciem wydajności, najbardziej nam zależy. Niech na potrzeby niniejszego cyklu wskaźnikiem tym będzie szybkość wykonywania kodu. Aby umożliwić wszystkim zainteresowanym wykonywanie podobnych pomiarów na niedotnetowych platformach działających pod kontrolą systemu Windows, jako podstawowe narzędzie pomiarowe wykorzystam funkcję QueryPerformanceCounter oraz QueryPerformanceFrequency dotępne w Win32 API. Dokładność QueryPerformanceCounter jest rzędu nanosekundy. Zbliżoną do niej funkcjonalność w środowisku .NET Framework 2.0 i wyżej zapewnia klasa StopWatch w przestrzeni nazw System.Diagnostics (ekhm, dokładnie taką samą).

W programowaniu unikam mieszania języków polskiego i angielskiego, dlatego trzymam się konwencji stosowania angielskich nazw dla klas, obiektów i innych reprezentujących kluczowe elementy kodu źródłowego.

Prosta implementacja QueryPermormanceCounter w kodzie zarządzanym (C#), czyli tzw. klasa opakowująca (prawda, że "wrapper" brzmi lepiej?), którą będziemy notorycznie wykorzystywali w przyszłości wygląda następująco:

using System; using System.Text; using System.ComponentModel; using System.Security; using System.Runtime.InteropServices; namespace TestPerformance1 { class Program { static void Main(string[] args) { PerformanceCounter counter = new PerformanceCounter(); counter.Start(); Console.WriteLine("Test wydajności...\n"); counter.Stop(); Console.WriteLine("Czas trwania testu:\n{0:F3} ns\n{1:F3} ms\n", counter.ElapsedNanoseconds, counter.ElapsedMiliseconds); } } class PerformanceCounter { [DllImport("kernel32.dll"), SuppressUnmanagedCodeSecurity] private static extern bool QueryPerformanceCounter(out long lpPerformanceCount); [DllImport("kernel32.dll")] private static extern bool QueryPerformanceFrequency(out long lpFrequency); private long start; private long stop; private long frequency; Decimal nanoMultiplier = new Decimal(1.0e9); Decimal miliMultiplier = new Decimal(1.0e3); public PerformanceCounter() { // Więcej informacji na: // http://msdn.microsoft.com/en-us/library/windows/desktop/ms644905(v=vs.85).aspx if (QueryPerformanceFrequency(out frequency) == false) throw new Win32Exception(); } public void Start() { QueryPerformanceCounter(out start); } public void Stop() { QueryPerformanceCounter(out stop); } public double ElapsedNanoseconds { get { return (((double)(stop - start) * (double)nanoMultiplier) / (double)frequency); } } public double ElapsedMiliseconds { get { return (((double)(stop - start) * (double)miliMultiplier) / (double)frequency); } } } }

Sprawdźcie czy działa, pobawcie się. Zwróćcie uwagę na atrybut SuppressUnmanagedCodeSecurity. Niebawem wykorzystamy naszą klasę w bardziej racjonalny sposób. 

porady programowanie

Komentarze

0 nowych
  #1 16.02.2012 09:13

Zainteresowanie takimi wpisami budzi całkiem spore zainteresowanie... :)

alucosoftware   7 #2 16.02.2012 10:42

hmm, to na pewno dlatego, że synek mi skakał po głowie i z uśmiechem mówił "klaaawiaatuuuraa, daaaj"

PcSA   4 #3 16.02.2012 12:47

Będzie ciekawie :) Czekam na kolejne wpisy. O profilowaniu kodu też wspomnisz?

  #4 16.02.2012 20:06

artykuł ciekawy, jednak po co taka klasa skoro Stopwatch świetnie się sprawdza.
Nie znam C++ ale chętnie zapoznałbym się z testami c++owych odpowiedników Dictionary List, czyli np szybkość szukania, iteracji itp.

Nie rozumiem (tak jak Ty) tego całego zamieszania wokół wydajności. Jakość kodu i algorytmów ma większy wpływ na wydajność niż sama technologia (sic!) Można przecież napisać kod w c++ i go skutecznie skoncić, że bedzie wolniejszy od Żółwia LOGO.

djfoxer   17 #5 16.02.2012 20:55

Kiedy następna część i ile ich planujesz? :) Narazie wstęp wydaje się ciekawy :)

adii_22   4 #6 16.02.2012 21:04

Dobry wpis :) Czekam z niecierpliwością na kolejne części.

adii_22   4 #7 16.02.2012 21:04

Dobry wpis :) Czekam z niecierpliwością na kolejne części.

alucosoftware   7 #8 16.02.2012 21:41

@PcSA
O wszystkim wspomnę w swoim czasie :)

@djfoxer
Dokładnej ilości nie planuję, ale póki będzie zainteresowanie - jak najdłużej. takie odświeżanie pamięci pozwala spojrzeć na niektóre sprawy z całkiem nowej perspektywy.

Frankfurterium   9 #9 16.02.2012 21:47

Ja to się tylko dziwię, że wpisu nie namierzyli jeszcze znawcy technologii wszelakich i nie rozpętał się shitstorm... Tzn. konstruktywna i wielce ważna dyskusja, który język jest najlepszy i co komu najszybciej chodzi :P

kostek135   8 #10 16.02.2012 22:04

Przyczepie się do kilku kwestii.
W językach z wirtualnymi maszynami też mogą nastąpić wycieki. GC usunie obiekt, dopiero gdy nie ma do niego żadnej referencji. Poza tym imho lepsza jest kontrola, bo wiesz co robisz (jeśli wiesz).

Nie wiem czy dyskusje nad tym mają w ogóle jakiś sens. Np. w php jest np. exec, można odebrać dane w php przesłać jako argumenty do wykonania programu C i odebrać wynik, który jakoś tam wyświetlimy. Zapewne i w C# i w javie tak się da.

Na zakończenie, i tak sądzę, że bolączką jest nie tyle język, co leżąca i kwicząca algorytmika. Np. widziałem problem, który ktoś rozwiązywał problem w czasie wykładniczym, gdy można go bardzo łatwo obliczyć liniowo stosując programowanie dynamiczne. W sumie to nic nie pomoże jak się nie optymalizuje swoich działań.

kostek135   8 #11 16.02.2012 22:11

rozwiązywał problem* miało być algorytmem ;)

revcorey   6 #12 16.02.2012 22:27

Teraz ktoś od javy(tzn. ktoś kto na co dzień w javie pisze) i przepisywać :D Dodać do tego jeszcze wersję w c++ i będzie gitara :D
@kostek135
Jak ktoś ma problemy z zarządzaniem pamięci wraz z gc to jeszcze gorzej będzie z jak zacznie operować wskaźnikami(ale są jeszcze wskaźniki inteligentne). Swoją drogą w c++ też są GC( np.Boehm).

Ja powiem tak ja bym siepał tak:
-Ważna przede wszystkim wydajność, rozsądne zużycie pamięci c++ (+qt4)
-wydajność mało ważna a chcemy pisać bardzo szybko to python(pisałem kiedyś prymitywną obsługę skype i jeszcze parę rzeczy to mini projektu i kod do tego powstawał sam z siebie :) )
-wydajność mało ważna(Ale lepiej wyższa niż w python) ale hmm nie pasuje nam też sam python to jvm/.net

kostek135   8 #13 16.02.2012 22:36

"Jak ktoś ma problemy z zarządzaniem pamięci wraz z gc to jeszcze gorzej będzie z jak zacznie operować wskaźnikami(ale są jeszcze wskaźniki inteligentne)."

Nie zgodzę się z tym. Mając np. 10 referencji do jednego obiektu, trzeba pamiętać, żeby usunąć wszystkie 10 i dopiero GC łaskawie usunie obiekt. Dzięki np. delete mam dziesięć odniesień wybieram jedno usuwam obiekt, a na resztę leje (tak wiem wiszące wskaźniki, ale nie rozumiem kogoś kto odwołuje się do obiektu, który usunął - chyba, że skleroza).
Oczywiście wiadomo, że początkujący będzie miał łatwiej, bo z reguły nie narobi tych odniesień bóg wie ile, ale i tak chodziło mi, że można się naciąć myśleniem GC załatwi sprawę.

alucosoftware   7 #14 16.02.2012 23:05

@kostek135
Entuzjasta jednej tylko technologii często wydaje krzywdzące opinie na temat innych mu nieznanych. Tak właśnie mógłby pomyśleć świeżo upieczony dotnetowiec, olśniony nowinkami i zaprezentowaną mu funkcjonalnością, na temat tradycyjnej drogi programowania. Dlatego też część zdania umieściłem w cudzysłowie.

Tematu GC nie podnoszę, bynajmniej nie w tej chwili. Tym niemniej stosowanie pewnej konwencji w projektowaniu aplikacji w .NET'ie usprawnia działanie GC i wpływa na wydajność nie tyle samej aplikacji co całej platformy. Dyskusja na ten temat ma więc duży sens.

alucosoftware   7 #15 17.02.2012 11:46

@.netowiec
"Aby umożliwić wszystkim zainteresowanym wykonywanie podobnych pomiarów na niedotnetowych platformach działających pod kontrolą systemu Windows"...

Podstawową zasadą w nauce jest wykorzystywanie tych samych narzędzi w celu powielania i ewentualnego korygowania doświadczeń. Użytkownik Javy także może wywołać w/w funkcję, a ewentualny narzut czy też koszt wywołania tej funkcji na platformie JVM będzie porównywalny. Z drugiej strony, skąd osoba spoza środowiska .NET ma wiedzieć w jaki sposób zaimplementowana jest klasa StopWatch oraz jaki będzie odpowiednik na wykorzystywanej platformie.

Co do drugiej kwestii - właśnie o jakości kodu jest (i będzie) tu mowa.

djfoxer   17 #16 18.02.2012 12:10

kiedy następny wpis?

alucosoftware   7 #17 18.02.2012 12:16

@djfoxer
Może uda mi się znaleźć wolną chwilę wieczorem. Pozdrawiam