03/09/2009

ESRI WPF/Silverlight API

Home

ESRI to lider światowego rynku systemów informacji geograficznej (ang. GIS). Lider przez duże L - Na całym świecie z rozwiązań ESRI korzysta 300 tyś instytucji w tym 2/3 firm z listy Fortune. ESRI to taki Microsoft w świecie GIS'ów :). Czym jednak są systemy GIS'owe?

GIS to w bardzo dużym skrócie system służący do wprowadzania, przechowywania, przetwarzania, analizowania i wizualizowania danych przestrzennych. Upraszczając jeszcze bardziej, a wręcz trywializując chodzi tu po prostu o mapę ;) Niektórzy zapewne pomyślą coś w rodzaju: "Czym ty zawracasz głowę człowieku, przecież jest już Google Maps". Nie obrażając nikogo Google Maps w porównaniu z produktami ESRI to zabawka dla dzieci, którą poważni ludzie się nie zajmują. Patrząc na to z innej strony oferta ESRI (w jednym z kolejnych postów postaram się ją przybliżyć) jest dużo, dużo, dużo bogatsza. Poza tym jest skierowana do zupełnie innego rodzaju odbiorców, którzy poza wizualizacją danych geograficznych chcą je analizować i przetwarzać w celu usprawnienia swoich procesów biznesowych.

Czemu jednak w ogóle o tym piszę? Otóż rozwiązania ESRI są generalnie bardzo drogie i dostępnie dla niewielkiego grona specjalistów, a co z tym związane mało znane. Ostatnio dotarłem jednak do darmowego (dla celów niekomercyjnych) API dla WPF/Silverlight umożliwiającego tworzenie całkiem fajnych aplikacji. Tworzone aplikacje możemy zasilić danymi udostępnianymi przez ESRI poprzez ArcGIS Online. Zasoby udostępnione publicznie nie są tak duże jak w przypadku Google ale można już coś zdziałać przy ich pomocy. Możliwe jest również wykorzystanie map publikowanych przez Microsoft na Bing Maps.

Potrzebne biblioteki można pobrać tutaj, a dokumentację znajdziemy tutaj. Początkowo chciałem opisać prosty przykład użycia ale zrezygnowałem z tego kiedy zobaczyłem tą stronę. Znajdziecie tam kilkanaście bardzo prostych, poglądowych aplikacji. Polecam.

26/08/2009

Przykład tego jak nie należy pisać kodu

Home

Czasami jak patrzę na niektóre kody to zastanawiam się o czym myślał piszący je programista. Ostatnio natknąłem się na kod mniej więcej jak poniżej (zmieniłem nazwy zmiennych, metod itd.). Pomińmy jaka jest jego logika i gdzie został użyty. Proponuję natomiast przyjrzeć się przez chwilę temu fragmentowi i spróbować odpowiedzieć na pytanie co jest nie tak.
...
for (int i = 0; i < values.Length; i++)
{
  if (values[i] != null)
  {
    if (hash[values[i]] != null)
      Process(hash[values[i]]);

    if (hash[values[i]] != null)
    { 
      Process(values[i]);
      Process(values[i], hash[values[i]]);
      hash.Remove(values[i]);
    }
  }
}
...
Mnie uderzyła duża i zupełnie zbyteczna liczba odwołań do zmiennych values (tablica) i hash (Hashtable). Odwołanie do tej pierwszej występuje 8 razy, a do drugiej 5 razy! Po chwili pracy i wprowadzeniu bardzo prostych poprawek kod wygląda tak:
...
for (int i = 0; i < values.Length; i++)
{
  string val = values[i];
  if (val != null)
  {
    object obj = hash[val];
    if (obj != null)
      Process(obj);

    if (obj != null)
    {
      Process(val);
      Process(val, obj);
      hash.Remove(obj);
    }
  }
}
...
Odwołanie do zmiennych values oraz hash występuje tylko 1 raz. Czemu o tym piszę? Odpowiedź jest tak prosta jak wprowadzone poprawki: wydajność. Dodam jescze, że kod ten można jeszcze ulepszyć. W tym poście skupiam się jednak na tej jednej rzeczy.

W tym przypadku poprawiona wersja działa jakieś 3x/4x razy szybciej! Przyznam jednak, że całościowy efekt tej optymalizacji jest niezauważalny z dwóch powodów. Po pierwsze długość używanej w kodzie tablicy i liczba elementów w tablicy hashującej jest generalnie niewielka. Przeważnie nie przekracza kilkudziesięciu, może kilkuset elementów. W takim scenariuszu czas wykonania tego kodu nie przekracza milisekundy. Po drugi częstotliwość użycia tego kodu jest niewielka.

Z drugiej jednak strony programista, który napisał omawiany kod stosuje (zastosował) takie konstrukcje w wielu miejscach. Być może wywołuje jedną, ciężką, metodę wielokrotnie zamiast zapamiętać jej wynik wywołania na później. Przykładów takich można mnożyć.

Reasumując należy uczyć się na cudzych błędach i strzec się takich konstrukcji.

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.

14/08/2009

