-
161. Data: 2011-10-12 23:03:44
Temat: Re: Dlaczego w branży rozrywkowej najsłabiej płacą?
Od: Wojciech Jaczewski <w...@o...pl>
Andrzej Jarzabek wrote:
> Powiedzmy
> masz licznik czegośtam w postaci inta, który musi być podbijany z
> kilku różnych wątków, więc dodajesz mutex i komentarz, że ten mutex
> służy do zabezpieczania licznika, który jest podbijany z kilku wątków.
> A może zamiast tego lepiej zrobić klasę i nazwać ją ThreadSafeCounter,
> to komentarz przestanie być potrzebny.
Ten przykład jest bezsensowny.
Jeśli jest użyty mutex, to wiadomo że jest synchronizacja między wątkami i
nie trzeba tego dodatkowo komentować.
Natomiast jeśli komentarz zawiera "licznik jest modyfikowany przez wątki A,B
oraz X", to użycie ThreadSafeCounter tego komentarza nie zastąpi.
Swoją drogą... jaki interfejs do ThreadSafeCounter uznałbyś za właściwy:
atomic_inc(counter) / synchronized_inc(counter), czy ++counter ?
> Nie uważam, że to jest jakaś złota zasada czy srebrna kula, ale jednak
> zauważyłem, że często tak faktycznie jest: mam do czynienia z funkcją,
> w której bez komentarzy trudno byłoby rozkminić jakiś istotny aspekt,
> dzięki komentarzom jest to możliwe, ale jednak jest możliwe i byłoby
> lepiej, gdyby te aspekty były wprost wyrażone w kodzie.
Czasem zdarza mi się (nie w ramach pracy) używać jakichś bardzo
specyficznych algorytmów, np. z zakresu przetwarzania sygnałów. Robię to
bardzo rzadko i bez komentarzy absolutnie nie domyśliłbym się dlaczego jest
tak a nie inaczej, bo czasem ciężko zrozumieć jakim cudem dany algorytm w
ogóle działa i kroki do osiągnięcia celu wydają się na prawdę dziwne
(polecam np. "band edge component maximization").
-
162. Data: 2011-10-13 00:31:21
Temat: Re: Dlaczego w branży rozrywkowej najsłabiej płacą?
Od: Andrzej Jarzabek <a...@g...com>
On 12/10/2011 23:51, Wojciech Jaczewski wrote:
> Andrzej Jarzabek wrote:
>>
>> Do pisania oprogramowania niezawodnego potrzebna jest abstrakcja i
>> czytelność kodu. Kod, który sobie po cichu zakłada, że int ma 32 bity ma
>> niedobory w jednym i drugim.
>
> Mowa o funkcjach systemowych, a nie zakładaniu ile bitów ma int. Trudno mi
> sobie wyobrazić, aby ktoś korzystał z tego założenia, mając do dyspozycji
> alternatywę w postaci int32_t.
Ja mówię o tym i o tym. A przykład z intem z życia wzięty: profesor to
właśnie napisał ledwie kilka dni temu.
> Bardzo jest dla mnie zagadkowe, że tak wiele osób uważa, iż interfejs
> opracowywany i testowany przez kilka dziesięcioleci koniecznie wymaga
> opakowania w jakąś abstrakcję, bo inaczej korzystać z niego nie należy...
Cała sztuka porządnego pisania większych programów polega na przejściu
między końcową funkcjonalnością a wykorzystywanymi bibliotekami przez
wiele warstw abstrakcji - i wyrażeniu tych abstrakcji w kodzie.
> Nie wiem jak jest z interfejsami windowsowymi, bo z nimi doświadczenie
> miałem znikome, ale interfejsy POSIX-owe są bardzo dobre. Abstrakcja w
> postaci deskryptora pliku - coś wspaniałego.
Właśnie kaszanka. Przykładowo, jeśli mówimy o niezawodności, to sam
fakt, że deskryptor jest typem liczbowym ułatwia popełnienie błędu
polegającego na tym, że jako deskryptora używasz liczby, która
deskryptorem nie jest.
Kolejny problem z deskryptorami jest taki, że łatwo przez pomyłkę o kod,
który w pewnych sytuacjach nie zamyka deskryptorów, które otwiera, np.
na wskutek early return czy rzucenia wyjątkiem.
Dalej, jeśli np. próbujesz zrobić formatowane wyjście na deskryptor
przez ręczne babranie się z alokowaniem buforów, kopiowaniem i
sklejaniem stringów, jakimiś dodatkowymi funkcjami do formatowania
liczb, to masz całą masę pomyłek, które łatwo popełnić z katastrofalnymi
skutkami: dangling pointers, bufer overflow, memory leaks, czy dany
string jest null-terminated czy nie itd. itp.
> Również dla krytykowanych przez niektórych uniksowych sygnałów daje się
> czasem znaleźć fajne zastosowanie, gdzie bez sygnałów program byłby dużo
> bardziej zawiły.
Również dla sygnałów unixowych są abstrakcje, które będą zwykle
sensowniejsze do stosowania niż systemowe API, na przykład w modelu
programu, który obsługuje jakieś zdarzenia sygnał może wsytępować po
prostu jako jeden z typów "zdarzeń".
Kolejne unixowe API którego łatwo przez pomyłkę użyć nieprawidłowo i
rozwalić program to choćby pthreads.
> Oczywiście w każdym API dałoby się wskazać jakieś wady, ale na jakiej
> podstawie miałbym przypuszczać, że zrobiona w dużo krótszym czasie
> abstrakcja będzie istotnie lepsza?
Dlaczego zakładasz, że w dużo krótszym czasie? Jeśli mówimy o
generycznych abstrakcjach na system operacyjny, to jak pisałem są to
albo istniejące i szeroko stosowane biblioteki third party, albo też
rozwiązanie in house stosowane w wielu produktach danej firmy i często
rozwijane latami.
> Dla mnie "opakowywanie systemowego API" to takie wynalazki jak np. QSocket z
> Qt. Dla programów międzyplatformowych - może ma sens. Dla programów na jedną
> platformę - żadnego.
Nie znam Qt, więc się nie wypowiem. Zapewne ewidentną zaletą tago
rozwiązania jest to, że nie da się pomylić inta zawierającego deskryptor
z intem zawierający liczbę przeczytanych bajtów.
> Interfejs udostępniany przez system (POSIX) jest
> lepszy. Testowałem też kiedyś Boost::Asio - nieczytelny w porównaniu z tym,
> co można zrobić korzystająć bezpośrednio z POSIX. I ciężki do integracji,
> jeśli program ma reagować jednocześnie na zdarzenia innego typu.
Nie mam doświadczenia z tym, ale pracowałem kiedyś z event handling
frameworkiem opakowującym unixowe API i praktyka jednak była taka, że
nowy typ zdarzeń był do niego dodawany dużo, dużo rzadziej niż pisanie
programów, które korzystały tylko z już obsługiwanych typów.
>> Pracowałem przy kilku większych projektach i normą było korzystanie z
>> biblioteki opakowującej API, chociaż nie takiej, która jest tworzona pod
>> konkretny projekt: albo były to biblioteki third party, albo robione w
>> firmie na potrzeby wielu projektów. To, czy korzystają czy nie
>> korzystają ze wszystkich funkcji API nie ma znaczenia - istotne jest, że
>> reprezentują wyższy poziom abstrakcji.
>
> Czy wyższy poziom abstrakcji w sensie np. baza danych zamiast bezpośredniego
> czytania z plików i synchronizowania przy pomocy fcntl/flock? czy tylko
> pozbierane kilka funkcji systemowych do kupy i pozamykane w obiekty?
Można mieć różne poziomy abstrakcji do różnych rzeczy. Dla pliku
chociażby możesz mieć poziom abstrakcji mówiący, że jest strumieniem, że
po zakończeniu działań na nim jest zamykany, na innym poziomie
abstrakcji możesz mieć wyrażony fakt, że jest fizycznym plikiem na
dysku, że np. program zapisuje określone dane do pliku o określonej
ścieżce, na wyższych poziomach masz semantykę: plik może być bazą
danych, logiem, dokumentem o zadanym formacie, plikiem konfiguracyjnym
itd. Brak odpowiedniego opakowania może choćby spowodować błąd
polegający na tym, że ktoś potraktuje plik XML z fiflakami jako plik
binarny zawierający bazę danych fiflaków lub odwrotnie.
-
163. Data: 2011-10-13 00:39:50
Temat: Re: Dlaczego w branży rozrywkowej najsłabiej płacą?
Od: Andrzej Jarzabek <a...@g...com>
On 13/10/2011 00:03, Wojciech Jaczewski wrote:
> Andrzej Jarzabek wrote:
>
>> Powiedzmy
>> masz licznik czegośtam w postaci inta, który musi być podbijany z
>> kilku różnych wątków, więc dodajesz mutex i komentarz, że ten mutex
>> służy do zabezpieczania licznika, który jest podbijany z kilku wątków.
>> A może zamiast tego lepiej zrobić klasę i nazwać ją ThreadSafeCounter,
>> to komentarz przestanie być potrzebny.
>
> Ten przykład jest bezsensowny.
> Jeśli jest użyty mutex, to wiadomo że jest synchronizacja między wątkami i
> nie trzeba tego dodatkowo komentować.
Ale nie wiadomo, co konkretnie dany mutex synchronizuje.
> Swoją drogą... jaki interfejs do ThreadSafeCounter uznałbyś za właściwy:
> atomic_inc(counter) / synchronized_inc(counter), czy ++counter ?
Z zasady nie lubię skrótów typu 'inc'. Zrobiłbym ++counter albo
counter.increment(), ale raczej nie traciłbym czasu na zastanawianie się
nad tym zbyt długo.
> Czasem zdarza mi się (nie w ramach pracy) używać jakichś bardzo
> specyficznych algorytmów, np. z zakresu przetwarzania sygnałów. Robię to
> bardzo rzadko i bez komentarzy absolutnie nie domyśliłbym się dlaczego jest
> tak a nie inaczej, bo czasem ciężko zrozumieć jakim cudem dany algorytm w
> ogóle działa i kroki do osiągnięcia celu wydają się na prawdę dziwne
> (polecam np. "band edge component maximization").
Ale co ci tutaj dają komentarze? Czy może są to komentarze typu:
/* Wiem, że to wygląda bez sensu, ale uwierz mi, tak właśnie ma być */
?
-
164. Data: 2011-10-13 09:10:53
Temat: Re: Dlaczego w branży rozrywkowej najsłabiej płacą?
Od: Wojciech Jaczewski <w...@o...pl>
Andrzej Jarzabek wrote:
>> Nie wiem jak jest z interfejsami windowsowymi, bo z nimi doświadczenie
>> miałem znikome, ale interfejsy POSIX-owe są bardzo dobre. Abstrakcja w
>> postaci deskryptora pliku - coś wspaniałego.
>
> Właśnie kaszanka. Przykładowo, jeśli mówimy o niezawodności, to sam
> fakt, że deskryptor jest typem liczbowym ułatwia popełnienie błędu
> polegającego na tym, że jako deskryptora używasz liczby, która
> deskryptorem nie jest.
Teoretycznie możliwe, w praktyce jeszcze tego nie widziałem.
> Kolejny problem z deskryptorami jest taki, że łatwo przez pomyłkę o kod,
> który w pewnych sytuacjach nie zamyka deskryptorów, które otwiera, np.
> na wskutek early return czy rzucenia wyjątkiem.
Jeśli akurat mi w danym momencie na czymś takim zależy i akurat używam C++,
to robię klasę o nazwie fd_guard_t (analogiczną do lock_guard z nowego
standardu). I dalej operuję na nie-opakowanym deskryptorze.
> Dalej, jeśli np. próbujesz zrobić formatowane wyjście na deskryptor
> przez ręczne babranie się z alokowaniem buforów, kopiowaniem i
> sklejaniem stringów, jakimiś dodatkowymi funkcjami do formatowania
> liczb, to masz całą masę pomyłek, które łatwo popełnić z katastrofalnymi
> skutkami: dangling pointers, bufer overflow, memory leaks, czy dany
> string jest null-terminated czy nie itd. itp.
Do formatowania do pliku, czy na stdout, rzeczywiście może być wygodniejsze
użycie FILE* lub iostream. Natomiast ich interfejs nie pasuje do sensownej
obsługi np. połączeń sieciowych, gdzie przed wywołaniem funkcji nie wiadomo
ile bajtów będzie się dało w tej chwili odczytać/zapisać. W takim wypadku
lepszy jest jawny podział na dwie części: formatowanie do pamięci, a
następnie przerzucenie w jednym lub wielu etapach przez deskryptor.
>> Również dla krytykowanych przez niektórych uniksowych sygnałów daje się
>> czasem znaleźć fajne zastosowanie, gdzie bez sygnałów program byłby dużo
>> bardziej zawiły.
>
> Również dla sygnałów unixowych są abstrakcje, które będą zwykle
> sensowniejsze do stosowania niż systemowe API, na przykład w modelu
> programu, który obsługuje jakieś zdarzenia sygnał może wsytępować po
> prostu jako jeden z typów "zdarzeń".
Można. Z tym że nie wiem, czemu miałoby to być "sensowniejsze". Jest tak
samo sensowne jak POSIX-owe, tylko z innym interfejsem.
> Kolejne unixowe API którego łatwo przez pomyłkę użyć nieprawidłowo i
> rozwalić program to choćby pthreads.
Wątków akurat unikam jak mogę. Łatwiej się testuje dwa współpracujące
procesy, niż dwa współpracujące wątki. API opakowujące pthreads nie
wniosłoby mi w tej kwestii absolutnie nic.
>> Oczywiście w każdym API dałoby się wskazać jakieś wady, ale na jakiej
>> podstawie miałbym przypuszczać, że zrobiona w dużo krótszym czasie
>> abstrakcja będzie istotnie lepsza?
>
> Dlaczego zakładasz, że w dużo krótszym czasie?
Porównuję do czasu od którego istnieją systemy Uniksowe. Większość założeń
jest już bardzo starych, ale jednak przez bardzo wiele lat standard się
jeszcze rozwija.
>> Dla mnie "opakowywanie systemowego API" to takie wynalazki jak np.
>> QSocket z Qt. Dla programów międzyplatformowych - może ma sens. Dla
>> programów na jedną platformę - żadnego.
>
> Nie znam Qt, więc się nie wypowiem. Zapewne ewidentną zaletą tago
> rozwiązania jest to, że nie da się pomylić inta zawierającego deskryptor
> z intem zawierający liczbę przeczytanych bajtów.
Nie widziałem jeszcze przypadku pomylenia inta zawierającego deskryptor z
intem zawierającym liczbę przeczytanych bajtów. Ta zaleta jest czysto
teoretyczna.
-
165. Data: 2011-10-13 09:58:40
Temat: Re: Dlaczego w branży rozrywkowej najsłabiej płacą?
Od: Wojciech Jaczewski <w...@o...pl>
Andrzej Jarzabek wrote:
>>> Powiedzmy
>>> masz licznik czegośtam w postaci inta, który musi być podbijany z
>>> kilku różnych wątków, więc dodajesz mutex i komentarz, że ten mutex
>>> służy do zabezpieczania licznika, który jest podbijany z kilku wątków.
>>> A może zamiast tego lepiej zrobić klasę i nazwać ją ThreadSafeCounter,
>>> to komentarz przestanie być potrzebny.
>>
>> Ten przykład jest bezsensowny.
>> Jeśli jest użyty mutex, to wiadomo że jest synchronizacja między wątkami
>> i nie trzeba tego dodatkowo komentować.
>
> Ale nie wiadomo, co konkretnie dany mutex synchronizuje.
Synchronizuje ten fragment, wokół którego jest użyty. To w kodzie widać.
>> Swoją drogą... jaki interfejs do ThreadSafeCounter uznałbyś za właściwy:
>> atomic_inc(counter) / synchronized_inc(counter), czy ++counter ?
>
> Z zasady nie lubię skrótów typu 'inc'. Zrobiłbym ++counter albo
> counter.increment(), ale raczej nie traciłbym czasu na zastanawianie się
> nad tym zbyt długo.
Wg mnie słówko atomic, bądź synchronized jest konieczne - aby wyraźnie
oznaczyć, że to jest operacja z synchronizacją między wątkami. I że w razie
problemów z wydajnością na wielu procesorach, należy się przyjrzeć także
temu fragmentowi.
>> Czasem zdarza mi się (nie w ramach pracy) używać jakichś bardzo
>> specyficznych algorytmów, np. z zakresu przetwarzania sygnałów. Robię to
>> bardzo rzadko i bez komentarzy absolutnie nie domyśliłbym się dlaczego
>> jest tak a nie inaczej, bo czasem ciężko zrozumieć jakim cudem dany
>> algorytm w ogóle działa i kroki do osiągnięcia celu wydają się na prawdę
>> dziwne (polecam np. "band edge component maximization").
>
> Ale co ci tutaj dają komentarze? Czy może są to komentarze typu:
> /* Wiem, że to wygląda bez sensu, ale uwierz mi, tak właśnie ma być */
> ?
Np. "Filtr pasmowo-przepustowy wokół częstotliwości 2400Hz, ale ze
współczynnikami zespolonymi, tak aby w jego widmie częstotliwościowym były
niezerowe wartości wyłącznie dla częstotliwości dodatnich".
-
166. Data: 2011-10-13 14:13:02
Temat: Re: Dlaczego w branży rozrywkowej najsłabiej płacą?
Od: Andrzej Jarzabek <a...@g...com>
On Oct 13, 10:58 am, Wojciech Jaczewski <w...@o...pl> wrote:
> Andrzej Jarzabek wrote:
>
> > Ale nie wiadomo, co konkretnie dany mutex synchronizuje.
>
> Synchronizuje ten fragment, wokół którego jest użyty. To w kodzie widać.
Przecież defnicja mutexa jest gdzie indziej niż kod, gdzie jest
używany.
> > Z zasady nie lubię skrótów typu 'inc'. Zrobiłbym ++counter albo
> > counter.increment(), ale raczej nie traciłbym czasu na zastanawianie się
> > nad tym zbyt długo.
>
> Wg mnie słówko atomic, bądź synchronized jest konieczne - aby wyraźnie
> oznaczyć, że to jest operacja z synchronizacją między wątkami. I że w razie
> problemów z wydajnością na wielu procesorach, należy się przyjrzeć także
> temu fragmentowi.
Według mnie jeśli klasa nazywa się ThreadSafeCounter i można ją tylko
np. inkrementować, resetować i sprawdzać wartość, to oczywistym jest,
że te wszystkie operacje są thread-safe.
> > Ale co ci tutaj dają komentarze? Czy może są to komentarze typu:
> > /* Wiem, że to wygląda bez sensu, ale uwierz mi, tak właśnie ma być */
> > ?
>
> Np. "Filtr pasmowo-przepustowy wokół częstotliwości 2400Hz, ale ze
> współczynnikami zespolonymi, tak aby w jego widmie częstotliwościowym były
> niezerowe wartości wyłącznie dla częstotliwości dodatnich".
Nie znam się na tym, ale zasadniczo wydaje mi się, że tak jak liczba
zespolona może być wyrażona odpowiednim typem, tak możesz mieć typ
generyczny Współczynnik (być może zdefiniowany wewnątrz klasy filtra
czy przestrzeni nazw 'Filtr', żeby było jasne, że chodzi o
współczynnik filtra), możesz mieć konkretną klasę
WspółczynnikZespolony parametryzującą szablon FiltrPasmowoPrzepustowy,
który ma konstruktor przyjmujący argument typu Częstotliwość i tak
dalej.
-
167. Data: 2011-10-13 16:12:38
Temat: Re: Dlaczego w branży rozrywkowej najsłabiej płacą?
Od: Andrzej Jarzabek <a...@g...com>
On Oct 13, 10:10 am, Wojciech Jaczewski <w...@o...pl> wrote:
> Andrzej Jarzabek wrote:
>
> > Kolejny problem z deskryptorami jest taki, że łatwo przez pomyłkę o kod,
> > który w pewnych sytuacjach nie zamyka deskryptorów, które otwiera, np.
> > na wskutek early return czy rzucenia wyjątkiem.
>
> Jeśli akurat mi w danym momencie na czymś takim zależy i akurat używam C++,
> to robię klasę o nazwie fd_guard_t (analogiczną do lock_guard z nowego
> standardu). I dalej operuję na nie-opakowanym deskryptorze.
Lock guard jest specjalnie tak zrobiony, żeby ten sam lock mógł być
używany po wyjściu z zakresu, gdzie jest zalockowany przez tego
guarda. Zastosowanie tego samego narzedzia do deskryptora, gdzie
takich wymagań nie ma (nie masz sytuacji, gdzie po zamknięciu
deskruptora próbujesz go jeszcze raz otworzyć albo coś w tym stylu)
jest suboptymalne - co prawda rozwiązuje ci bezpośrednio ten problem,
ale umożliwia powstanie różnych błędów, które bez tego byłyby
niemożliwe - choćby że w trakcie modyfikacji kodu ktoś zmodyfikuje
zakresy tak, że deskryptor będzie zdefiniowany w innym niż jego lock,
albo ktoś gdzies wstawi zamykanie tego deskryptora ręcznie.
> > Dalej, jeśli np. próbujesz zrobić formatowane wyjście na deskryptor
> > przez ręczne babranie się z alokowaniem buforów, kopiowaniem i
> > sklejaniem stringów, jakimiś dodatkowymi funkcjami do formatowania
> > liczb, to masz całą masę pomyłek, które łatwo popełnić z katastrofalnymi
> > skutkami: dangling pointers, bufer overflow, memory leaks, czy dany
> > string jest null-terminated czy nie itd. itp.
>
> Do formatowania do pliku, czy na stdout, rzeczywiście może być wygodniejsze
> użycie FILE* lub iostream. Natomiast ich interfejs nie pasuje do sensownej
> obsługi np. połączeń sieciowych, gdzie przed wywołaniem funkcji nie wiadomo
> ile bajtów będzie się dało w tej chwili odczytać/zapisać. W takim wypadku
> lepszy jest jawny podział na dwie części: formatowanie do pamięci, a
> następnie przerzucenie w jednym lub wielu etapach przez deskryptor.
streambuf ma buforowanie, więc tak to właśnie raczej powinno działać.
Jednak musisz przyznać, że z punktu widzenia kogoś, kto chce
sfotmatować komunikat i przesłać go po połączeniu albo z kolei
odczytać komunikat zakończony np. CR-LF, niekoniecznie takie szczegóły
implementacyjne są interesujące.
> > Również dla sygnałów unixowych są abstrakcje, które będą zwykle
> > sensowniejsze do stosowania niż systemowe API, na przykład w modelu
> > programu, który obsługuje jakieś zdarzenia sygnał może wsytępować po
> > prostu jako jeden z typów "zdarzeń".
>
> Można. Z tym że nie wiem, czemu miałoby to być "sensowniejsze". Jest tak
> samo sensowne jak POSIX-owe, tylko z innym interfejsem.
Sensowniejsze o tyle, że masz rodzieloną logikę mówiącą jak na jakieś
zdarzenie trzeba reagować z samą obsługą i rozstrzyganiem tego, na co
jak trzeba zareagować. W związku z tym komuś dodającemu nowy typ
zdarzenia do obsługi trudniej będzie zepsuć jakiś aspekt istniejącej
logiki, niż jeśli modyfikuje dużą pętlę z 'jeśli sygnał to tamto,
jeśli coś na sockecie to śmamto, jeśli minęło tyle czasu to owamto".
> > Kolejne unixowe API którego łatwo przez pomyłkę użyć nieprawidłowo i
> > rozwalić program to choćby pthreads.
>
> Wątków akurat unikam jak mogę. Łatwiej się testuje dwa współpracujące
> procesy, niż dwa współpracujące wątki. API opakowujące pthreads nie
> wniosłoby mi w tej kwestii absolutnie nic.
W rzeczywistości wątków się używa bo to wydajny i elastyczny sposób na
zrównoleglenie aplikacji. W przypadku stosowania oddzielnych procesów
samo kszta związane z narzutem na ich odpalanie, IPC itd. często
zabijają wszelkie korzyści ze zrównoleglenia.
> >> Oczywiście w każdym API dałoby się wskazać jakieś wady, ale na jakiej
> >> podstawie miałbym przypuszczać, że zrobiona w dużo krótszym czasie
> >> abstrakcja będzie istotnie lepsza?
>
> > Dlaczego zakładasz, że w dużo krótszym czasie?
>
> Porównuję do czasu od którego istnieją systemy Uniksowe. Większość założeń
> jest już bardzo starych, ale jednak przez bardzo wiele lat standard się
> jeszcze rozwija.
Owszem, standard się rozwija, ale zachowanie wsteczniej
kompatybilności z latami 70-tymi jest ważniejsze niż tworzenie
lepszych abstrakcji. Raczej bym powiedział, że rozwój koncentruje się
na dostarczaniu nowej funkcjonalności, słusznie zakładając, że
potrzeba abstrakcji będzie zaspokojona przez biblioteki opakowujące.
Już nie mówiąc o tym, że API jest w C, a to język dosyć kiepsko
nadający sie do wyrażania abstrakcji czy budowania struktur
wspierających niezawodność.
> > Nie znam Qt, więc się nie wypowiem. Zapewne ewidentną zaletą tago
> > rozwiązania jest to, że nie da się pomylić inta zawierającego deskryptor
> > z intem zawierający liczbę przeczytanych bajtów.
>
> Nie widziałem jeszcze przypadku pomylenia inta zawierającego deskryptor z
> intem zawierającym liczbę przeczytanych bajtów. Ta zaleta jest czysto
> teoretyczna.
Ja widziałem wiele przypadków błedów spowodowanych pomyleniem x-a
robiącego A z x-em robiącym B przez kogoś, kto nigdy przedtem nie
widział takiego przypadku. Mnie też to oczywiście wiele razy
dotyczyło.
-
168. Data: 2011-10-15 21:51:59
Temat: Re: Dlaczego w branży rozrywkowej najsłabiej płacą?
Od: Wojciech Jaczewski <w...@o...pl>
Andrzej Jarzabek wrote:
>> > Kolejny problem z deskryptorami jest taki, że łatwo przez pomyłkę o
>> > kod, który w pewnych sytuacjach nie zamyka deskryptorów, które otwiera,
>> > np. na wskutek early return czy rzucenia wyjątkiem.
>>
>> Jeśli akurat mi w danym momencie na czymś takim zależy i akurat używam
>> C++, to robię klasę o nazwie fd_guard_t (analogiczną do lock_guard z
>> nowego standardu). I dalej operuję na nie-opakowanym deskryptorze.
>
> Lock guard jest specjalnie tak zrobiony, żeby ten sam lock mógł być
> używany po wyjściu z zakresu, gdzie jest zalockowany przez tego
> guarda. Zastosowanie tego samego narzedzia do deskryptora, gdzie
> takich wymagań nie ma (nie masz sytuacji, gdzie po zamknięciu
> deskruptora próbujesz go jeszcze raz otworzyć albo coś w tym stylu)
> jest suboptymalne - co prawda rozwiązuje ci bezpośrednio ten problem,
> ale umożliwia powstanie różnych błędów, które bez tego byłyby
> niemożliwe - choćby że w trakcie modyfikacji kodu ktoś zmodyfikuje
> zakresy tak, że deskryptor będzie zdefiniowany w innym niż jego lock,
Mało prawdopodobne, aby w wyniku zwykłej pomyłki a nie celowego psucia
rozdzielić taką sekwencję do osobnych bloków:
int fd = jakas_tam_metoda_tworzenia_fd();
fd_guard_t fd_guard(fd);
> albo ktoś gdzies wstawi zamykanie tego deskryptora ręcznie.
Opakowanie deskryptora w obiekt też wiele na to nie pomoże, bo np. dalsza
część funkcji chciała coś wysłać przez deskryptor, a on zamknięty.
> streambuf ma buforowanie, więc tak to właśnie raczej powinno działać.
> Jednak musisz przyznać, że z punktu widzenia kogoś, kto chce
> sfotmatować komunikat i przesłać go po połączeniu albo z kolei
> odczytać komunikat zakończony np. CR-LF, niekoniecznie takie szczegóły
> implementacyjne są interesujące.
Są interesujące, jeśli nie chce aby użytkownik przy jego programie ćwiczył
swoją cierpliwość - gdy akurat przywiesiło się połączenie.
Albo załóżmy, że mamy prosty protokół, gdzie każda ze stron, na przemian
wysyła po jednej lub więcej linii tekstu. Za pomocą interfejsów istream tego
się nie odbierze, bo nie wiadomo ile linii zostało przysłanych.
> Sensowniejsze o tyle, że masz rodzieloną logikę mówiącą jak na jakieś
> zdarzenie trzeba reagować z samą obsługą i rozstrzyganiem tego, na co
> jak trzeba zareagować. W związku z tym komuś dodającemu nowy typ
> zdarzenia do obsługi trudniej będzie zepsuć jakiś aspekt istniejącej
> logiki, niż jeśli modyfikuje dużą pętlę z 'jeśli sygnał to tamto,
> jeśli coś na sockecie to śmamto, jeśli minęło tyle czasu to owamto".
Wszystko zależy od przypadku. Czasem lepiej widać co się dzieje, gdy
wszystkie te zdarzenia obsłużone są w jednym miejscu.
>> > Kolejne unixowe API którego łatwo przez pomyłkę użyć nieprawidłowo i
>> > rozwalić program to choćby pthreads.
>>
>> Wątków akurat unikam jak mogę. Łatwiej się testuje dwa współpracujące
>> procesy, niż dwa współpracujące wątki. API opakowujące pthreads nie
>> wniosłoby mi w tej kwestii absolutnie nic.
>
> W rzeczywistości wątków się używa bo to wydajny i elastyczny sposób na
> zrównoleglenie aplikacji. W przypadku stosowania oddzielnych procesów
> samo kszta związane z narzutem na ich odpalanie, IPC itd. często
> zabijają wszelkie korzyści ze zrównoleglenia.
Pamięci dzielonej da się używać także między procesami. Jedyne ograniczenie:
nie można między jednym a drugim przekazywać wprost wskaźników. Czy tak
często jest to problemem... nie wiem. Narzut odpalania występuje także dla
wątków. Nieprzypadkowo istnieją thread-pool, więc mogą i process-pool.
Oczywiście lepiej, jeśli zadania są ze sobą na tyle mało splecione, że można
nie używać pamięci dzielonej, a np. potoków do komunikacji.
>> Porównuję do czasu od którego istnieją systemy Uniksowe. Większość
>> założeń jest już bardzo starych, ale jednak przez bardzo wiele lat
>> standard się jeszcze rozwija.
>
> Owszem, standard się rozwija, ale zachowanie wsteczniej
> kompatybilności z latami 70-tymi jest ważniejsze niż tworzenie
> lepszych abstrakcji. Raczej bym powiedział, że rozwój koncentruje się
> na dostarczaniu nowej funkcjonalności, słusznie zakładając, że
> potrzeba abstrakcji będzie zaspokojona przez biblioteki opakowujące.
W samym Linuksie dodawane są też wywołania systemowe, które nowej
funkcjonalności tak na prawdę nie wprowadzają, tylko zapewniają inny
interfejs do funkcjonalności już istniejących. Np. timerfd_create, signalfd.
Dla frameworka to bez większej różnicy, natomiast jest różnica dla programów
operujących bezpośrednio na tym API.
[Wymienione przeze mnie przykłady są Linux-specific, ale w końcu to też jest
uniksowego API].
-
169. Data: 2011-10-15 21:59:37
Temat: Re: Dlaczego w branży rozrywkowej najsłabiej płacą?
Od: Wojciech Jaczewski <w...@o...pl>
Andrzej Jarzabek wrote:
>> > Ale co ci tutaj dają komentarze? Czy może są to komentarze typu:
>> > /* Wiem, że to wygląda bez sensu, ale uwierz mi, tak właśnie ma być */
>> > ?
>>
>> Np. "Filtr pasmowo-przepustowy wokół częstotliwości 2400Hz, ale ze
>> współczynnikami zespolonymi, tak aby w jego widmie częstotliwościowym
>> były niezerowe wartości wyłącznie dla częstotliwości dodatnich".
>
> Nie znam się na tym, ale zasadniczo wydaje mi się, że tak jak liczba
> zespolona może być wyrażona odpowiednim typem, tak możesz mieć typ
> generyczny Współczynnik (być może zdefiniowany wewnątrz klasy filtra
> czy przestrzeni nazw 'Filtr', żeby było jasne, że chodzi o
> współczynnik filtra), możesz mieć konkretną klasę
> WspółczynnikZespolony parametryzującą szablon FiltrPasmowoPrzepustowy,
> który ma konstruktor przyjmujący argument typu Częstotliwość i tak
> dalej.
Nie będę podejmował próby tłumaczenia tego, bo sam za słabo tę tematykę
znam, a jednocześnie nie jest to tematyka grupy.
Być może gdybym znał się na tym lepiej, to znałbym również ogólne określenie
filtrów o takiej charakterystyce (z którą się nie spotkałem nigdy poza tym
przypadkiem BECM) i sama nazwa by wystarczyła... Filtry często się tworzy
przy pomocy wzoru, z którego na pierwszy rzut oka częstotliwości nie
widać... ale pomińmy ten temat.
-
170. Data: 2011-10-16 21:26:25
Temat: Re: Dlaczego w branży rozrywkowej najsłabiej płacą?
Od: Andrzej Jarzabek <a...@g...com>
On 15/10/2011 22:51, Wojciech Jaczewski wrote:
> Andrzej Jarzabek wrote:
>
>> niemożliwe - choćby że w trakcie modyfikacji kodu ktoś zmodyfikuje
>> zakresy tak, że deskryptor będzie zdefiniowany w innym niż jego lock,
>
> Mało prawdopodobne, aby w wyniku zwykłej pomyłki a nie celowego psucia
> rozdzielić taką sekwencję do osobnych bloków:
>
> int fd = jakas_tam_metoda_tworzenia_fd();
> fd_guard_t fd_guard(fd);
Nie jest to aż tak mało prawdopodobne, żeby dla wielu takich mało
prawdopodobnych zdarzeń te prawdopodobieństwa nie skumulowały się do
czegoś zbliżonego do pewności.
>> albo ktoś gdzies wstawi zamykanie tego deskryptora ręcznie.
>
> Opakowanie deskryptora w obiekt też wiele na to nie pomoże, bo np. dalsza
> część funkcji chciała coś wysłać przez deskryptor, a on zamknięty.
Kompilator wyłapie, że obiektu nie ma w zakresie.
>> streambuf ma buforowanie, więc tak to właśnie raczej powinno działać.
>> Jednak musisz przyznać, że z punktu widzenia kogoś, kto chce
>> sfotmatować komunikat i przesłać go po połączeniu albo z kolei
>> odczytać komunikat zakończony np. CR-LF, niekoniecznie takie szczegóły
>> implementacyjne są interesujące.
>
> Są interesujące, jeśli nie chce aby użytkownik przy jego programie ćwiczył
> swoją cierpliwość - gdy akurat przywiesiło się połączenie.
No więc z punktu widzenia sensowności organizacji kodu (i co za tym
idzie niezawodności, maintainability itd.) to mieszanie obsługi
przywieszonego połączenia z logiką obsługi tego, o czym program jest
informowany albo o czym informuje za pomocą tego połączenia to tragedia.
> Albo załóżmy, że mamy prosty protokół, gdzie każda ze stron, na przemian
> wysyła po jednej lub więcej linii tekstu. Za pomocą interfejsów istream tego
> się nie odbierze, bo nie wiadomo ile linii zostało przysłanych.
Sensowne protokoły mają zwykle jakiś sposób zaznaczania końca komunikatu.
>> Sensowniejsze o tyle, że masz rodzieloną logikę mówiącą jak na jakieś
>> zdarzenie trzeba reagować z samą obsługą i rozstrzyganiem tego, na co
>> jak trzeba zareagować. W związku z tym komuś dodającemu nowy typ
>> zdarzenia do obsługi trudniej będzie zepsuć jakiś aspekt istniejącej
>> logiki, niż jeśli modyfikuje dużą pętlę z 'jeśli sygnał to tamto,
>> jeśli coś na sockecie to śmamto, jeśli minęło tyle czasu to owamto".
>
> Wszystko zależy od przypadku. Czasem lepiej widać co się dzieje, gdy
> wszystkie te zdarzenia obsłużone są w jednym miejscu.
Być może, ale to "czasami" według mnie w większości odnosi się do
niewielkich programów.
>> W rzeczywistości wątków się używa bo to wydajny i elastyczny sposób na
>> zrównoleglenie aplikacji. W przypadku stosowania oddzielnych procesów
>> samo kszta związane z narzutem na ich odpalanie, IPC itd. często
>> zabijają wszelkie korzyści ze zrównoleglenia.
>
> Pamięci dzielonej da się używać także między procesami. Jedyne ograniczenie:
> nie można między jednym a drugim przekazywać wprost wskaźników. Czy tak
> często jest to problemem... nie wiem.
Bardzo wiele struktur danych zawiera w sobie wskaźniki. Można oczywiście
zrobić ich odpowiedniki bez wskaźników, ale to jest dodatkowa
komplikacja, gorsza wydajność i im więcej tego robisz, tym więcej ci
wraca problemów, które masz z wątkami.
Pracowałem z takimi systemami, gdzie zamiast wątków robiło się wiele
procesów operujących na pamięci dzielonej działającej jako prosta baza
danych, i skalowanie tego było bardzo problematyczne.
> Narzut odpalania występuje także dla wątków.
Znacznie mniejsze.
> Nieprzypadkowo istnieją thread-pool, więc mogą i process-pool.
Można, ale masz dodatkową komplikację opisu każdego zadania i każdego
rodzaju danych który chcesz przekazać. Na wątkach można po prostu
przekazać jakiegoś dowolnego funktora.
>> Owszem, standard się rozwija, ale zachowanie wsteczniej
>> kompatybilności z latami 70-tymi jest ważniejsze niż tworzenie
>> lepszych abstrakcji. Raczej bym powiedział, że rozwój koncentruje się
>> na dostarczaniu nowej funkcjonalności, słusznie zakładając, że
>> potrzeba abstrakcji będzie zaspokojona przez biblioteki opakowujące.
>
> W samym Linuksie dodawane są też wywołania systemowe, które nowej
> funkcjonalności tak na prawdę nie wprowadzają, tylko zapewniają inny
> interfejs do funkcjonalności już istniejących. Np. timerfd_create, signalfd.
> Dla frameworka to bez większej różnicy, natomiast jest różnica dla programów
> operujących bezpośrednio na tym API.
> [Wymienione przeze mnie przykłady są Linux-specific, ale w końcu to też jest
> uniksowego API].
Drugi problem, o którym miałem napisać, ale zapomniałem, to że te
interfejsy systemowe są zazwyczaj w C, a to język wyjątkowo kiepsko
nadający się do wyrażania jakichkolwiek abstrakcji.