24/12/2014

Życzenia świąteczne

Home

Kolejne Święta Bożego Narodzenia już tuż tuż. W tym roku czekam na nie jeszcze bardziej niż zwykle, bo to pierwsze wydarzenie tego rodzaju dla naszej córeczki. Wszystkim czytającym mojego bloga i nie tylko życzę dużo radości w gronie bliskich Im osób, a w Nowym Roku ciekawych wyzwań zawodowych oraz zapału do nauki i doskonalenia się.

Serdecznie Pozdrawiam,
Michał Komorowski

21/12/2014

Czego prawdopodobnie nie wiedzieliście o Excel'u

Home

Sądzę, że wielu z Was otarło się na studiach o programowanie liniowe oraz algorytm sympleks. Ja uczyłem się o tym na przedmiocie zwanym w skrócie POBO, co rozwija się dumnie brzmiące Podstawy badań operacyjnych. Od czasów studiów nie zajmowałem się tym zagadnieniem, aż do dzisiaj. Pomagając siostrze w rozwiązywaniu zadań na studia dowiedziałem się o możliwościach Excel'a, których w ogóle nie byłem świadomy, a są naprawdę super i każdy ma do nich dostęp. Mam tutaj na myśli dodatek Solver, który, między innymi, implementuje algorytm sympleks w bardzo przystępnej formie. Tyle tytułem wstępu. Spójrzmy na prosty przykład.

Zaczynamy od uruchomienia Excel'a. Następnie klikamy tą fikuśną okrągłą ikonę w lewym górnym roku okna i wybieramy Opcje programu Excel. Dalej przechodzimy do zakładki Dodatki i klikamy przycisk Przejdź.



W oknie, jakie się pojawi, wybieramy Dodatek Solver i zatwierdzamy.



Po zatwierdzeniu w zakładce Dane na wstążce pojawi się nowa opcja.



Teraz spróbujmy rozwiązać przykładowe proste zadanie. Załóżmy, że mamy 5 fabryk i chcemy znaleźć lokalizację centrum dystrybucyjnego tak aby suma odległości od wszystkich fabryk była minimalna. Dodatkowe ograniczenie jest takie, że odległość od każdej z fabryk nie może być większa niż 60. Położenia fabryk podane są we współrzędnych kartezjańskich. Odległość pomiędzy fabrykami, a centrum obliczamy przy pomocy standardowego wzoru. Sytuacja początkowa wygląda tak. Dla ułatwienia naniosłem położenia fabryk i początkowe położenie centrum na wykres.



Teraz uruchamiamy Solver. Jako komórkę celu wybieram pole z sumą odległości i zaznaczam, że tą wartość chcę minimalizować. Jako komórki zmieniane wybieram współrzędne centrum. Dodajemy też ograniczenie na odległość każdej z fabryk od centrum. Na koniec uruchamiam obliczenia i klikam Rozwiąż.



Wynik końcowy wygląda w następujący sposób:



To tylko wierzchołek góry lodowej. Dodatek Solver ma dużo większe możliwość i wiele opcji. Można go wykorzystać do harmonogramowania, zdefiniować wiele ograniczeń, ustalić maksymalny czas obliczeń, dokładność uzyskanego wyniku i wiele więcej. Sądzę, że warto sobie zapamiętać, że Excel ma takie możliwości i w razie potrzeby doczytać i douczyć się jak z tego korzystać.

08/12/2014

Wykrywanie nieużywanych elementów bibliografii

Home

Moim zdaniem LaTeX ma wspaniałą obsługę cytowań i bibliografii. Jest to jeden z powodów, dla którego tak lubię go używać np.: bardzo łatwo znaleźć cytowania w formacie rozumianym przez LaTeX'a, nie musimy martwić się o formatowanie, odpowiednie posortowanie czy numerowanie.

Domyślnie jest dodamy jakąś pozycję do bazy danych odnośników literaturowych, a jej nie zacytujemy to w finalnym dokumencie wygenerowanym przez LaTeX'a (np.: PDF) zostanie ona pominięta. Może to być zachowanie pożądane lub nie. Jeśli nie jest z pomocą przychodzi komenda \nocite, w szczególności jej forma \nocite{*}, która powoduje, że cokolwiek dodamy do spisu odnośników to znajdzie się to w finalnym dokumencie w sekcji z bibliografią. W pewnym momencie możemy jednak chcieć uporządkować bazę odnośników i sprawdzić co używamy, a czego nie. Przy dużym dokumencie, z dziesiątkami lub setkami cytowań nie jest to sprawa oczywista.

W takiej sytuacji z pomocą przychodzi, odkryty przeze mnie ostatnio, pakiet refcheck. Po jego włączeniu dla każdego nieużywanego elementu bibliografii, ale także dla każdej nieużywanej etykiety zostanie wygenerowane ostrzeżenie. Dodatkowo nieużywane elementy zostaną oznaczone w wygenerowanym dokumencie przy pomocy etykiet na marginesie dokumentu.

Kolejny raz okazuje się, że czegokolwiek bym sobie nie zamarzył to istnieje już pakiet, który to zapewnia :)

05/12/2014

Latex i drzewa

Home

Wczoraj już późno w nocy zamarzyło mi się umieścić w dokumencie tworzonym w LaTeX'u rysunki drzew binarnych. Początkowo myślałem o narysowaniu ich w jakimś programie graficznym, a następnie wyeksportowaniu do jpg z czym LaTeX już sobie poradzi. Potem pomyślałem jednak, że to nie ma sensu bo z pewnością już ktoś miał taki problem i stworzył odpowiedni pakiet gotowy do użycia. Nie pomyliłem się. Bardzo szybko znalazłem pakiet qtree i już 5 minut później miałem w swoim artykule piękne drzewka. Przykład użycia:.
\Tree[.{L0 - Root} 
 [.{L1 - Left child} 
  [.{L2 - Left child} ] 
  [.{L2 - Right child} ]]
 [.{L1 - Right child} ]]
Efekt końcowy wygląda natomiast następująco:



LaTeX z pewnością jest trudniejszy w użyciu niż taki Word, ale jak już się go poznasz to zrobisz wszystko. Społeczność około LaTeX'owa stworzyła tyle różnych pakietów, że nie pozostaje nic innego jak brać i korzystać.

02/12/2014

Zabawy z domenami aplikacyjnymi

Home

Proponuję zabawę z serii co zostanie wypisana na ekran. Mamy dwie klasy jak poniżej. Pierwsza z nich przekazywana jest pomiędzy domenami przez referencję, a druga przez wartość. Interfejs ITest ma charakter pomocniczy i nie ma znaczenia.
public interface ITest
{
   int Value { get; set; }
   void Start();
}

public class MarshalByRefObjectClass : MarshalByRefObject, ITest
{
   public int Value { get; set; }

   public void Start()
   {
      Console.WriteLine(AppDomain.CurrentDomain.FriendlyName);
      Value++;
   }
}

[Serializable]
public class MarshalByValueClass : ITest
{
   public int Value { get; set; }

   public void Start()
   {
      Console.WriteLine(AppDomain.CurrentDomain.FriendlyName);
      Value++;
   }
}
Mamy również następujący kod, w którym testuję jak zachowuja się:
  • Obiekty przekazywane przez wartość i przez referencję.
  • Utworzone w bieżącej (przy pomocy konstruktora) oraz w innej domenie (przy pomocy AppDomain.CreateInstanceFromAndUnwrap).
  • Przy wywołaniu na nich metody bezpośrednio oraz przy pomocy AppDomain.Callback.
Co daje łączenie 2 x 2 x 2 = 8 możliwości. Do testowania używam takiej metody pomocniczej:
private static void Test(AppDomain app, ITest test, bool doCallBack)
{
   if (doCallBack)
      app.DoCallBack(test.Start);
   else
      test.Start();

   Console.WriteLine(test.Value);
}
A to właściwy test, w którym najpierw tworzę obiekty przekazywane przez wartość/referencję lokalnie i w nowej domenie. Następnie wywołuję dla nich metodę Start i odczytuję właściwość Value.
var app = AppDomain.CreateDomain("TestDomain");

var asm = Assembly.GetExecutingAssembly();
var byRef = new MarshalByRefObjectClass();
var byRef1 = new MarshalByRefObjectClass();
var byRef2 = (MarshalByRefObjectClass)app.CreateInstanceFromAndUnwrap(asm.CodeBase, typeof(MarshalByRefObjectClass).FullName);
var byRef3 = (MarshalByRefObjectClass)app.CreateInstanceFromAndUnwrap(asm.CodeBase, typeof(MarshalByRefObjectClass).FullName);

var byValue = new MarshalByValueClass();
var byValue1 = new MarshalByValueClass();
var byValue2 = (MarshalByValueClass)app.CreateInstanceFromAndUnwrap(asm.CodeBase, typeof(MarshalByValueClass).FullName);
var byValue3 = (MarshalByValueClass)app.CreateInstanceFromAndUnwrap(asm.CodeBase, typeof(MarshalByValueClass).FullName);

Test(app, byRef, true);
Test(app, byRef1, false);
Test(app, byRef2, true);
Test(app, byRef3, false);

Test(app, byValue, true);
Test(app, byValue1, false);
Test(app, byValue2, true);
Test(app, byValue3, false);
Pytanie brzmi co zostanie wypisane na ekranie? Pokaż/Ukryj odpowiedź

Obiekty przekazywane przez referencję wypiszą na ekran nazwę domeny w jakiej zostały utworzone. Nawet jeśli wywołanie jest inicjowane w innej domenie to zostanie przekazne do domeny orginalej ponieważ pracujemy z proxy do obiektu. W przypadku obiektów przekazywanych przez wartość na ekran zostanie wypisana nazwa domeny w jakiej następuje wywołanie.

Jeśli chodzi o wartość wypisaną na ekran to obiekty przekazywane przez referencję zawsze wypiszą ten sam wynik = 1 ponieważ zarówno wywołanie metody Start jak i odczyt Value dotyczy tego samego obiektu. W przypadku obiektów przekazywanych przez wartość w niektórych przypadkach możemy otrzymać zero. Stanie się tak kiedy wywołanie Start nastąpi w innej domenie niż ta, w której odczytujemy właściwość Value. A dzieje sie tak ponieważ obie czynność dotyczą de facto innych obiektów.
Sandbox.vshost.exe
1
Sandbox.vshost.exe
1
TestDomain
1
TestDomain
1
TestDomain
0
Sandbox.vshost.exe
1
TestDomain
0
Sandbox.vshost.exe
1

25/11/2014

Trochę o wynagradzaniu w IT 2

Home

W tym poście będę kontynuował tematykę wynagradzaniu programistów i napiszę o tzw. bonusach na wejście oraz pakietach akcji (znanych też pod nazwą RSU - Restricted Stock Unit). W Polsce są to rzeczy, powiedziałbym, egzotyczne. O pakietach akcji to pewnie jeszcze sporo osób słyszało, ale o bonusie na wejście to pewnie mało kto.

Tak jak napisałem w poprzednim poście, obie te rzeczy są dodatkiem do systemu poziomów. Sprawiają, że jest bardziej elastyczny i dają pole do negocjacji, czego w systemie poziomów nie ma. Bonus na wejście, po pierwsze, pozwala przyciągnąć pracownika, jeśli goła pensja wynikająca z poziomu jest za niska, a jednak chcemy go zatrudnić. Bonus na wejście wypłacany jest w całości z pierwszą pensją albo proporcjonalne co miesiąc przez 1 lub 2 lata. Czasami jest tak, że w pierwszy roku bonus wynosi X, a w drugim mniej. Bonus potrafi stanowić bardzo ważną część pensji, nawet 30%!

