18/08/2009

Ciekawy przypadek optymalizacji

Home

Ostatnio zajmowałem się optymalizacją aplikacji mapowej do planowania i inwentaryzacji sieci telekomunikacyjnych. Problem polegał na tym, że przy dużej liczbie warstw aplikacja zaczynała działać powoli. Warstwa to taki pojemnik na dane tego samego rodzaju np.: warstwa kabli, warstwa wzmacniaczy itd.

W określonych scenariuszach liczba takich warstw mogła sięgać nawet kilku tysięcy. Zacząłem od wrzucenia aplikacji do profilera (AQTime). Jak to często bywa w takich sytuacjach okazało się, że czas pożera kilka metod, które same z siebie wykonują się bardzo szybko ale są wołane bardzo dużo razy - proporcjonalnie do liczby rastrów. W rzeczywistości dojście to tego o jakie metody chodzi nie było oczywiście takie proste ale nie będę zanudzał szczegółami.

Po pierwsze postanowiłem ograniczyć liczbę wywołań tych metod. Niestety okazało się, że z różnych powodów nie da się tego zrobić. W drugim podejściu postanowiłem, więc ograniczyć czas potrzebny na ich wykonanie. Co ciekawe okazało się, że metody te pozornie prawie nic nie robią czyli zawierają tylko odwołania do właściwości Count czy też indeksera jakiejś kolekcji.

Zanim przejdę dalej wyjaśnię, że jako platformę mapową używam rozwiązań firmy ESRI opartych o technologię COM. W związku z tym konieczne jest użycie mechanizmów interoperacyjności pomiędzy kodem zarządzanym i niezarządzanym. W omawianym przypadku wspomniana kolekcja nie była kolekcją zarządzaną ale wrapper'em na obiekt COM. Postanowiłem, więc zrezygnować z odwołań do tej kolekcji i użyć dobrze znanej klasy List<T>. W praktyce utworzyłem instancję listy zarządzanej istniejącą równolegle do kolekcji niezarządzanej. Elementy do obu kolekcji dodawane są równocześnie, podobnie usuwane. Nie jest to problem ponieważ czynność ta wykonywana jest jednorazowo.

Postępując podobnie w kilku innych miejscach, to znaczy eliminując odwołania do wrapper'ów do obiektów COM, zmniejszyłem czas wykonywania niektórych czynności o połowę! Wydaje mi się, że to niezły wynik szczególnie, że dalsze optymalizację cały czas są możliwe. Wniosek z tego taki, że czasem warto wprowadzić redundancję aby zyskać na wydajności.

5 comments:

Paweł Łukasik said...

Ciekawy tekst. Fajnie jakbyś wrzucił konkretne przykłady kodu opisywanych przypadków.

mawoc said...

W podobnym środowisku pracy (oprogramowanie GIS z obiektami COM, ale nie ESRI), przy podobnych sytuacjach wystarczyło zawsze ograniczać odwołania do obiektów COM, przede wszystkim konstrukcji w stylu: for (int i = 0; i < kolekcjaCOM.Count); i++). Strata czasu na kolekcjaCOM.Count wyjdzie przy pierwszyo odpaleniu AQTime :-). Ale własna manipulacja na geometrii już wymagała użycia własnych klas .NET w celu osiągnięcia akceptowalnej szybkości.

mawoc said...

Dodam jeszcze że czytając tego posta czułem się jakby ktoś patrzył mi w ekran i opisał moje doświadczenia (GIS, COM, AQTime...) :-)

Michał Komorowski said...

Też używam AQTime :) Na początku zmyliło mnie to, że w ogóle nie podejrzewałem takich rzeczy jak kolekcjaCOM.Count. Bardziej spodziewałem się niepotrzebnych operacji wykonywanych wielokrotnie, nieoptymalnego obiektu lub czegoś takiego.

mawoc said...

Najciekawsze przypadki widziałem gdy trzeba było dopisać jakieś metody manipulujące na geometrii. Np. mając łamaną i punkt, znaleźć odcinek 'prostopadły' od punktu do łamanej, lub mając poligon i odcinek przecinający ten poligon, tak przesunąć ten odcinek, by przecią poligon w odpowiednich proporcjach... Takie zabawy .NETem na COMach mocno pokazują koszty marshalingu .NET<->COM. Wtedy redundancja danych bardzo poprawia wydajność.

Post a Comment