Site, Site Collection, IIS Web Site...

Home

Oj dużo miałem ostatnio na głowie: ślub, wesele, podróż poślubna :) ale pora wrócić do blogowania. Do tej pory nie miałem do czynie z WSS (Windows SharePoint Services) ale los chciał, że przyjdzie mi poznać również tą technologię Microsoftu. Przygodę w świecie Sharepoint'a postanowiłem zacząć od znalezienia i zebrania serwisów, blogów i książek dotyczących tego zagadnienia. Jak się szybko okazało pierwszym problemem okazała się terminologia. Doświadczony developer WSS skwitowałby to pewnie ironicznym uśmieszkiem ale dla mnie terminologia WSS jest co tu dożo mówić 'zakręcona'. Jedną z przyczyn jest zapewne fakt, że jako developer ASP.Net widząc słowo site od razu przychodzi mi do głowy projekt typu Web Site, podobnie Web Application to również dla mnie typ projektu VS. W nomenklaturze WSS terminy te przyjmują natomiast inne znaczenia. Postanowiłem więc opisać pojęcia, które wprawiły mnie w taką konsternację.
  • IIS Web Site - Punkt wejściowy do serwera IIS. Domyślny site (Default Web Site) oczekuje na żądania HTTP na porcie 80. Nie mylić z projektem typu Web Site w VS. Projekt typu Web Site po zainstalowaniu na IIS widoczny jest najczęściej jako katalog wirtualny.
  • Web Application - IIS Web Site przygotowana do pracy z WSS. Nie mylić z projektem typu Web Application w VS. Projekt typu Web Application po zainstalowaniu na IIS widoczny jest najczęściej jako katalog wirtualny.
  • Virtual Server - W WSS 2.0 oznacza to samo co Web Application.
  • Virtual Directory - Katalog wirtualny. Tworzy podprzestrzeń adresów URL w ramach IIS Web Site w jakiej został utworzony. Katalogi wirtualne mapują się na fizyczne lokalizacje na dysku.
  • Site Collection - Podstawowa jednostka organizacyjna służąca do grupowania i zarządzania site'ami. Web Application może składać się z wielu takich kolekcji. Na poziomie programistycznym reprezentowana przez klasę SPSite.
  • Site - W WSS 3.0 to porostu pojemnik na jakąś zawartość: listy, biblioteki dokumentów czy też inne pojemniki. Każdy site musi należeć do jakiejś kolekcji. Na poziomie programistycznym reprezentowany przez klasę SPWeb. W WSS 2.0 termin ten oznacza to samo co Site Collection. Stąd w WSS 3.0 klasa reprezentująca kolekcję site'ów nazywa się SPSite, a nie SPSiteCollection.
  • Web - Stary termin z WSS 2.0 oznaczający to samo co site w WSS 3.0. Stąd w WSS 3.0 klasa modelująca site nazywa się SPWeb, a nie SPSite.
  • Top-level Site - Główny pojemnik (site) w ramach kolekcji pojemników (site collection).
  • Root Web - W WSS 2.0 oznacza to samo co Top-level Site.

22/07/2009

Czemu należy używać właściwości SyncRoot?

Home

SyncRoot to właściwość zdefiniowana na poziomie interfejsu ICollection służąca do synchronizowania operacji wykonywanych na kolekcjach przy pomocy słowa kluczowego lock lub jawnie przy pomocy monitora. Czemu jednak należy używać tej właściwości zamiast instancji kolekcji, czyli czemu zalecany jest taki kod:
lock(list.SyncRoot)
{
   ...
}
A nie taki?
lock(list)
{
   ...
}
Kiedy postawiłem sobie to pytanie okazało się, że odpowiedź nie jest dla mnie oczywista. Wizyta w dokumentacji MSDN nic nie pomogła bo udzielone wyjaśnienie należy do grupy tych, które stają się jasne i oczywiste jak już znasz prawidłową odpowiedź ;).

Spędziłem trochę czasu zastanawiając się nad tą kwestią oraz szperając po sieci i doszedłem do następujących wniosków. Należy używać właściwości SyncRoot ponieważ zapewnia ona centralny, dobrze określony, bezpieczny punkt synchronizacji dla kodu zewnętrznego używającego kolekcję jak i dla kodu wewnętrznego kolekcji. Inaczej mówiąc zapewnia to, że każdy kod używa tego samego obiektu do synchronizacji. Bezpieczny w tym sensie, że bardzo łatwo można stwierdzić kto i kiedy uzyskuje dostęp do obiektu synchronizacyjnego - wystarczy ustawić pułapkę w getter-ze właściwości. Synchronizacja bezpośrednio przy użyciu instancji kolekcji czy też na obiekcie this może natomiast doprowadzić to bardzo trudnych do wykrycia zakleszczeń ponieważ nie będzie można łatwo określić kto i w jakim momencie blokuje obiekt. Z drugiej strony jeśli taka semantyka SyncRoot jest z jakiegoś powodu niepożądana kolekcje wydziedziczone z kolekcji bazowej mogą dostarczyć własnej implementacji tej właściwości.