Ktoś zapyta, a po co taki bonus skoro od razu można dać więcej? No właśnie nie można, bo system poziomów jest po to, aby się go trzymać. Jeśli robimy wyjątki to nie ma on sensu. Sensu nie ma też sztuczne zawyżanie poziomu, to byłoby jeszcze gorsze. W tym momencie dochodzimy do drugiej funkcji bonusu. Tak jak napisałem jest on czasowy i stanowi ważną część pensji. Po upływie 2 lat potencjalnie możemy więc ''stracić'' kawał grosza, o ile się nie wykażemy i nie awansujemy na kolejny poziom. Czyli ten bonus w założeniu działa dodatkowo jako motywator.

Teraz trochę o akcje. Ten element wynagrodzenia ma za zadanie związać pracownika z firmą. Chodzi o to, że na wejściu dostajesz na przykład pakiet 50 akcji, ale nie możesz ich od razu sprzedać. Dopiero po upływie 1 roku uzyskujesz dostęp na przykład do 10% z nich, po upływie 2 roku kolejne 15%, a w każdym kolejnym roku kolejne 25%. Systemy różnią się w zależności od firmy. Co więcej, co roku możemy dostać kolejny pakiet akcji. Załóżmy, że na wejściu dostaliśmy 50 akcji, a potem co roku po 50. Pracujemy już 5 i do tej pory nie sprzedawaliśmy niczego, a więc na koniec 5 roku mamy przypisanych 250 akcji i według moich obliczeń możemy sprzedać 170 z nich. A, jeśli pracujemy dla takiego Google, będzie to stanowić na dzisiaj około 90 tysięcy $, a akcje warte kolejne 42 tysiące $ będą na nas czekać i wciąż możemy dostawać kolejne porcje.

Z tego co wiem, zdarza się tak, że niektórzy pracownicy dostają co roku w akcjach nawet więcej niż ich pensja. Zauważcie, że celowo w pierwszych latach dostaje się mało akcji, aby ich nie marnować na pracowników, którzy się nie sprawdzą lub szybko odejdą. Równocześnie odchodząc z pracy po kilku latach mamy świadomość, że tyle, a tyle akcji na nas jeszcze czeka i jak tu zmienić pracę w takiej sytuacji ;)

Co o tym myślicie? Ja mam mieszane uczucia, szczególnie jeśli chodzi o bonus na wejście. Z drugiej strony to pewnie dlatego, że jestem przyzwyczajony do konkretnej kwoty w umowie, którą staram się maksymalizować. Przy systemie z bonusem na wejście i dodatkowymi akcjami bazowa pensja może być niższa, niż gdyby tych bonusów nie było i dochodzi też element niepewności. Jak zawsze jest coś za coś.

20/11/2014

Trochę o wynagradzaniu w IT

Home

W Polsce system wynagrodzeń w IT przeważnie wygląda tak. Przychodzi się do firmy i na wejściu dostaje się tyle, ile wynegocjowało się na początku. W niektórych firmach co roku można też liczyć na wyrównanie inflacyjne (z reguły kilka procent), a jeśli się posiedzi w firmie dłużej lub awansuje to jest szansa na większą podwyżkę. Czasami jest też tak, że pensje są zamrożone nawet prze kilka lat. Według mojego i ludzi z jakim rozmawiałem doświadczenia o dużą podwyżkę najłatwiej zmieniając pracę. Dodatkowe benefity to przeważnie opieka medyczna i karta Multisport lub podobna. Bywają też inne, ale, moim zdaniem, jeśli już są, to pełnią rolę wisienki na torcie niż czegoś co mogłoby rzeczywiście kogoś skusić. Oczywiście szczegóły zależą od firmy, ale moim zdaniem ten system jest mocno nieokreślony i bardzo dużo zależy od umiejętności negocjowania i momentu, kiedy przyszło się do firmy.

Mi do gustu przypadł system stosowany w niektórych, bo w cale nie wszystkich, firmach na zachodzie. Zacznę od tego, że opiera się na systemie poziomów, a od zdobytego poziomu bezpośrednio zależy nasza pensja. Nie jest to regułą, ale ja akurat spotkałem się ze skalą zaczynającą się w okolicach 8 oraz kończącą się w okolicach 20 i zawsze się zastanawiam z czego to wynika. Nawet o to pytałem, ale odpowiedzi były, jak dla mnie, mgliste. Na przykład spotkałem się z stwierdzeniem, że z powodów psychologicznych po poziom 1 to brzmi źle.

Ostatnio, całkiem przez przypadek, natknąłem się na kilka artykułów na ten temat napisanych, między innymi, przez Joel'a Spolsky'ego jednego z założycieli portalu stackoverflow.com. W firmie Joel'a algorytm liczenia poziomu jest funkcją 3 zmiennych tj. doświadczenia (liczonego w latach), umiejętności (ocenianych w zakresie 0-6) oraz zakresu obowiązków (również ocenianego w zakresie 0-6). A możliwe kombinacje zostały ujęte w taką tabelkę:



Ważne jest to, że pracowniczy na danym poziomie zarabiają tyle samo względem kwoty bazowej, która aktualizowana jest co jakiś czas. Czyli na przykład na poziomie dziewiątym zarabiasz X, na dziesiątym X+10%, a na jedenastym X+20% itd. Kwota X nie podlega dyskusji. Jeśli chce się zarabiać więcej to najłatwiej osiągnąć to przez zwiększenie swoich umiejętności. Zauważcie, że według powyższej tabelki wraz z kolejnymi latami doświadczenia poziom rośnie powoli w porównaniu do wzrostu umiejętności i zakresu obowiązków.

Istotne jest to, że osiągniecie maksymalnego poziomu nie oznacza, że programista przestaje być programistą i staje się menadżerem. Wprost odwrotnie, to oznacza, że jest wyjebistym programistą i będzie nadal robił to w czym jest najlepszy. Natomiast jak zaczynasz bez żadnego doświadczenia to dostajesz co prawda najniższy poziom, ale po pierwszym roku możesz skoczyć o 5 oczek do przodu, czyli tyle co ma ktoś z 15 latami doświadczenia, ale nie jest tak dobry jak ty.

Jak dla mnie takie postawienie sprawy to super rzecz. Każdy wie, na co może liczyć i mniej więcej przewidzieć, jak będzie wyglądała jego pensja w przyszłości. Można się pewnie czepiać, że ocena umiejętności jest subiektywna, że ktoś może nas nam zaniżyć ocenę... Ja na taki zarzut odpowiem tak: nie ma systemu idealnego i uważam, że ten jest dużo lepszy od tego co opisałem na początki postu. Natomiast jeśli uważasz, że ktoś może Ci celowo zaniżyć ocenę to lepiej bierz nogi za pas i zmień pracę.

Co o tym myślicie? Czy taki system mógłby przyjąć się w Polsce?

Na koniec trochę linków:
W kolejnym poście napiszę o systemie bonusów na wejście oraz opcji na akcje, które stanowią ważne uzupełnienie czystego systemu poziomów, a są bardzo mało znane w Polsce.

11/11/2014

Domeny aplikacyjne i wątki

Home

Jakiś czas temu odpowiedziałem na pytanie na stackoverflow.com dotyczące kończenia pracy wątków z zewnętrznej biblioteki w sytuacji kiedy powinny już zakończyć pracę, a jednak tego nie zrobiły. Moja odpowiedź została co prawda skrytykowana, zresztą słusznie, ale dzięki temu dowiedziałem się rzeczy, która mi wcześniej umknęła.

W mojej odpowiedzi zasugerowałem, że skoro z jakiegoś powodu musimy użyć zewnętrznej biblioteki i ona nie działa to ja bym ją załadował do oddzielnej domeny aplikacyjnej i w tej nowej domenie uruchomił też obliczenia (wątki). Taką domeną można natomiast w dowolnym momencie "odładować" przy pomocy AppDomain.Unload. Napisałem nawet przykład pokazujący, że to działa. W czym więc problem?

Otóż wywołanie AppDomain.Unload może się nie powieść i zostanie wtedy rzucony wyjątku CannotUnloadAppDomainException. Powodów mogą być trzy, ale nas interesuje jeden. Kiedy wołamy AppDomain.Unload i wewnątrz domeny są aktywne jakieś wątki to AppDomain.Unload spróbuje je ubić przy pomocy metody Thread.Abort. Jeśli się uda to ok, ale Thread.Abort może nie być w stanie ubić wątku i wtedy zostanie wygenerowany wzmiankowany powyżej wyjątek.

Kiedy Thread.Abort nie zadziała? Dokumentacja wspomina o dwóch przypadkach.
  • Kiedy wątek wykonuje kod niezarządzany.
  • Kiedy wątek wykonuje właśnie kod wewnątrz bloku finally. To można bardzo łatwo symulować przez umieszczenie wywołania Thread.Sleep(100000) wewnątrz bloku finally.

08/11/2014

Jako programista przyzwyczaiłem się do...

Home

Jakiś czas temu rozmawiałem z kolegą z branży i w pewnym momencie powiedział on zdanie, które utkwiło mi w pamięci. Parafrazując stwierdził, że:

"Jako programista przyzwyczaiłem się do tego, że się ze mną rozmawia."

Powiedział to w kontekście pracy w firmie, dla kogoś. Chodziło o to, że nawet jeśli coś mu się nie podobało, ale musiał to zrobić bo ktoś podjął taką decyzję, to nie bolało tak bardzo jeśli ktoś z nim wcześniej o tym porozmawiał. Mam tutaj na myśli zarówno sprawy techniczne co nasuwa się samo, ale również te "miękkie" na przykład związane z organizacją pracy.

Jak się nad tym chwilę zastanowiłem to stwierdziłem, że z pewnymi wyjątkami mam podobne doświadczenie i w sumie takie podejście wydało mi się oczywiste. Pytanie czy tak jest wszędzie? Może po prostu mieliśmy szczęście. Czy macie podobne wrażenia, a może wprost przeciwnie?

01/11/2014

Własne komendy w menu kontekstowym Windows'a

Home

Kiedy napiszę jakieś narzędzie to często chcę mieć do niego łatwy dostęp z poziomu menu kontekstowego Windows'a. Już od dłuższego czasu robię to przez dodanie odpowiednich wpisów do rejestru systemowego. W ogólności grzebanie w rejestrze nie jest bardzo przyjemne i istnieje ryzyko, że się coś popsuje. Dlatego też korzystam z następujących wypróbowanych wzorców.

Jeśli polecenie ma być dostępne dla wszystkich plików
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\*\Shell\COMMAND_NAME]
[HKEY_CLASSES_ROOT\*\Shell\COMMAND_NAME\command]
@="COMMAND"
Jeśli polecenie ma być dostępne dla określonych plików
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\.EXTENSION\Shell\COMMAND_NAME]
[HKEY_CLASSES_ROOT\.EXTENSION\Shell\COMMAND_NAME\command]
@="COMMAND"
Jeśli polecenie ma być dostępne dla katalogów
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\Directory\Shell\COMMAND_NAME]
[HKEY_CLASSES_ROOT\Directory\Shell\COMMAND_NAME\command]
@="COMMAND"

[HKEY_CLASSES_ROOT\Directory\Background\Shell\COMMAND_NAME]
[HKEY_CLASSES_ROOT\Directory\Background\Shell\COMMAND_NAME\command]
@="COMMAND"
W powyższych wzorcach COMMAND_NAME oznacza nazwę opcji jaka pojawi się w menu kontekstowym np.: Shred, EXTENSION to rozszerzenie pliku np.: txt i w końcu COMMAND to skrypt/polecenie jakie ma zostać wykonane po wybraniu przez użytkownika określonej opcji z menu kontekstowego np.: T:\\bin\\shred -n 10 -v -z \"%1\". Zwróćcie uwagę, że przy podawaniu ścieżki stosuję podwójny ukośnik tj. \\ oraz, że cudzysłów wewnątrz polecenia jest poprzedzony ukośnikiem tj. \". Na koniec uzupełnione wzorce należy zapisać w pliku z rozszerzeniem *.reg i przez podwójne kliknięcie na takim pliku dodać go do rejestru. Zmiany będą widoczne natychmiast.

29/10/2014

Co wyróżnia bardzo dobrego programistę (2)?

Home

Pewnie słyszeliście o strumieniach (ang. stream) w .NET. O tym, że się je otwiera, a po użyciu zamyka. Zresztą w innych technologiach jest podobnie. Sprawa stara jak świat i wydawałoby się, że każdy o tym wie. Ja w każdym razie z definicji tak robię.

Dziwi mnie więc, że po tylu latach istnienia .NET ciągle pojawiają się pytanie z tym związane, jak na przykład to na stackoverflow.com. Dziwi mnie również, że przy okazji tego rodzaju pytań często pojawiają się najróżniejsze koncepcje / pomysły / odpowiedzi i to od użytkowników, którzy wcale nie są początkujący. Tymczasem wystarczy wrócić do podstaw, aby rozwiązać problem.

A może jest tak, że kiedy na co dzień pracujemy z skomplikowanymi algorytmami / trudnymi problemami biznesowymi / strukturami danych (niepotrzebne skreślić) to w pewnym momencie tracimy perspektywę i każdy napotkany problem od razu wydaje się nam skomplikowany.

Kolejny czynnik powodujący tą utratę perspektywy to chyba również mnogość różnych technologii i bibliotek, jakie są u obecnie używane. Ta biblioteka do tego, tamta ułatwia to, ta automatyzuje tamto itd. W ten sposób przyzwyczajamy się do tego, że wiele rzeczy coś robi za nas i my nie wnikamy w szczegóły i równocześnie zapominamy również o podstawach.

Nie mówię, że nie należy używać bibliotek, framework'ów czyli ułatwiać sobie pracy, ale bardzo dobry programista powinien również wiedzieć co, się za tym wszystkim kryje i nie zapominać o podstawach.

20/10/2014

Z rozmów o pracę

Home

Nie wiem dlaczego, ale ostatnio wzięło mnie na refleksje. Siedzę, popijam sobie herbatę i tak ni z gruszki ni z pietruszki przypomniała mi się dawna rozmowa o pracę. Rozmawiałem z dwoma menadżerami, ale takimi z wiedzą techniczną, co to nie zmieniają szybko tematu, jak przechodzisz na tematy techniczne.

Rozmawialiśmy dość długo i w głowie utknęła mi jedna rzecz, jaką powiedzieli. Parafrazując, zgodnie stwierdzili, że ciężko znaleźć programistę, który nie byłby code monkey. Przy czym przez code monkey rozumieli trochę coś innego niż ja w tamtym momencie. Ja pod tym terminem rozumiałem osobę słabo radząca sobie z programowaniem, umiejąca napisać coś pod dyktando, ale nie dużo więcej. Dla nich jednak code monkey mógł być nawet bardzo dobry technicznie programista, doskonale radzący sobie z zadaniami, samodzielny... lecz z jednym bardzo istotnym z ich menadżerskiego punktu widzenia "ale". To jest nie umiejący lub słabo radzący sobie z komunikacją, w szczególności z biznesem, ludźmi nietechnicznymi, taki nieumiejący postawić się w roli użytkownika końcowego.

Z pewnością my programiści nie jesteśmy mistrzami komunikacji i Ci, którzy posiadają obok wiedzy technicznej umiejętności miękkie mogą się łatwo wyróżnić. Zastanawiam się jednak czy rzeczywiście większość z nas to geeki, którzy nie widzą nic poza kodem. Co myślicie?

19/10/2014

Problemy z MSDTC

Home

DTC - Distributed Transacion Coordinator to takie ustrojstwo w postaci usługi systemowej, które jest potrzebne, jeśli korzystamy z transakcji rozproszonych. Dostajemy to w komplecie razem z Windows'em, a zarządzamy tym z poziomu przystawki do konsoli zarządzania systemem Control Panel -> Administrative Tools -> Component Services. Jak działa to działa i nic nie trzeba robić, a jak nie to zaczynają się schody. Ja ostatnio spędziłem nad uruchomieniem DTC z pół dnia rozwiązując kolejne problemy. Oto moja historia ku pamięci (dotyczy Windows 7):

Problem 1

Usługa nie chce się uruchomić na domyślnym koncie Network Service, a jak zmienimy konto na Local System to w logu systemowym zaczną się pojawiać komunikaty:

The account that the MS DTC service is running under is invalid. This can happen if the service account information has been changed using the Services snap-in in Microsoft Management Console (MMC). MS DTC service will continue to start. Please make sure that the MS DTC service account information is updated using the Component Services Explorer.

Mi pomógł ten artykuł, a konkretniej nadanie uprawnień Read & Execute kontu Network Service do pliku C:\Windows\system32\Msdtc\MSDTC.LOG

Problem 2

Kolejny problem to słynna czerwona strzałka. Po otworzeniu Component Services i kliknięciu ikony My Computer dostajemy komunikat:

The Transaction Manager is not available. (Exception from HRESULT: 0x8004D01B).

Wypróbowałem wiele wskazówek, ale ostatecznie pomogło mi nadanie pełnych uprawnień kontu System i grupie Administrators do katalogu C:\Windows\registration. Zalecenie to wziąłem z tego artykułu. Dużo wskazówek dotyczących tego konkretnego problemu można też znaleźć tutaj.

Problem 3

Kiedy udało się już uruchomić usługę to przy próbie skorzystania z transakcji rozproszonej w kodzie był rzucany wyjątek z komunikatem:

Network access for Distributed Transaction Manager (MSDTC) has been disabled. Please enable DTC for network access in the security configuration for MSDTC using the Component Services Administrative tool

Okazało się, że we właściwościach lokalnego DTC (dostępnych z wspomnianej przystawki konsoli zarządzania systemem Component Services) w zakładce Security nie włączyłem opcji Network DTC Access.

Problem 4

Kolejny wyjątek jakim dostałem po twarzy to:

The MSDTC transaction manager was unable to pull the transaction from the source transaction manager due to communication problems. Possible causes are: a firewall is present and it doesn't have an exception for the MSDTC process, the two machines cannot find each other by their NetBIOS names, or the support for network transactions is not enabled for one of the two transaction managers. (Exception from HRESULT: 0x8004D02B)"

W tym przypadku okazało się, że jedna z maszyn biorąca udział w transakcji rozproszonej nie potrafi znaleźć adresu IP mojej maszyny na podstawie jej nazwy. Pomogło dodanie wpisu do pliku hosts.

15/10/2014

Co wyróżnia bardzo dobrego programistę?

Home

Sądzę, że dla każdego bycie bardzo dobrym programistą oznacz trochę coś innego, na przykład do wyboru: znajomość nowych technologii, minimalizowanie używania myszki na rzecz posługiwania się głównie klawiaturą, samodzielne doszkalanie się, blogowanie, udział w konferencjach, komunikatywność, umiejętność pracy pod presją czasu... Ja jednak chciałbym zwrócić uwagę na inną rzecz.

Czasami jest tak, że dostajemy do naprawienie jakiś "wredny" błąd. Niekoniecznie wymaga on wielu zmian w kodzie, ale jest trudny do odtworzenia. Być może brakuje nam też danych, aby go powtórzyć lub opis jest niedokładny. A może jest tak, że błąd dotyczy nieznanego nam obszaru, kod jest brzydki albo to kolejny błąd dotyczący podobnej rzeczy i zaczyna nas to irytować.

W każdym razie mamy dosyć i, jak tylko znajdziemy przyczynę i wyk-minimy rozwiązanie (być może nietrywialne i wymagające dużo technicznej wiedzy), to szybko robimy commit i zapominamy o sprawie. To jest moim zdaniem ten moment, kiedy można zobaczyć na czym polega bycie bardzo dobrym programistą. Moim zdaniem w takiej sytuacji bardzo dobry programista, nawet mimo zniechęcenia, zada sobie dodatkowe pytania:
  • Czy ten błąd nie występuje jeszcze gdzieś indziej, nawet jeśli zgłoszenie tego nie dotyczy?
  • Jak jego poprawka wpłynie na resztę systemu? Powiązania pomiędzy różnymi modułami systemu mogą być nieoczywiste i często uzyskanie odpowiedzi na takie pytanie wymaga dużo wysiłku.
  • Jaka jest pra-przyczyna błędu? Na przykład NullReferenceException można "naprawić" bardzo łatwo przy pomocy jednego if'ka. Często będzie to dobre rozwiązanie, ale może być też tak, że null nigdy nie powinien się pojawić w danym miejscu i jest on wynikiem zmiany gdzieś indziej.
To mogą być bardzo niewygodne pytania, szczególnie, jeśli się nam nie chce, ręce bolą i jest coś ciekawszego do zrobienia, ale równocześnie bardzo potrzebne, szczególnie, jeśli pracujemy z starszymi systemami czy nawet tymi młodszymi, ale bez testów automatycznych.

Nie wiem jak Wy, ale ja na początku swojej kariery uważałem, że w zawodzie programisty właściwie liczy się tylko strona techniczna, znajomość nowych technologii, bibliotek itd. W obecnej chwili rzeczy te uważam za niezwykle ważne, ale nie najważniejsze i coraz większą wagę przywiązuje do umiejętności i predyspozycji, nazwijmy je około-technicznych i miękkich. A co wy o tym myślicie? Też tak macie, a może to tylko moje "zboczenie"?

06/10/2014

OxyPlot i nieciągłe przedziały

Home

Od dłuższego czasu używam biblioteki OxyPlot i bardzo ją sobie chwalę. Ostatnio zamarzyło mi się stworzenie wykresu przedziałowego. Na początek stwierdziłem, że wystarczy zastosować zwykły wykres liniowy czyli klasę LineSeries. Załóżmy, że chcemy zaprezentować na wykresy przedziały tj.; (2,3), (4,6) oraz (8,10). Spróbujmy, więc czegoś takiego:
var s = new LineSeries();
s.Title = "Nieciągłe przedziały";
s.Points.Add(new DataPoint(2, 1));
s.Points.Add(new DataPoint(3, 1));
           
s.Points.Add(new DataPoint(4, 1));
s.Points.Add(new DataPoint(6, 1));
        
s.Points.Add(new DataPoint(8, 1));
s.Points.Add(new DataPoint(10, 1));
To jednak nie zadziała gdyż w rezultacie otrzymamy linię ciągłą, czego zresztą należało się spodziewać ponieważ LineSeries po prostu łączy kolejne punkty. Wypróbowałem więc inne rodzaje wykresów, bawiłem się ustawieniami, ale bez rezultatów. Rozwiązanie okazało się jednak bardzo proste. Jeśli nie chcemy, aby dwa punkty zostały połączone linią to pomiędzy nimi należy umieścić punkt o współrzędnych (Double.Nan, Double.NaN).
var s = new LineSeries();
s.Title = "Nieciągłe przedziały";
s.Points.Add(new DataPoint(2, 1));
s.Points.Add(new DataPoint(3, 1));
s.Points.Add(new DataPoint(Double.NaN, Double.NaN));            
s.Points.Add(new DataPoint(4, 1));
s.Points.Add(new DataPoint(6, 1));
s.Points.Add(new DataPoint(Double.NaN, Double.NaN));       
s.Points.Add(new DataPoint(8, 1));
s.Points.Add(new DataPoint(10, 1));
Na koniec jeszcze przykład tak skonstruowanego wykresu:

28/09/2014

Telltale rulez

Home

Dawno nie pisałem już o elektronicznej rozrywce. Raz, że mam coraz mniej i mniej czasu na granie, a dwa, że chyba się starzeję i coraz trudniej znaleźć jakiś tytuł, który zwróci moją uwagę. Ostatnio udało się to jednak jednemu ze studiów developerskich i to aż dwukrotnie. Mam na myśli Telltale Games i ich dwie produkcje: Walking Dead sezon 1 oraz 2 oraz ostatnio The Wolf Among Us.


Żywe trupy powstały na bazie, swoją drogą genialnej, serii komiksów Roberta Kirkmana pod tym samym tytułem  Od jakiegoś czasu możemy też oglądać serial na ich kanwie produkowany przez stację AMC. Jeśli lubcie te klimaty to też polecam. Wracając jednak do samej gry to jest ona zbliżona do przygodówki point-and-click, chociaż dla mnie jest to przede wszystkim interaktywny, mega wciągający serial,w którym nasze decyzję wpływają na dalsze losu bohaterów. Naprawdę chylę czoła przez scenarzystami bo pomimo tego, że przeczytałem komiks, obejrzałem serial nadal potrafili mnie zaskoczyć, a niektóre sceny po prostu wbijają w fotel. Szczególnie polecam granie po zmroku, przy zgaszonym świetle i ze słuchawkami na uszach. Osobiście nie mogę doczekać się kontynuacji.

Drugi tytuł osadzony jest w tym samym stylu interaktywnego serialu. Nie zdradzę dużo jeśli powiem, że opowiada o postaciach z bajek (na przykład tytułowym złym wilku), które żyją w ukryciu we współczesnym Nowym Yorku. Może i brzmi to niedorzecznie i przypomina grę dla dzieci, ale w rzeczywistości jest to bardzo mroczny thriller, w żadnym wypadku nie dla dzieci. Jestem dopiero na początku grania, a raczej oglądania pierwszej serii, i zapowiada się równie dobrze jak Walking Dead.



Jeśli lubicie dobre, mocne historie i gry to szczerze polecam i bynajmniej nie jest to wpis sponsorowany. Zaletą obu tytułów jest to, że można do nich usiąść na kilkanaście minut i nieśpiesznie popychać przygodę do przodu. Ostrzegam jednak, że jeśli Was wciągnie to grozi Wam nieprzespana noc.

26/09/2014

Token Content in state Epilog would result in an invalid XML document

Home

To kolejny post z serii ku pamięci aby oszczędzić innym bólu. Otóż po wprowadzeniu zmian do logiki pewnego web service'u i napisaniu testów jednostkowych, postanowiłem go jeszcze przetestować integracyjnie przy pomocy skądinąd fajnego narzędzia SoapUI. Zacząłem od przygotowałem komunikatu XML do wysłania do usługi. Poszło szybko i myślałem, że to już prawie koniec mojej pracy. Niestety był to dopiero początek, gdyż okazało się, że po wysłaniu komunikatu serwer zwraca błąd jak poniżej:

Token Content in state Epilog would result in an invalid XML document.

Zdziwiłem się ponieważ to nie był pierwszy raz kiedy przechodziłem przez tą procedurę i nigdy z czymś takim się nie spotkałem. Początkowo pomyślałem, że to wina moich zmian, ale testy przy użyciu starej wersji kodu dały ten sam wynik. W następnej kolejności sprawdziłem, wiec czy używane przeze mnie wcześniej komunikaty/żądania dają ten sam błąd, ale działały poprawnie. W tym momencie wiedziałem już, że coś jest nie tak z tym konkretnym żądaniem. Zacząłem z niego wycinać kolejne elementy, aż pozostał prawie pusty, a błąd wciąż występował. W tym momencie mnie oświeciło i postanowiłem podejrzeć to czego nie widać gołym okiem czyli białe znaki. Kiedy to zrobiłem zakląłem szpetnie ponieważ przyczyną problemu okazał się znak nowej linii na końcu komunikatu - tuż po tagu zamykającym.

23/09/2014

10000 UserObjects

Home

10000 to limit User Objects jakie na moim sprzęcie mogła zaalokować aplikacja zanim się wywaliła. 10000 to w gruncie rzeczy bardzo duża liczba i jeśli osiągamy ten limit to z dużą pewnością mamy jakiś problem i tak było w przypadku aplikacji napisanej w WinForms jaką miałem naprawić.

Po podłączeniu się debugger'em do aplikacji szybko stwierdziłem, że problem dotyczy jednej z kontrolek, która była alokowana dynamcznie w pętli. Na menadżerze zadań bardzo ładnie było widać jak wzrasta liczba User Objects z każdą kolejną instancją tej kontrolki. Nie byłem natomiast pewny czemu te kontrolki nie były zwalniane pomimo, że były usuwane z UI i nie było na nich wskazań (jak się później okazało pozornie). Tutaj z pomocą przyszedł ANTS Memory Profiler. Jedno uruchomienie i już wiedziałem w czym tkwi problem. Mrówki pokazały, że nie zwolnione kontrolki wciąż są wskazywane przez obiekt klasy System.Windows.Forms.DropTarget.

To dość częsty błąd, a dzieje się tak jeśli kontrolka wspiera Paste & Copy i w związku z tym ma ustawioną właściwość AllowDrop na true. Powoduje to, że kontrolka wskazywana jest przez wspominany obiekt klasy DropTarget dopóki nie ustawimy AllowDrop z powrotem na false. Poprawka błędu okazała się więc trywialna. Wystarczyło, że przy usuwaniu kontrolki z UI ustawiłem tą właściwość na false. W sumie drobna rzecz, ale łatwa do pominięcia.

Jeszcze krótki komentarz odnośnie technologii WPF. Tutaj też mamy właściwość AllowDrop, ale opisany problem nie występuje ponieważ WPF ma zupełnie inne bebechy niż WinForms.

21/09/2014

Od jakiej wartości zaczynają się indeksy tablic w C#?

Home

To post z serii ciekawostki. Większość z Was zapytana od jakiej wartości zaczynają się indeksy tablic w C# odpowie z pewnością, że od 0 i tego należy się trzymać, ale są pewne wyjątki. Oto przykład na jaki natknąłem się eksplorując czeluście platformy .NET pokazujący jak stworzyć tablicę 10x10 z indeksami zaczynającymi się od 5:
var array = Array.CreateInstance(typeof(int), new[] { 10, 10 }, new[] { 5, 5 });
var array2 = (int[,]) array;
A teraz mały przykład użycia:
array2[1,3] = 1 // Out of bounds array index
array2[5,6] = 1 // OK
array2[15,14] = 1 // Out of bounds array index
array2[14,14] = 1 // OK
Oczywiście nie byłbym sobą gdybym nie spróbował tego samego z tablicą jednowymiarową:
var array = Array.CreateInstance(typeof(int), new[] { 10 }, new[] { 5 });
var array2 = (int[]) array;
Taka próba rzutowania zakończy się niestety, a może na szczęście, wyjątkiem o treści:

Unable to cast object of type 'System.Int32[*]' to type 'System.Int32[]'.

Co oznacza zapis System.Int32[*]? Mówiąc szczerze nie jest do końca pewny. Bazując jednak na poniższym teście:
Console.WriteLine(new int[10].GetType()); 
Console.WriteLine(Array.CreateInstance(typeof(int), new[] { 10 }, new[] { 0 }).GetType());
Console.WriteLine(Array.CreateInstance(typeof(int), new[] { 10 }, new[] { 5 }).GetType()); 
Console.WriteLine(new int[10,10].GetType()); 
Console.WriteLine(Array.CreateInstance(typeof(int), new[] { 10, 10 }, new[] { 5, 5 }).GetType()); 
Który wypisze na ekran takie wyniki:

System.Int32[]
System.Int32[]
System.Int32[*]
System.Int32[,]
System.Int32[,]

Można stwierdzić, że nazwa typu TYP[*] oznacza po prostu tablicę jednowymiarową z indeksem nie zaczynającym się w zerze.

Na koniec jeszcze jedna ciekawostka. Rzutowanie System.Int32[*] na System.Int32[] w kodzie programu nie powiedzie się, ale już w oknie Quick Watch albo Immediate Window już tak:

17/09/2014

Zużycie procesora przez wskazany proces

Home

Potrzebowałem ustalić programowo bieżące zużycie procesora przez wskazany proces. Zacząłem od przyjrzenia się licznikom wydajności i szybko naklepałem coś takiego:
var p = Process.GetProcessById(id);
var counter = new PerformanceCounter("Process", "% Processor Time", myProcess.ProcessName);
Console.WriteLine("Processor %: {0}", counter.NextValue());
Przetestowałem i wyniki okazały się dziwne ponieważ czasami uzyskiwałem zużycie większe niż 100%. Chwila, ale przecież mam 8 rdzeniowy procesor. Spróbujmy więc czegoś takiego:
Console.WriteLine("Processor %: {0}", counter.NextValue() / Environment.ProcessorCount);
To już działało dobrze. Postanowiłem jednak poszukać alternatywnego sposobu i przyjrzałem się co oferuje klasa Process. Szybko znalazłem właściwość Process.TotalProcessorTime, która zgodnie ze swoją nazwą zwraca całkowity czas procesora zużyty przez dany proces począwszy od jego uruchomienia. Ja potrzebowałem natomiast aktualnego zużycia. Trochę myślenia, szukania w Internecie (na przykład tutaj) i szybko dopisałem coś takiego:
public class Utils
{
    public class ProcessorUtilizationInfo
    {
        public TimeSpan LastProcessorTime { get; set; }
        public DateTime LastCheck { get; set; }
        public double Value { get; set; }
        public Process Process { get; set; }
    }

    public static ProcessorUtilizationInfo GetProcessorUtilization(ProcessorUtilizationInfo info)
    {
        info.Process.Refresh();

        var now = DateTime.Now;
        var elapsed = now - info.LastCheck;
        var processorTime = (info.Process.TotalProcessorTime - info.LastProcessorTime);

        info.LastProcessorTime = info.Process.TotalProcessorTime;
        info.LastCheck = now;
        info.Value = processorTime.TotalMilliseconds / elapsed.TotalMilliseconds / Environment.ProcessorCount;

        return info;
    }
}
Dla przejrzystości pominąłem walidację danych. Klasa pomocnicza ProcessorUtilizationInfo jest potrzebna gdybyśmy chcieli wołać metodę GetProcessorUtilization wielokrotnie dla tego samego procesu. Ktoś może marudzić, że używam DateTime.Now, że wynik może być nieprecyzyjny, ale moje testy pokazały, że zastosowanie licznika wydajności i metody GetProcessorUtilization daje podobny rezultaty. Przykład użycia metody GetProcessorUtilization wygląda następująco:
var p = Process.GetProcessById(id);
var info = new Utils.ProcessorUtilizationInfo {Process = p};
Console.WriteLine("Processor %: {0}", Utils.GetProcessorUtilization(info).Value * 100);
Do opisanych 2 sposobów uzyskania aktualnego zużycia procesora dla wskazanego procesu mam jedno zastrzeżenie. Otóż oba rozwiązania dają co prawda bardzo zbliżone wyniki (zaobserwowałem różnice rzędu 0.1 - 0.2%), ale wyniki te różniły się nawet o 1% od tego co pokazywał menadżer zadań. Ktoś wie dlaczego? Może znacie lepsze rozwiązanie postawionego problemu?

10/09/2014

Sortowanie w czasie liniowym

Home

Teoria mówi, że optymalna złożoność obliczeniowa dla algorytmów sortowania opartych o porównywanie wynosi O(nlogn), czyli, innymi słowy, szybciej być już nie może. Taką złożoność optymistyczną ma na przykład używany w .NET algorytm sortowania szybkiego. Ale chwila, co to znaczy sortowanie oparte o porównywanie? To w ogóle istnieje inna możliwość? Przecież, aby posortować na przykład listę liczb całkowitych, to trzeba je porównywać!

Okazuje się, że do problemu można podejść inaczej. Jeśli zrezygnujemy z porównywania, to, pod pewnymi warunkami, osiągniemy złożoność liniową O(n). Niby szybciej, ale czy rzeczywiście warto się tym przejmować? Postanowiłem to sprawdzić i porównałem zachowanie algorytmu sortowania używanego przez .NET, naiwnej rekurencyjnej implementacji sortowania szybkiego oraz algorytmu sortowania w czasie liniowym o nazwie sortowanie przez zliczanie.

Sortowanie przez zliczanie zostało opisane choćby na Wikipedii, dlatego tutaj przybliżę tylko jego ideę. Zacznijmy od tego, że algorytm ten bardzo dobrze nadaje się do sortowania liczb całkowitych z zadanego zakresu, powiedzmy od K do N. Im N mniejsze tym lepiej i jest to oczywiście istotne jego ograniczenie. Jego działanie opiera się na policzeniu, ile razy w tablicy do posortowania pojawiła się liczba K, ile razy liczba K + 1 ... i ile razy liczba N. W tym celu wystarczy odwiedzić każdy element tablicy tylko raz. Kiedy mamy już te informacje, sortowanie właściwie jest zakończone. Na przykład: jeśli liczba 0 wystąpiła 10 razy, a liczba 3 wystąpiła 2 itd., to jako wynik wypiszemy najpierw 10 razy 0, potem 2 razy 3 itd.

Wyniki porównania wspomnianych 3 algorytmów zaprezentowałem na 2 wykresach poniżej. Oś X pokazuje liczbę elementów do posortowania, a oś Y czas sortowania w ms. Pierwszy wykres pokazuje przypadek, kiedy dane do posortowania zawierały liczby z zakresu od 0 do 100, a drugi od 0 do 100 tysięcy.



Wnioski są następujące, niektóre bardzo ciekawe:
  • Dla małego zakresu liczb naiwna implementacja sortowania szybkiego działa bardzo słabo (mamy tutaj pesymistyczny przypadek i złożoność O(n^2)).
  • Nie widać tego na wykresie, lecz dla małego zakresu liczb sortowanie przez zliczanie jest nieznacznie szybsze od implementacji używanej przez .NET: dla 1 miliona elementów jest to 15 ms w porównaniu do 64 ms.
  • W związku z tym pojawia się pytanie, czym różni się naiwna rekurencyjna implementacja sortowania szybkiego od tej używanej przez .NET? Można by o tym dużo napisać, więc tutaj tylko zasygnalizuję o co chodzi. Otóż w rzeczywistości .NET używa tzw. sortowanie introspektywnego, który stanowi połączenie "zwykłego" sortowania szybkiego, sortowania przez kopcowanie i sortowania przez wstawianie, przez co wyeliminowano przypadek pesymistyczny, kiedy QuickSort ma złożoność wielomianową.
  • Dla dużego zakresu elementów różnica pomiędzy 3 porównywanymi algorytmami zaciera się chociaż rekurencyjna implementacja QuickSort jest wciąż najwolniejsza, a sortowanie przez zliczanie jest trochę szybsze od tego co oferuje .NET.
  • Sprawdziłem czas sortowania dla maksymalnie 50 milionów elementów i trwało to 2 s dla sortowanie przez zliczanie oraz 5 s dla hybrydowego.
  • Wniosek końcowy jest taki, że biorąc pod uwagę ograniczenia i wady sortowania przez zliczanie oraz to, jak dobrze działa implementacja hybrydowa w .NET należy jej po prostu używać i nie zastanawiać się specjalnie nad innymi wynalazkami.

09/08/2014

Desktop Heap

Home

W swojej pracy bardzo lubię dowiadywać się o zupełnie nowych rzeczach, o których jeszcze nigdy nie słyszałem. Ostatnio taką nowością dla mnie było pojęcie desktop heap (sterta pulpitu???). Jest to coś, czym 99% z nas przez 99% czasu nie ma potrzeby się zajmować, a zainteresujemy się tym dopiero, jak to często bywa, kiedy pojawią się kłopoty.

Ja spotkałem się z desktop heap przy okazji problemów z usługami systemowymi tj. jeszcze kilka minut temu nie było kłopotów i nagle przestały się uruchamiać. W logu systemowym pojawił się natomiast komunikat podobny do poniższego:

Could not find file ‘C:\WINDOWS\TEMP\ayui23kj.dll’

Kolega znalazł artykuł, który jako winowajcę wskazywał właśnie desktop heap i było to strzałem w dziesiątkę. Pojęcie to zostało dobrze opisane w tym artykule, dlatego tutaj opiszę je tylko pokrótce. Zacznijmy od tego, że w systemie Windows mamy pojęcie sesji (ang. session) pod którym kryje się ogół zasobów powiązanych z pojedynczym zalogowanym użytkownikiem.

W ramach sesji może istnieć wiele window station. Obsługują one schowek i zawierają w sobie różne procesy oraz pulpity (ang. desktop). Warto zapamiętać, że tylko jedna stacja może komunikować się z użytkownikiem i nazywa się ona Winsta0.  Co okaże się ważne później, wszystkie usługi systemowe działające na tym samym koncie zostają przydzielone do tej samej stacji.

To dość daleka analogia, ale stacja przypomina mi domeny aplikacyjne znane z platformy .NET. Domeny aplikacyjne to takie małe procesy, które służą zwiększeniu bezpieczeństwa przez separację różnych zadań od siebie, a więc oba pojęcia mają podobną rolę.

Wspomniane pulpity zawierają w sobie wirtualną/logiczną przestrzeń, na której można coś wyświetlić. To również pojemniki na obiekty interfejsu użytkownika takie, jak okna i menu. Dokładniej obiekty te przechowywane są w części pulpitu zwanej właśnie stertą. Problem polega na tym, że sterta ma ograniczoną wielkość i może się zapełnić, i tak było w moim przypadku.

Wielkością sterty można co prawda sterować (patrz tutaj oraz tutaj), ale pytanie brzmi co spowodowało jej przepełnienie. W moim przypadku problemem okazała się zła obsługa niezarządzanych zasobów przez jedną z usług. Zasoby te nie były poprawnie zwalniane i wypełniały stertę, dodatkowo blokując inne usługi. Po wprowadzeniu poprawki problem z uruchomieniem usług ustąpił.

Na koniec chcę zwrócić uwagę na jedną rzecz. Ta jedna usługa blokowała inne bo wszystkie działały na tym samym koncie. W związku z tym korzystały z tej samej stacji, pulpitu i sterty. To co potwierdziło przepełnienie sterty był fakt, że zmiana konta używanego przez usługę powodowała, że można ją było poprawnie uruchomić. Innymi słowy zmiana konta powodowała przeskoczenie do innej stacji, a więc również do innej, nie wypełnionej sterty.

03/08/2014

Czy użycie if zamiast else if ma znaczenie?

Home

Historia zaczyna się od prostego fragmentu kodu pokazanego poniżej. Kod ten to fragment walidatora, ktory ma za zadanie określić, czy dane są prawidłowe. Jeśli nie, to zmienna isValid powinna zostać ustawiona na false.
var isValid = true;

if (condition_1)
   isValid = false;

if (condition_2)
   isValid = false;
Kod ten działał do momentu, kiedy wprowadzono do niego małą zmianę pokazaną poniżej. Było to pewne uszczegółowienie logiki walidacji danych wejściowych.
var isValid= true;

if (condition_1)
   isValid = false;

if (condition_2)
   isValid = false;
//Wprowadzona zmiana
else 
{
   if (condition_3)
      isValid = true;
   else if (condition_4)
      isValid = false;
}
Po zastanowieniu się z pewnością stwierdzicie, że coś tutaj nie pasuje. Problem polega na tym, że w określonych warunkach zmienna isValid może zostać z powrotem ustawiona na true nawet jeśli wcześniej stwierdzono, że dane są błędne i ustawiono ją na false!

Może są jakieś rzadkie i dziwne przypadki kiedy błędne dane mogą stać się nagle poprawne, ale w przypadku, z jakim się spotkałem, był to ewidentny błąd i przeoczenie albo brak zrozumienia działania kodu. Moim zdaniem błąd tkwi jednak w jeszcze jednym miejscu tj. w użyciu wielu if'ów zamiast konstrukcji else if w kodzie pokazanym na samym początku. Gdyby początkowy kod wyglądał w ten sposób:
var isValid = true;

if (condition_1)
   isValid = false;
// else if zamiast if
else if (condition_2)
   isValid = false;
To nawet jego modyfikacja w opisany wcześniej sposób nie spowodowałaby, że walidator pozwolił na kontynuację przetwarzania pomimo błędnych danych. Może wydawać się, że kilka if'ów zamiast użycia else if nie robi różnicy bo to przecież oczywiste jak ten kod działa. Może i oczywiste, ale tu i teraz. Za kilka tygodni lub miesięcy, dla kogoś innego, może już nie być takie oczywiste. Ktoś inny może również to po prostu przeoczyć.

Tworząc kod trzeba się starać, aby był możliwie łatwy do zrozumienia, rozszerzania i wprowadzania zmian, a w tym celu w przypadkach podobnych do opisanego wystarczy po prostu użycie trochę inne konstrukcji językowej, która lepiej wyrazi nasze zamiary innym.

16/07/2014

LaTeX + PDF + obrazki

Home

Korzystając z LaTeX'a dokumenty PDF można tworzyć na przynajmniej dwa sposoby. Te znane mi to:
  • Pliki źródłowe *.tex -> dokument w formacie DVI -> PDF
  • Pliki źródłowe *.tex -> PDF
W pierwszym przypadku używamy dwóch programów tj. latex.exe oraz dvipdfm.exe, a w drugim tylko jednego tj. pdflatex.exe. Do tej pory stosowałem pierwsze podejście, ale w pewnym momencie chciałem skorzystać z szablonu, który był dostosowany do drugiego podejścia. I tu zaczęły się problemy, na których straciłem dużo czasu, a więc piszę ten post ku pamięci. Problemy te dotyczyły osadzania obrazków w dokumencie PDF.

Pierwszy problem polegał na tym, że kompilator krzyczał, że nie rozpoznaje rozszerzenia PS. To udało mi się przewalczyć w miarę szybko zmieniając rozszerzenia na EPS. Dziwne, ale zadziałało. Jakie było jednak moje zdziwienie kiedy po otworzeniu wygenerowanego dokumentu PDF nie znalazłem w nim żadnych obrazków, a dokładniej w miejscach przeznaczonych na obrazki widniały białe obszary o rozmiarze obrazków! Rozwiązanie okazało się jest dziwniejsze niż wcześniej. Otóż, wystarczyło z nazwy obrazka usunąć rozszerzenie czyli zamiast obrazek.eps wpisać obrazek.

Na tym nie koniec. Po jakimś czasie udało mi się doprowadzić do sytuacji kiedy obrazki były widoczne w dokumencie PDF zarówno kiedy nazwa zawierała rozszerzenie jak i bez niego. Otóż w celu umieszczenia w dokumencie obrazka w formacie JPG najpierw konwertuję go do formatu PS przy pomocy programu jpeg2ps.exe. Nie pamiętam już dlaczego, ale jpeg2ps.exe używałem z przełącznikiem -b. Nie wnikając w szczegóły do czego służy, po jego usunięciu problem ustąpił.

Mam nadzieję, że te krótkie wskazówki oszczędzą komuś czasu. Cały czas uważam, że LaTeX jest wspaniałym narzędziem, ale czasami potrafi doprowadzić do szewskiej pasji.

11/07/2014

Konferencja wewnątrz-firmowa - edycja 2

Home

Rok temu tutaj oraz tutaj opisałem zorganizowaną przez siebie konferencję wewnątrz-firmową. W tym roku odbyła się jej druga edycja. O szczegółach organizacyjnych nie będę pisał, aby się nie powtarzać. Z drobnymi szczegółami wyglądało to podobnie. Tematy prezentacji były przynajmniej tak ciekawe, jak przed rokiem i można było posłuchać następujących wystąpień:

Janusz Prusaczyk - Introduction to PowerShell scripting

Paweł Kupis - C# 5.0 Async Feature (async/await) and Synchronization Context – the old new friends

Marcin Wierzbicki - Praktyczny pomiar i atrybucja efektywności portfela inwestycji finansowych

Kamil Langowski - Wolfram Alpha: Computational Knowledge Engine

Adam Juszkiewicz - Mobile game development in Unity

Albert Skłodowski - Design Patterns in Functional Programming

Daniel Celeda - Kręgosłup programisty

Jaroslaw Trafidlo - Czy możemy czuć się bezpiecznie podłączając komputer do sieci?

Damian Orzechowski - Silniki gier komputerowych

Paweł Kaczan - Claims based itentity

Mikołaj Barwicki - Science of Motivation

Michał Komorowski - Debuggery historyczne, co to takiego?

Konferencja ponownie udała się i nie jest to tylko moja opinia. Znowu można było posłuchać o najróżniejszych rzeczach, typowo technicznych, wymagających więcej i mniej uwagi, luźnych... i właśnie to mi się w tym wszystkim najbardziej podoba. Być może się powtórzę, ale pozwala to dowiedzieć się o rzeczach, którymi na co dzień zupełnie się nie interesujemy i nawet do głowy nam nie przyjdzie, że jednak warto. Zdecydowanie polecam.

Mam też trochę wniosków na przyszłość. Było bardzo fajnie, ale zawsze może być lepiej. Jednym z pomysłów na poprawę jest wprowadzenie szablonu prezentacji z przykładami oraz ostrego limitu na poziom slajdów. Szablon powinien zapewnić, że wszystkie prezentacje będą równie czytelne oraz, że nikogo nie porwie zbytnia fantazja przy tworzeniu slajdów ;) Limit na liczbę slajdów powinien natomiast ułatwić zmieszczenie się w czasie. Po głowie chodzi mi również zaproszenie na konferencję ludzi z poza firmy. Może to być trudne do zrealizowania, ale może się uda. Co o tym myślicie, bylibyście chętni do wzięcia udziału w takim wydarzeniu?

Z nowości względem poprzedniej edycji, w tym roku wystąpienia były nagrywane. A wszystko dzięki uprzejmości Jarka Trafidło, który udostępnił kamerkę z serii GoPro, był operatorem, a także poddał zgromadzony materiał obróbce. Jeśli ktoś nie był na jakiejś prezentacji, to będzie mógł ją teraz obejrzeć na spokojnie w domu.

Z mojej perspektywy ogromnym dodatkowym plusem tych nagrań jest to, że w końcu mogłem sporzeć na siebie od tej drugiej strony. Przyznam, że to trochę dziwne uczucie, bo głos brzmi jakoś tak inaczej i w ogóle percepcja siebie się zmienia. Na przykład, wiedziałem, że dużo gestykuluję, ale nagranie pokazało, że chyba zbyt dużo. Po drugie, występując wydawało mi się, że ogólnie stoję w miejscu i tylko od czasu do czasu zrobię krok w jedną albo w drugą stronę. Nagranie znowu uświadomiło mi, że poruszam się więcej niż mi się wydaje, a to kroczek w tą, a to kroczek w drugą. Nie mówię, że trzeba stać w miejscu, ale zbytnia ruchliowść również przeszkadza. Bardzo cenne wskazówki na przyszłość.

06/07/2014

1.25 biliona dolarów

Home

Niedawno przygotowując prezentację na temat debugger'ów historycznych i szukając informacji na temat kosztu naprawiania błędów natknąłem się na ciekawe badanie na ten temat. Prezentowane wyniki wydały mi się na tyle ciekawe, że postanowiłem się nimi z Wami podzielić.

Publikację, o której mowa, można znaleźć na portalu citiseerx, a tytułowe 1.25 biliona dolarów to według autorów tego opracowania globalny koszt wytwarzania oprogramowania na świecie. Co dla nas ciekawe, połowa z tej sumy przypada na pensje programistów, czyli bagatela 625 miliardów dolarów. Autorzy badania szacują również, ile z tego przypada na naprawianie błędów, właściwe programowanie..., ale nie o tym będę pisać.

Pozwolę sobie natomiast pociągnąć temat wynagrodzeń. Otóż, według IDC, estymowana liczba profesjonalnych programistów na świecie to 11 005 000. Z prostego dzielenia wynika więc, że globalnie uśredniona pensja programisty to 56 792 dolarów, co po aktualnym kursie daje 173 357 złotych, czyli średnio 14 446 złotych miesięcznie brutto.

Czy to dużo czy mało? Dla przeciętnego Kowalskiego zapewne bardzo dużo. Dla tych z nas, którzy tyle nie zarabiają, też będzie to sporo. Niektórzy powiedzą natomiast, że można zarabiać jeszcze więcej, a jakby dostać dobrą robotę za granicą, to ta suma wyda się mała. Należy też pamiętać, że te wyliczenia są uproszczone, a średnia to wrzucenie wszystkie do jednego wora. Wynik pokazuje jednak, że tak globalnie nie powinniśmy narzekać.

Na tą średnią można też spojrzeć inaczej. Truizmem będzie stwierdzenie, że otrzymana średnia dzieli programistów na tych co zarabiają mniej i na tych co zarabiają więcej. Odpowiadając jednak na pytanie, jak średnia pensja programisty w Polsce kształtuje się względem średniej globalnej uzyskamy przybliżoną informację o tym, czy pensje powinny rosnąć czy nie. Osobiście sądzę, że nasza lokalna średnia jest zauważalnie niższa, a więc jest pole do wzrostów. I tym optymistycznym stwierdzeniem zakończę.

PS.

Kiedy skończyłem pisać ten krótki artykuł zdałem sobie sprawę, że dokładam swoje 3 grosze do dyskusji, która rozgorzała po publikacji IT-arystokracja... na przykład na facebook'u czy blogach. Nie było to jednak zamierzone.

08/06/2014

CareerCon 2014 - Kraków

Home

Jakiś czas temu otrzymałem propozycję wystąpienia na konferencji CareerCon w Trójmieście. W tamtym momencie nie miałem na to czasu, ale trochę później miała odbyć się konferencja w Krakowie. Zaproponowałem więc wzięcie w niej udziału i wygłoszenie prezentacji na temat automatycznych testów integracyjnych. Od około roku nie miałem okazji niczego prezentować. Dlatego początkowo miałem wątpliwości i obawy, ale cieszę się, że przemogłem lenistwo i asekuranctwo. Korzystając z tej okazji zaplanowaliśmy z żoną krótki weekend w Krakowie, a więc udało się połączyć przyjemne z pożytecznym, a właściwie przyjemne z przyjemnym.

Nie lubię i raczej nie umiem improwizować. Dlatego do przygotowania prezentacji podszedłem skrupulatnie. Temat już miałem. Następnie zastanowiłem się i ułożyłem sobie w głowie, o czym chcę powiedzieć. Na prezentację dostałem około 30 minut, a więc tyle czasu, aby coś już powiedzieć, ale za mało, aby wchodzić w szczegóły. Z góry odrzuciłem więc programowanie na żywo itp. W kolejnym kroku zabrałem się za przygotowanie pierwszej wersji prezentacji. Poszło gładko. W tej kwestii od dawna trzymam się zasady minimalizmu, czyli moje prezentacje mają białe tło, czarną i szarą czcionkę oraz niebieskie wypunktowania. Dodatkowo staram się minimalizować liczbę słów na slajdach - to akurat najtrudniejszy element. Przyjmuję też 2-3 minuty na jeden slajd, a ostatecznie wyszło mi 14 slajdów włączając slajd tytułowy i pożegnalny.

Prezentacja prezentacją, ale trzeba ją jeszcze wygłosić. Zabrałem się więc za ćwiczenia. Pierwsza próba, pomimo tego, że przecież wiedziałem, co chcę powiedzieć wyszła słabo. Zatrzymywałem się, aby się zastanowić, plątałem się w zeznaniach itp. Kolejnym razem było już lepiej, wprowadziłem również poprawki do prezentacji. Za trzecim razem widownią była żona. Dostałem kilka merytorycznie celnych uwag oraz uwagę abym uważał na tempo i się nie śpieszył. Prezentację przećwiczyłem sobie jeszcze przed wyjazdem. Dzięki tym kilku próbom zawartość slajdów znałem na pamięć. Sądzę, że w razie czego obyłbym się bez nich. W ramach przygotowań poprosiłem również organizatorów o informacje na temat wyglądu sali, czy jest tam podwyższenie, jak wygląda sprawa nagłośnienia, co ze sprzętem itd. No cóż, lubię być przygotowany na wszystko.

Tyle na temat przygotowań. Jak wyszło w praktyce? Zacznę od samej konferencji bo będzie krótko. Powiem tylko tyle, że z mojej perspektywy było po prostu ok. Nie byłem tam jednak długo. Przyszedłem tylko na swoją prezentację, w końcu to był również urlop. Jeśli chodzi o mój występ, to osobiście jestem zadowolony. Na sali było mniej więcej 30 osób, sama prezentacja zajęła mi ok. 25 minut, a więc tyle ile powinna. Kiedy zacząłem mówić, czułem duże zdenerwowanie, ale po 2 slajdach, kiedy dotarło do mnie, że idzie dobrze, udało mi się rozluźnić. Niestety nie udało się mi się nakłonić do zadawania pytań, padło tylko jedno. Na koniec prezentacji poprosiłem również o feedback i udało mi się zdobyć jedną, ale cenną opinię. Jeśli ktoś z Was ma jeszcze jakieś uwagi to bardzo chętnie je poznam.

Co do feedback'u, to z jednej strony jego autor powiedział, że było ok, prezentacja miała dobry plan, a ja mówiłem jasno. Z drugiej stwierdził, że było "zbyt idealnie" i lepiej by było gdybym bardziej pokazywał emocje. Z tą opinią się zgadzam i wiem, że wynika to z tego, że poświęcam dużo czasu na przygotowania. W efekcie wynik końcowy może być trochę wyprany z uczuć, mało charakterystyczny, ale ja czuję się pewny siebie. Sądzę, że będę musiał nad tym popracować, chociaż będzie to walka z samym z sobą. Druga uwaga krytyczna dotyczyła tego, że za mało zaakcentowałem dlaczego mówię to co mówię, czemu uważam to za fajne. Kolejna lekcja do zapamiętania. Sądzę, że wynika to z tego, że zafiksowałem się na pewnym spojrzeniu na temat i zapomniałem, że będą mnie słuchać ludzie o różnych doświadczeniach.

Na koniec zamieszczam też link do mojej prezentacji.


26/05/2014

CTE i wydajność

Home

Ten post będzie o tym jak można zrobić sobie krzywdę stosując skądinąd bardzo fajne narzędzia. W tym przypadku mam na myśli CTE (ang. Common Table Expressions). Moim zdaniem stosowane z umiarem podnoszą czytelność kodu, z drugiej jednak strony użycie CTE może wpłynąć negatywnie na wydajność naszych zapytań.

Należy pamiętać o tym, że CTE nie mają fizycznej reprezentacji w tempdb tak jak tabele tymczasowe czy zmienne tabelaryczne. Na CTE można patrzeć jak na taki tymczasowy, nie zmaterializowany widok. Kiedy MSSQL wykonuje zapytanie i napotka CTE to odwołanie do tego CTE zastępuję jego definicją. W związku z tym jeśli dane CTE jest używane kilkakrotnie w danym zapytaniu to ten sam kod zostanie wykonany kilka razy i MSSQL tego nie optymalizuje. Ten kod pokazuje to zachowanie:

WITH  Test (Id)  
AS (SELECT  NEWID())
SELECT * FROM Test
UNION ALL
SELECT * FROM Test

Na ekran zostaną wypisane 2 różne identyfikatory. Gdyby Test było tabelą otrzymalibyśmy ten sam wynik. W skrajnych przypadkach może to spowodować, że zapytanie będzie koszmarnie wolne. Optymalizacja jest natomiast prosta. Wystarczy w pewnym momencie wrzucić dane do tabeli tymczasowej i dalej używać tej tabeli zamiast odwoływać się do CTE.

Jakiś czas temu widziałem przypadek gdzie dzięki temu jakże prostemu zabiegowi zapytanie zamiast wykonywać się kilka minut wykonywało się kilka sekund!

17/05/2014

Przydaś do rysowania wykresów

Home

Od jakiegoś czasu sporo pracuję z różnymi seriami danych numerycznych np.: sekwencje identyfikatorów metod wywołanych w programie. Serie takie mogą być bardzo długie i potrzebowałem narzędzia, które łatwo i szybko pozwoli mi je wizualizować czyli wygenerować wykres, tak abym mógł szybko ocenić co znajduje się w danym pliku. Kombajnów do rysowania wykresów jest dużo, choćby Excel, ale ja potrzebowałem czegoś jeszcze prostszego. Idealnie abym mógł po prostu zaznaczyć plik z danymi i z menu kontekstowego wybrać polecenie Narysuj wykres. Możliwe, że znalazłbym coś takiego, ale napisanie takie narzędzia samemu zajęło mi dosłownie chwilę.

Po pierwsze zdecydowałem się użyć biblioteki OxyPlot bo jest bardzo prosta w użyciu, a także radzi sobie z długimi seriami danych i pozwala narysować wiele różnych typów wykresów. W drugim kroku stworzyłem bardzo prostą aplikację z jednym oknem w WPF'ie. Do projektu dodałem referencje do bibliotek OxyPlot oraz OxyPlot.Wpf. XAML dla głównego i jedynego okna wygląda w następujący sposób:
<Window x:Class="DrawPlot.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
        xmlns:oxy="http://oxyplot.codeplex.com"
        xmlns:local="clr-namespace:DrawPlot"
        Title="DrawPlot" Height="350" Width="525">
    <Window.DataContext>
        <local:MainViewModel/>
    </Window.DataContext>
    <Grid>
        <oxy:Plot Model="{Binding MyModel}"/>
    </Grid>
</Window>
Trzeci krok to napisanie klasy MainViewModel, która stworzy i zainicjuje obiekt klasy PlotModel. Obiekt ten będzie zawierał informacje potrzebne do narysowania wykresu i przy pomocy binding'u zostanie przekazany do kontrolki Plot. W podstawowej wersji kod tej klasy jest bardziej niż prosty. Zakładam z góry określony format pliku wejściowego tj. każda linia zawiera jedną liczbę, a część ułamkowa musi być oddzielona od części całkowitej kropką. Użyłem klasy LineSeries czyli rysuję wykres liniowy. W poniższym kodzie celowo pominąłem również bardziej przyjazną obsługę błędów czyli jeśli coś się nie powiedzie to po prostu wyświetlam treść błędu i zamykam aplikację.
namespace DrawPlot
{
    public class MainViewModel
    {
        public PlotModel MyModel { get; private set; }

        public MainViewModel()
        {
            try
            {
                var args = Environment.GetCommandLineArgs();

                var lines = File.ReadAllLines(args[1]);

                var lineSeries = new LineSeries();
                for (var i = 0; i < lines.Length; ++i)
                    lineSeries.Points.Add(new DataPoint(i, Double.Parse(lines[i], CultureInfo.InvariantCulture)));

                var model = new PlotModel(Path.GetFileName(args[1]));
                model.Series.Add(lineSeries);
                MyModel = model;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
                Application.Current.Shutdown();
            }
        }
    }
}
Ostatni krok to dodanie nowej pozycji do menu kontekstowego. Uzyskałem to przy pomocy dodania odpowiedniego wpisu w rejestrze. W moim przypadku skompilowana aplikacja znajduje się w lokalizacji T:\bin\DrawPlot\DrawPlot.exe.
Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\*\Shell\Draw plot]

[HKEY_CLASSES_ROOT\*\Shell\Draw plot\command]
@="T:\\bin\\DrawPlot\\DrawPlot.exe \"%1\""
Narzędzie jest bardzo proste, ale spełnia swoje zadanie. Możne je również łatwo rozbudować na przykład użyć innego rodzaju serii aby narysować wykres kołowy, dodać obsługę innych formatów plików wejściowych itd.

09/05/2014

Niepozorna pomyłka

Home

Regularnie przeglądam różne portale na temat szeroko pojętego bezpieczeństwa w IT i co raz to dziwię się jak pozornie małe błędy programistów doprowadzają do poważnych problemów i awarii. Czytając ostatnio taki artykuł przypomniałem sobie rozmowę na temat implementacji stronicowania (ang. paging), czyli podziału dużej liczby rekordów na mniejsze porcje i przesyłanie tych porcji do klienta m.in. w celu ograniczenia ruchu w sieci.

W tym przypadku domyślna wielkość strony była skonfigurowana po stronie serwera, ale wysyłając żądanie klient mógł ją nadpisać i podać własną wielkość strony. Rozwiązanie w miarę standardowe, coś podobnego można zrobić odpytując Active Directory, o czym pisałem w tym artykule. Problem w tym przypadku polegał na tym, że wartość podana przez klienta nie była w żaden sposób sprawdzana po stronie serwera. Teoretycznie można więc było zażądać zwrócenie z serwera dowolnie dużej porcji danych. Innymi słowy, wysyłając żądanie wielkości kilku kilobajtów można było otrzymać odpowiedzieć wielkości na przykład 10 MB, a to przecież gigantyczne zwielokrotnienie. A gdyby takich żądań wysłać kilkadziesiąt, kilkaset, a może kilkanaście tysięcy, a do tego podmienić w żądaniu adres odbiorcy?

O czymś podobnym można było niedawno przeczytać w kontekście protokołu NTP. Wspomniany przeze mnie przypadek to w porównaniu z tym pikuś, gdyż użyty protokół komunikacyjny był specyficzny dla danej aplikacji, liczba użytkowników była niewielka itd. więc i zakres potencjalnego ataku był bardzo ograniczony. Z drugiej strony, skoro programista napisał taki kod raz, to może go napisać drugi, trzeci i w którymś momencie nie będzie to mała aplikacja. Na takie rzeczy należy, więc zawsze zwracać uwagę, nawet jeśli w danym przypadku wydaje się to nadmiarowe. Możliwe, że twórca kodu wybrał takie rozwiązanie celowo, ale równie dobrze może to być po prostu przeoczenie lub pomyłka.

29/04/2014

This feature has been disabled by your administrator

Home

Jakiś czas temu zacząłem używać pakiety Office 2013. Wszystko było w porządku dopóki nie zorientowałem się, że OneNote przestał synchronizować się z dyskiem SkyDrive. Dokładniej przy próbie zalogowania się do usługi krzyczał komunikatem:

This feature has been disabled by your administrator

Szybko znalazłem ten artykuł, z którego wynikało, że winne mogą być złe wpisy w rejestrze tj.:

HKEY_CURRENT_USER\Software\Microsoft\Office\15.0\Common\SignIn\SignInOptions
HKEY_CURRENT_USER\Software\Microsoft\Office\15.0\Common\Internet\UseOnlineContent

W moim przypadku ich brakowało, a więc je dodałem ustawiając na domyślne wartości (odpowiednio 0 oraz 3). Potem zrestartowałem OneNote'a. Po ponownym uruchomieniu aplikacja dłużej zastanawiała się co zrobić, ale w końcu znowu wyświetliła ten samym komunikat.

Postanowiłem, więc użyć programu Process Monitor i zobaczyć jakich kluczy używa jeszcze OneNote. Jest ich bardzo dużo, ale dopisało mi szczęście ponieważ zauważyłem,  że na samym początku odczytywany jest następujący klucz:

HKEY_CURRENT_USER\Software\Policies\Microsoft\Office\15.0\Common\SignIn\SignInOptions

Nie był on wymieniony we wspomnianym artykule, ale jak łatwo zauważyć ścieżka do tego klucza jest prawie taka sama jak do jednego ze wspomnianych kluczy. Postanowiłem, więc skonfigurować go w taki sam sposób czyli ustawić na wartość 0. Okazało się to strzałem w dziesiątkę i teraz OneNote działa mi poprawnie :)

26/04/2014

Jeśli nie na tabela tymczasowa to co?

Home

W poście Quiz - coś do poduszki opublikowałem zestaw pytań z Quiz'u jaki zorganizowałem dla kolegów z pracy. Od czasu do czasu wpadają mi do głowy różne zagadki dlatego postanowiłem, że co ciekawsze będę wrzucał ku pamięci na bloga. Oto pierwsza z nich.

Po wykonaniu poniższego kodu na ekran zostanie wypisana wartość 0. Czego należy użyć zamiast tabeli tymczasowej aby aby na ekran została wypisana wartość 2?
CREATE TABLE #Temp (
Id INT IDENTITY,
Value VARCHAR(10)
);

BEGIN TRANSACTION;
INSERT INTO #Temp VALUES('aaa');
INSERT INTO #Temp VALUES('bbb');
ROLLBACK;

SELECT COUNT(1) FROM #Temp;

DROP TABLE #Temp;
Pokaż/Ukryj odpowiedź

Zamiast tabeli tymczasowej należy użyć zmiennej tabelarycznej.
DECLARE @Temp TABLE (
Id INT IDENTITY,
Value VARCHAR(10)
);

BEGIN TRANSACTION;
INSERT INTO @Temp VALUES('aaa');
INSERT INTO @Temp VALUES('bbb');
ROLLBACK;

SELECT COUNT(1) FROM @Temp;

22/04/2014

A może coś do posłuchania...

Home

Czytam dużo, a nawet bardzo dużo i jeszcze 2-3 lata temu nie myślałem, że przestawię się z książek papierowych na e-book'i. W tej chwili korzystam już tylko z tych drugich. Co prawda nie pachną tak fajnie jak te papierowe, ale dzięki czytnikowi mogą mieć zawsze przy sobie całą bibliotekę, a przede wszystkim nie zajmują mi miejsca na półce, którego już mi brakuje ;) Ponieważ tak lubię czytać, a czytanie bezgłośne zajmuje mniej czasu to do niedawna sporadycznie korzystałem z audiobook'ów. Dodatkowo wiele audiobook'ów wymaga płyty CD i jak tego słuchać w drodze do pracy.

Napisałem do niedawna, bo ostatnio odkryłem serwis audioteka.pl, w którym można kupić audiobook'a i od razu pobrać go na telefon (na komputer zresztą też). Rzadko "reklamuję" komercyjne serwisy, ale jeśli ktoś dostarcza treść na bardzo wysokim poziomie to czemu nie chwalić. A słuchowisko "Niezwyciężony" na podstawie prozy Lema, od którego zacząłem swój kontakt z tym serwisem, jest po prostu świetne. Muzyka, efekty dźwiękowe, lektorzy, wszystko stoi dla mnie na światowym poziomie.

Początkowo myślałem, że wysłuchanie całości zajmie mi sporo czasu, że będę słuchał po kawałku w drodze do pracy lub w czasie biegania. Zamiast tego historia tak mnie wciągnęła, że całości wysłuchałem w ciągu niecałych 3 dni. Szczerze polecam! W tej chwili przerabiam "Cała prawda o Planecie KSI. Drugie spojrzenie na Planetę KSI" Zajdla oraz Dolores Claiborne King'a. To już typowe audiobook'i, ale też mi się podobają, w sam raz do jazdy samochodem albo na podróż do/z pracy.

18/04/2014

Notpron

Home

Skoro czytacie tego posta to do pewnego stopnia jesteście geekami, a skoro tak to chciałbym Wam polecić stronę, a właściwie grę notpron. Dowiedziałem się o niej niedawno i od razu mi się spodobała. To podobno jednak z najtrudniejszych gier tego typu na świecie. Zasady są natomiast bardzo proste. Zaczynasz od strony startowej i masz przejść do następnej, a aby to zrobić musisz podać login i hasło, które są odpowiedzią na zagadkę, zajrzeć do kodu strony, zmienić odpowiednio URL, przeczytać napis zakodowany Morsem itd. Gra składa się ze 140 poziomów, a ukończyło ją tylko około 30 osób z bardzo wielu, którzy próbowali. Jeśli więc lubcie wytężać szare komórki to zapraszam do zabawy.

10/04/2014

Jak komputer wpływa na na...

Home

W książce Płytki umysł. Jak internet wpływa na nasz mózg Nicholas Carr napisał, że Internet jest wspaniałym narzędzie, ale równocześnie wpływa na nasz sposób myślenia w niekoniecznie pozytywny sposób. Jako przykład podaje obniżoną możliwość skoncentrowania się na lekturze długich tekstów czy to artykułów czy książek.

Uważam, że niestety jest w tym dużo prawdy. Z wnioskami poszedłbym nawet dalej. Tutaj nie chodzi tylko o Internet, ale o używanie komputera w ogóle. Ostatnio zaobserwowałem u siebie skłonność do wykonywania nawet prostych obliczeń przy pomocy systemowego kalkulatora. Robię to wręcz bezrefleksyjnie. Niby nie ma w tym nic złego ponieważ w ten sposób ograniczam możliwość pomyłki. Z drugiej strony pamiętam czasy kiedy dużo bardziej skomplikowane zadania matematyczne rozwiązywałem w pamięci, na przykład zdarzało się, że wracając z uczelni liczyłem w głowie całki ;).

Komputer to pożyteczne narzędzie, ale rozleniwia. Postanowiłem, więc pilnować się i teraz nawet jeśli siedzę przed komputerem to staram się wykonywać obliczenia w głowie i ewentualnie później je sprawdzam. Mała rzecz, ale odzwyczaja od używania komputera do wszystkiego, nawet do rzeczy, które z powodzeniem można zrobić samemu.

Jestem ciekaw czy zauważacie u siebie takie lub podobne nawyki/tendencje spowodowane spędzaniem dużej ilości czasu przed komputerem? Jak sobie z tym radzicie?

06/04/2014

Zależności funkcyjne - kiedyś mnie tego uczono

Home

Ostatnia przeglądając pytania na stackoverflow.com natknąłem się na takie dotyczące wyznaczania kluczy głównych na podstawie zależności funkcyjnych. Uczono mnie tego na studiach, ale obecnie w praktyce tego nie stosuję i kiedy projektuję bazę danych to nie zaprzątam sobie tym głowy.

Pytanie skłoniło mnie  jednak do odświeżenia sobie wiedzy i sięgnąłem po klasykę w tej dziedzinie czyli "Systemy baz danych" Connollly'ego raz Begg'a. Po kilku minutach lektury okazało się, że w głowie pozostało więcej informacji niż się spodziewałem. Doszedłem jednak do wniosku, że może i zabawa z zależności funkcyjnymi jest dobra dla naszych szarych komórek, ale jednak jest to żmudny proces i fajnie by to zautomatyzować.

Trochę poszperałem w Internecie i znalazłem narzędzie dostępne on-line, które na podstawie zależności funkcyjnych wyznacza klucze kandydujące, weryfikuje w której postaci normalnej jest baza danych itp. Ma ograniczenia, ale zamieszczam link ku pamięci. A to dwa przykłady użycia tego narzędzi do rozwiązania pytań zadanych na stackoverflow.com:
A tak na marginesie. Czy w swojej praktyce zawodowej stosujecie lub znacie kogoś kto stosuje przy projektowaniu relacyjnych baz danych zależności funkcyjne i na tej podstawie sprawdza czy baza danych jest w odpowiedniej postaci normalnej?

29/03/2014

Słów kilka na temat Attach to Process

Home

Post z serii ku pamięci, o poleceniu Debug->Attach to Processz, które umożliwia podczepienie się pod już uruchomiony program i jego debugowanie, a które znane jest chyba wszystkim programistom pracującym z Visual Studio. Rzadziej stosowaną i znaną opcją Attach to Process jest możliwość wybrania rodzaju kodu jaki chcemy debugować (Native, Managed, Script...). Służy do tego okno Select Code Type, które pojawia się po wybraniu przycisku Select. Domyślnie typ kodu określany jest automatycznie i w większości wypadków jest to najlepsza opcja, można to jednak zmienić i wybrać debugowanie kodu na przykład tylko dla platformy .NET w określonej wersji.


Problem z tą opcją jest taki, że jak już się jej użyje to łatwo o tym zapomnieć,a Visual Studio zapamiętuje wybrane ustawienia. Załóżmy, że wybraliśmy debugowanie kodu zarządzanego w wersji 3.5. Wszystko fajnie działa, ale po paru dniach chcemy debugować kod dla platformy 4.0. Podczepiamy się pod wybrany proces, a tu nic się nie dzieje, żaden z naszych breakpoint'ów nie zadział, a Visual Studio nie zgłasza żadnych problemów.

I bądź tu mądry człowieku w takiej sytuacji, albo sobie przypomnimy, że zmieniliśmy tą opcję (o ile wiemy jak dokładnie działa) albo będziemy sobie rwać włosy z głowy.

27/03/2014

SizeLimit, PageSize i dokumentacja

Home

W poście wspomniałem o właściwościach DirectorySearcher.SizeLimit oraz DirectorySearcher.PageSize, których poprawne ustawienie zapewnia, że z bazy danych AD można pobrać więcej obiektów niż ustawiony na serwerze limit. Tym razem chciałbym sprecyzować do czego służą obie właściwości bo moim zdaniem dokumentacja nie jest precyzyjna, co potwierdza zresztą spora liczba pytań w Internecie na ten temat.

Otóż SizeLimit określa maksymalną liczbę obiektów jaka może zostać zwrócona w wyniku wyszukiwania (zero oznacza brak limitu) przy czym limit ustawiony na serwerze ma priorytet. PageSize służy natomiast do skonfigurowania stronicowania i wartość różna od zera je włącza. Na przykład wartość 100 oznacza, że obiekty będą zwracane w paczkach po 100, przy czym .NET jest na tyle miły, że samemu obsługuje doczytywanie kolejnych stron.

Teraz spójrzmy na przykład. W bazie danych AD znajdowało się 1580 obiektów spełniających kryteria wyszukiwania, a limit ustawiony na serwerze wynosił 1500. Poniższa tabelka pokazuje ile obiektów zwróci zapytanie w zależności od ustawień.

SizeLimitPageSizeLiczba obiektów w ADLiczba zwróconych obiektówUwagi
0015801500Brak stronicowania + domyślny limit z serwera
10001580100Brak stronicowania + limit określony przez nas
010015801580Stronicowanie włączone
20010015801580Stronicowanie włączone + limit określony przez nas
1002001580100Stronicowanie włączone + limit określony przez nas

Dwa ostatnie scenariusze są trochę zagadkowe. W obu przypadkach obie właściwości są różne od zera, ale liczba zwróconych obiektów jest inna tj. jeśli SizeLimit > PageSize to z AD pobrano wszystkie dostępne obiekty, a w przeciwnym wypadku tyle ile wynosił SizeLimit. Przypuszczam, że DirectorySearcher działa tak, że pobiera dane póki nie zostanie przekroczony limit. W pierwszym przypadku przy pobieraniu kolejnych stron liczba pobieranych obiektów nie przekracza limitu, a więc udało się odczytać wszystko. W drugim wypadku już przy pobraniu pierwszej strony liczba obiektów przekroczyła limit i dalsze pobieranie zostało zakończone. Pewnie można było to zaimplementować inaczej, ale cóż zrobić i po prostu warto o tym wiedzieć.

18/03/2014

Jak napisać szybki program pobierający dane z AD

Home

Post ten dotyczy tematu efektywnego pobierania danych z Active Directory. Załóżmy, że chcemy pobrać listę użytkowników przy czym interesują nas tylko niektóre właściwości, które ich opisują. Pokażę trzy niewiele różniące się z pozoru sposoby odczytania potrzebnych nam danych. Pozornie ponieważ te trzy podejścia znacząco różnią się wydajnością. W celu zademonstrowania różnic napisałem prostą klasę ADTester. Zawiera ona tylko jedną metodę Run w parametrach, której określamy tryb działania oraz liczbę obiektów do pobrania. Różnica pomiędzy trybami jest następująca:
  • W trybie szybkim (Mode.Fast) dla każdego obiektu z bazy danych Active Directory pobrane zostają tylko wybrane 4 właściwości, a to dzięki zastosowaniu właściwości DirectorySearcher.PropertiesToLoad
  • W trybie normalnym (Mode.Normal) dla każdego obiektu z bazy danych Active Directory pobrane zostają wszystkie dostępne dla danego obiektu właściwości.
  • W trybie wolnym (Mode.Slow) zamiast użyć danych zawartych w obiektach SearchResult korzystam z metody SearchResult.GetDirectoryEntry.
Poniższa tabela pokazuje czas działania programu w ms w zależności od trybu działania oraz liczby obiektów do pobrania:

maxNumberOfObjectsMode.FastMode.NormalMode.Slow
1003634637507
2004076611600
500797221239323
10001353306075935

W najlepszym przypadku tryb prosty jest 2.7 szybszy niż tryb normalny i prawie 60 razy szybszy niż tryb wolny. Różnica jest wręcz powalająca, a wnioski nasuwają się same.
  • Jeśli z góry wiemy jakie właściwości nas interesują to używajmy właściwości DirectorySearcher.PropertiesToLoad.
  • Korzystajmy z danych zwróconych przez klasę DirectorySearcher w postaci obiektów SearchResult.
  • Tylko jeśli to absolutnie konieczne korzystajmy z metody SearchResult.GetDirectoryEntry. Taka potrzeba zachodzi na przykład wtedy jeśli chcemy zmodyfikować dane w AD.
Na koniec jeszcze jedna uwaga. Wyniki czasu działania programu będą się różnić w zależności od tego gdzie znajduje się serwer Active Directory. W moim przypadku znajdował się on poza krajem. Na koniec zamieszczam kod programu do własnych testów:
public class ADTester
{
    public enum Mode { Fast, Normal, Slow }

    public void Run(Mode mode, int maxNumberOfObjects)
    {
        var ldapPath = "YOUR_LDAP_PATH";

        using (var root = new DirectoryEntry(ldapPath))
        {
            using (var searcher = new DirectorySearcher(root)
                    {
                        Filter = "(&(objectClass=user))", SearchScope = SearchScope.Subtree, SizeLimit = maxNumberOfObjects
                    })
            {
                if (mode == Mode.Fast)
                    searcher.PropertiesToLoad.AddRange(new[]{ "displayName","name", "pwdLastSet","userAccountControl" });

                using (SearchResultCollection searchResult = searcher.FindAll())
                {
                    foreach (SearchResult user in searchResult)
                    {
                        if (mode != Mode.Slow)
                        {
                            var displayName = user.Properties["displayName"];
                            ...
                        }
                        else
                        {
                            var entry = user.GetDirectoryEntry();
                            var displayName = entry.Properties["displayName"];
                            ...
                        }
                    }
                }
            }
        }
    }
}