10 pytań przyszłych programistów

Nie, nie chodzi mi o kolejny z tysięcy poradnik o tym, jak nauczyć się Rubiego w 5 minut (nie da się nauczyć Rubiego w 5 minut) albo jak zostać programistą w tydzień (nie da się zostać programistą w tydzień). Zauważyłem jednak w naszym środowisku, tak Eltenowym jak poza-Eltenowym, pewną ciekawą tendencję, którą sprowadzę do twierdzenia "umiem programować". I fajnie, ale… No właśnie, ale.
Nie wiem, czy to stereotyp niewidomego informatyka, czy też fakt, że wymusza się na nas nieco większą wiedzę o zawiłościach komputerowych w stosunku do przeciętnego użytkownika widzącego, czy też inne czynniki, ale świadomość komputerowa bywa dość spora. Pewna garstka z nas, bo jednak grupa ta nie jest bardzo liczna, zaczyna myśleć o nauce programowania. I w tym momencie dzieje się coś dziwnego.
Mamy więc ludzi, którzy naprawdę zdobyli jakąś wiedzę, ale na tym się skończyło, bo albo mają wspaniałe teorie rewolucji świata, które nikną w zderzeniu z rzeczywistością, albo poświęcają pięć lat na wspaniały projekt, który jakoś nigdy nie doczekuje się światła dziennego, albo wciąż czekają na wenę/inspirację/motywację, te jakoś jednak nie przychodzą. I w rezultacie, gdy myślę o niewidomych programistach, prawdziwych programistach, na myśli mam tylko kilka nazwisk.
Pomyślałem sobie więc, że napiszę serię artykułów (ha ha ha, naprawdę wierzycie, że będę miał wenę na pisanie kolejnych), które poświęcę temu, jak zacząć swoją przygodę z programowaniem. Nie chcę, by był to kolejny tutorial w stylu "najpierw naucz się C", ale garść konkretnych porad wynikłych z mojej obserwacji tematu.

Nie mam jeszcze tyle doświadczenia w branży programowania, ile bym chciał. Z wyjątkiem jednak Grzegorza Złotowicza, chyba na Eltenie styczność z tematem miałem największą. Mam za sobą i duży projekt (tu Elten), i kilka mniejszych, także różne zlecenia płatne i współprace nad kodem. I w oparciu o to nasuwa się mi kilka wniosków.
Od razu uczulam, że wpis może być zbyt techniczny, nie przestraszcie się tego. Zakładam jednak, że jako osoby tematem zainteresowane, wiecie już, co to jest C++, innymi słowy to nie jest wpis dla osób, które nie mają o temacie zielonego pojęcia, tu wymagane jest przynajmniej seledynowe. 🙂
Na początek chciałbym odpowiedzieć na 10 najczęstszych pytań, które albo się gdzieś po forach zadaje, albo w domyśle ma się na myśli, albo które mi na myśl przychodzą. Przy założeniu, że pojawią się jakieś komentarze, a mi pomysł z głowy nie wyparuje, w następnym wpisie chciałbym odpowiedzieć na wasze uwagi i skupić się na dziesięciu najczęściej popełnianych na początku błędach.
Dobra, spróbujmy.

1. Czy programista musi być dobry z matematyki?

To pytanie staje się niemal tak nudne, jak szablonowe "How are you", ale wciąż jest kwestią sporów. Kwestią zupełnie dziwną o tyle, że odpowiedź jest po prostu oczywista. Przykro mi, ale tak.
Wiem, że pojawią się pewnie osoby, które zaczną argumentować, że to bzdura, że one sobie radzą, że… Nie, programista musi dobrze sobie radzić z matmą. I nie chodzi tu nawet o rozwiązywanie układów równań różniczkowych, choć na pewnym poziomie zagadnienia np. teorii sygnałów i taka zdolność się przyda. Chodzi jednak o pewną wyobraźnię.
Nie jest ważne, czy programować będziecie w C, czy w Pythonie, na jakim poziomie złożoności i w jakiej branży, elementy logiki, systemu binarnego, obliczeń i oszacowań po prostu okażą się w waszym wypadku niezbędne. Na pewno traficie czasem na potrzebę oszacowania złożoności danego algorytmu albo podzielenia zadania na wątki. Nie ma rady, komputery są na matematyce zbudowane i od matematyki nie uciekną.
Oczywiście pytaniem innym jest, co znaczy "bycie dobrym z matematyki". Znam osoby, które miały z matury rozszerzonej z matematyki naprawdę wyniki średnie, a rozwiązywały od ręki takie zadania, nad którymi ja musiałbym posiedzieć przynajmniej kilka godzin. Znam też osoby, które uzyskiwały świetne wyniki z testów, ale kiedy stawały przed prawdziwym obliczeniem (nie schematycznym zadaniem ze szkoły), pojawiał się problem.
Ale matematyka jako taka jest w programowaniu niezbędna.

2. Od jakiego języka zacząć?

Od jakiegokolwiek, naprawdę. Najbardziej bawi mnie sytuacja, gdy dana osoba zamiast zabrać się do nauki, spędza pół roku na rozważaniu za i przeciw różnych technik, zaczyna jedno i zaraz przerywa, coś tam napisze w języku A, by przejść na B, C i D.
Jedyne ważne jest to, by był to język mainstreamowy, najlepiej należący do najpopularniejszych. Wg portalu Toward’s Data Science, w roku 2020 najpopularniejsze języki to, w kolejności, JavaScript, Python, Java, Go, TypeScript, C++, Ruby, PHP, C# i C. W tej mainstreamowości chodzi o to, by łatwo było znaleźć poradniki i przykłady danego języka, a nie męczyć się z szukaniem odpowiedzi, jak napisać coś w języku znanym przez trzydziestu ludzi w Polsce.
Jako programiści i tak będziecie musieli w praktyce poznać przynajmniej kilka języków biegle, a znośnie właściwie ze 10-20. Będzie jeszcze czas na wybranie tych ulubionych, tym czasem zastanawianie się, co wolicie na samym początku, początek ten tylko oddala.
Są kłótnie i spory, czy lepiej zaczynać od języków strukturalnych czy obiektowych, poznać najpierw podstawy, czy zrobić od razu coś dużego i do podstaw wrócić. Szkół jest tyle, że i tak nie zadowolicie wszystkich, więc spróbujcie po prostu zadowolić siebie.

3. Ile muszę się uczyć przed napisaniem pierwszego programu?

Jak najmniej, bo to pisząc się uczy. I nie ma na to innego sposobu. Nigdy nie poznacie programowania tak naprawdę, jeśli nie wydacie pierwszego, drugiego, dziesiątego projektu. Jeśli spojrzycie na swój kod z przed trzech lat i nie powiecie sobie "Jaki głupek to pisał?", to znaczy tylko tyle, że niczego się nie nauczyliście.
Fakt, dobrze jest rozplanować pracę, ale nie stworzycie dobrego planu bez doświadczenia. W programowaniu są różne wzorce projektowe, struktury danych, rozwiązania i biblioteki. Ale nie ma żadnego poradnika, który dokładnie wam wyjaśni, jakie rozwiązanie pasuje do jakiego problemu.
Po pierwsze dlatego, że taka książka miałaby z milion stron, a po drugie, że to także od waszego indywidualnego stylu będzie zależało.

4. A ten pierwszy program, to co to ma być?

Coś małego, użytecznego, ale prostego. Często słyszę o programistach (choć akurat widzących), których gubią nadmierne ambicje co do pierwszego projektu.
Jeśli zaczniecie od pisania na przykład nowego edytora dokumentów PDF albo klienta Facebooka, to raczej daleko nie zajdziecie. A przecież są inne, małe zadania, które można sobie uprościć.
I co, że na początek program będzie pisany w konsoli? Ważne, by działał.
Jeśli macie komu, pokażcie kod, bądźcie otwarci na sugestie. A potem napiszcie coś nowego. I nowego. Z biegiem czasu przyjdzie czas i na interfejs graficzny, i większe wyzwania.

5. Z innej bajki, jaki edytor na początek?

A nawet niech będzie to i Notatnik. To nieśmiertelne rozwiązanie, które będzie wam towarzyszyło do szybkiego szkicowania już na zawsze. O nowym edytorze czas pomyśleć wtedy, gdy pojawi się taka potrzeba. Na początek macie się nauczyć programowania, a nie obsługi złożonych narzędzi.
A jeśli wam jednak mimo wszystko od razu zależy na profesjonalnym wyglądzie albo już tego następnego narzędzia szukacie, to ja zdradzę, że pracuję w Visual Studio Code oraz Vimie.

6. Czego jeszcze trzeba się nauczyć, prócz języka programowania?

To dość złożone zagadnienie. Generalnie zanim sięgniecie głębiej do istoty tematu, naprawdę uznajcie, że coś umiecie. Analiza złożonych przypadków bez ogólnego pojęcia w temacie jest jak uczenie się gramatyki języka bez słówek.
Nie odkładajcie tego jednak też zbyt daleko. Pewne wzorce, struktury i pomysły w programowaniu powstawały przez lata nie w celu utrudnienia, ale uproszczenia wam życia. I nauczenie się poprawnej implementacji gotowych rozwiązań nie tylko wam uprości pracę, ale i sprawi, że będzie ona czytelniejsza dla innych.
Z drugiej strony nic na siłę. Często widuję kody początkujących programistów, które są tak na siłę budowane wedle wzorców projektowych, że wyglądają jak jakieś przykłady encyklopedyczne, a nie gotowe aplikacje, a ich czytelność, miast zyskiwać, traci. To po prostu kwestia wprawy, kiedy coś się przyda, a kiedy trzeba machnąć na to ręką.
Jak już wydacie kilka projektów z GUI, na pewno polecam zapoznanie się z tematem najpopularniejszych szablonów aplikacji (MVC, MVVM, MVP), jak również z najważniejszymi wzorcami projektowymi i strukturami danych. Gorąco też polecam książkę pt. "Programowanie współbieżne i rozproszone" autorstwa panów Zbigniewa Weis oraz Tadeusza Gruźlewskiego, która w moim wypadku była bardzo ważnym podręcznikiem dobrych praktyk algorytmicznych.

7. Kiedy używać tych klas, interfejsów?

Dość często zauważam, że osoby uczące się programowania, nie do końca łapią się w tym, kiedy stosować które narzędzia. Dostajemy więc aplikacje napisane np. w C++ zawierające 80 funkcji i ani jednej klasy, z jakimiś dziwnymi zmiennymi albo, najgorsza praktyka z możliwych, tablicami, w których elementy o danym indeksie oznaczają dany zasób, ale schemat tworzenia ich występuje naturalnie tylko w głowie twórcy.
Czasami jest to zachowanie korzystne, ale w zdecydowanej większości przypadków nie. Dlatego warto eksperymentować z nowo poznawanymi elementami programowania obiektowego lub strukturalnego. Wszystko się jakoś samo poukłada, kiedy zauważycie, co pomaga, a co nie.

8. Ile plików ma mieć mój program?

To jest generalnie ciekawe zagadnienie i nie ma na nie dobrej odpowiedzi. Zależy to od ilości plików, pomysłu na organizację kodu i preferencji twórcy. Czasem wygodniej jest mieć pliki giganty, czasem lepiej mieć dużo karłów.
Na pewno jednak należy unikać dwóch skrajnych sytuacji, gdy program ma 10 plików po 10 linijek albo jeden plik zawierający linijek 38520. Generalnie na początku najlepszą praktyką jest dzielenie programu wg zastosowania.
Jeśli piszę więc kalkulator, jeden plik może odpowiadać za interfejs graficzny, drugi za obliczenia, a trzeci za przekazywanie informacji między jednym a drugim. I tak, to jest klasyczny przykład schematu MVC typu 2, mówiłem, że szablony i wzorce są przydatne.

9. Optymalizować, czy nie optymalizować?

Oto jest pytanie!
Współcześnie bardzo się zaciera granica między tym, kiedy kod lepiej optymalizować, a kiedy dbać o jego czytelność. W czasach, kiedy platforma Dotnet używa wirtualnej maszyny do wykonywania kodu, naprawdę sprawa staje się złożona.
Z jednej strony, kiedy mamy za zadanie posortowanie gigantycznej listy, na pewno będziemy zastanawiać się nad najlepszym algorytmem. Zwłaszcza, gdy w grę wchodzi przycinanie się programu.
Z drugiej strony mamy na przykład wybór większej wartości. Załóżmy, że mamy dwie liczby i zastanawiamy się, która z nich jest większa. Pokażę to na przykładzie zmiennych a i b, chcemy w zmiennej x zapisać większą z nich.
Najprościej zrobić to tak:
Jeśli a jest większe od b, zapisz x równe a,
w przeciwnym wypadku zapisz x równe b.
W języku C++:
if(a>b) x=a;
else x=b;
Czy można zrobić to wydajniej? Oczywiście!
Zapiszemy, że x jest równe różnicy a oraz wartości powstałej z iloczynu bitowego różnicy a i b oraz różnicy a i b przesuniętej bitowo o rozmiar w prawo.
x = a – ((a-b) & ((a-b) >> sizeof(x)*8-1));
Gratuluję! Właśnie stworzyliśmy wspaniale wydajny kod! Wydajny w każdym razie dla komputera, bo jak za tydzień będziemy go czytać, spędzimy na pewno dobrych kilka sekund zastanawiając się, jak to ma działać, co robić i obiecując sobie, że od tej pory będziemy programować tylko na trzeźwo.
Dla jasności, są sytuacje, gdy takie sztuczki naprawdę się przydadzą. Jeśli dysponujemy ograniczoną pamięcią lub mocą obliczeniową, bez nich się nie obędziemy. A to nie jest aż tak nieprawdopodobne, wszak mikrokontrolery są wszędzie, a rzadko który przekracza 4kB pamięci RAM i 16MHz.
Ale po pierwsze, wtedy stosuje się makra, a po drugie… Uznajmy, początkujący programiści nie piszą oprogramowania dla mikrokontrolerów.

10. Jak się ćwiczyć w programowaniu?

To chyba najważniejsza sprawa. Programowanie to przede wszystkim gra wyobraźni. Trzeba się nauczyć algorytmicznego myślenia, odruchowego rozwiązywania kłopotów.
Wiele jest żartów o programistach, które jednak doskonale to lustrują. Ja przytoczę dwa.
Pierwszy z nich: Jak zająć programistę? Dać mu kartkę z napisem:
Przeczytaj poniższą linijkę.
Przeczytaj powyższą linijkę.
Drugi natomiast: Żona do męża programisty:
– Kup chleb, a jak będą jajka, kup 10.
Nasz programista wchodzi więc do sklepu i pyta: "Są jajka"?
– Są – odpowiada ekspedientka.
– To poproszę dziesięć chlebów.
Komputer to taka maszyna, która robi tylko i dokładnie to, co jej każemy. A początkujący programiści mają taką skłonność do myślenia po ludzku, nie zaś po komputerowemu. I z tego wynika masa błędów.
Z drugiej strony doświadczeni programiści mają skłonność odwrotną i… też bywa ciekawie.
Na koniec pozostawiam was z zagadką logiczną, którą powinno się zadawać na pierwszych zajęciach z programowania, a jakoś się tego nie robi.
Jasiu dostał pięć jabłek i zjadł dwa. Ile jabłek ma Jasiu?
Zapraszam do odpowiadania w komentarzach.

Ufff, przebrnęliśmy przez to. Wiem, że wpis był nieco chaotyczny, ale mam nadzieję, że dla kogoś będzie jakąś tam podpowiedzią.
Zapraszam do zadawania wszelkich waszych pytań w komentarzach, spróbuję na nie odpowiedzieć, a będą one tylko motywacją do napisania jednak tego następnego wpisu.
Jeśli będę widział zainteresowanie, następnym razem omówię 10 grzechów głównych początkujących programistów na Eltena i Eltenowiczów przykładzie. 🙂

Z pozdrowieniami,
Dawid Pieper
Chociaż raczej powinienem napisać
446177696420506965706572

Kontrtest i doskonały przykład na to, jak to jest z tymi wieloma rdzeniami i kiedy to jest fajne

Ostatnio zasugerowany został mi pewien eksperyment. Chodziło o porównanie kilku komputerów pod względem jednego zadania obliczeniowego. Długo zastanawiałem się nad konkretnym zadaniem, aż wreszcie wpadłem przypadkiem na pewien pomysł.
Chciałbym znaleźć zadanie, które jednocześnie pozwoli na dość dobre porównanie wielu komputerów, ale i pokaże czytelnikom tego bloga zawiłości związane z obliczeniami wykonywanymi na komputerze.

Cel zadania

Archiwum Klango jest dość spore. Postanowiłem więc je nieco skompresować. W tym celu zdecydowałem się na konwersję całego forum głosowego z MP3 96kbps (oryginał) do Opusa 32kbps. Zapewnia to relatywnie niewielki spadek jakości zważywszy na używane przez Klangowiczów mikrofony, a zmniejszy rozmiar całości z 46 do 15 GB.
Napisałem więc do tego prosty skrypt w Rubym i go uruchomiłem. Kiedy po czterech godzinach zadanie tylko raczkowało, zrozumiałem, że miałem tu zdecydowanie błędne podejście.

Z czego składa się konwersja audio

Konwersja audio z MP3 do Opusa składa się zasadniczo z dwóch faz. W pierwszej audio musi być zdekodowane z formatu MP3 do zapisu PCM, który następnie zrozumie enkoder, zamieniając go na Opusa. Nie jest możliwa bezpośrednia zmiana jednego formatu na drugi, bo stosują one zupełnie inne algorytmy.

Dekodowanie

Dekodowanie MP3 jest stosunkowo łatwym zadaniem. To między innymi prostota była jedną z poszukiwanych cech kodeka audio, pamiętajcie, że MP3 pochodzi z roku 1993, celując w różne wieże, później odtwarzacze przenośne itp. W tamtych czasach sprzęt nie cechował się współczesną mocą obliczeniową, tak więc poszukiwano rozwiązań, przy których odtworzenie formatu będzie tak mało wymagające, jak to tylko możliwe, kosztem jakości.
Jedną z cech, którymi miał się wyróżniać ten format, jest możliwość rozpoczęcia dekodowania od danego momentu, bez dekodowania całego pliku. W tym celu enkoder buduje pewną bazę informacji, które pozwalają później dekodować konkretne, wybrane fragmenty. Umożliwia to podzielenie zadania dekodowania pliku MP3 między wiele jednostek, np. mając 8 komputerów możemy zdekodować ten sam plik 8 razy szybciej.

Enkodowanie

Zakodowanie audio jest procesem dużo trudniejszym od jego odczytania. Wynika to z koncepcji dotyczącej formatów audio, w której plik powstaje raz, za to odtwarzany razy może być i tysiąc. Można więc ciężar obliczeniowy przenosić na producenta. Tak też na początku istnienia stratnej kompresji audio pliki w formatach typu MP3 potrafiły być przygotowywane wielokrotnie dłużej niż same trwały, za to odczytywane niemal w czasie rzeczywistym.
Opus, jak większość formatów stratnych, dzieli się na tak zwane ramki. Każda ramka to maleńki fragment pliku, np. 20ms. Enkoder pobiera taką ramkę z pliku źródłowego i ją analizuje. Następnie, zależnie od jej zawartości, dobierany jest odpowiedni sposób kompresji.
Przykładowo, jeśli nasze 20ms to 20ms ciszy, zamiast zapisywać informację: "cisza, cisza, cisza, cisza, cisza" (i tak 960 razy), idealny kodek mógłby napisać po prostu "To tylko cisza". W praktyce z wyjątkiem sytuacji wygenerowanych programowo, istnienie idealnej ciszy jest niemal niespotykane.
Enkoder musi więc się zastanowić nad dynamiką dźwięku, tym, co ucho wychwyci lepiej, a co gorzej. W tym celu potrzebuje informacji o tym, co było wcześniej, nie może dojść do sytuacji, gdy co chwilę dźwięk zaczyna brzmieć inaczej, bo enkoder uznał, że na czym innym oszczędzi.
W rezultacie enkodowanie jest procesem ciągłym. Nie można jednocześnie kodować pliku w kilku miejscach, zawsze przed kolejnym fragmentem trzeba zapisać poprzedni. W rezultacie, gdybyśmy mieli 8 takich samych komputerów, czas kodowania jednego pliku do formatu Opus byłby taki sam, jak gdybyśmy użyli tylko jednego komputera.
W powyższym przykładzie użyłem pewnego uproszczenia. Proces kodowania audio jest częściowo wątkowalny, to znaczy można pewne operacje wykonywać jednocześnie, np. jeden komputer mógłby analizować plik, drugi kompresować, a trzeci umieszczać w kontenerze OGG. Jako jednak, że w tym wypadku analiza stanowi jakieś 90 procent czasu, moglibyśmy ugrać na tym tylko 10 procent, czyli używając trzech komputerów, z kompresji trwającej pół godziny nie zrobimy 10 minut, a 27 minut.

Komputery to rdzenie

W powyższym przykładzie dzieliłem pracę między kilka komputerów. Dziś jednak zdecydowana większość procesorów to jednostki wielordzeniowe. Oznacza to, że potrafią one pracować jednocześnie nad kilkoma zadaniami.
Najlepiej wyobrazić sobie je jako wiele połączonych z sobą mniejszych procesorków. Tak więc, skoro przykładowo dekodować audio można w kilku miejscach na raz, ten sam procesor posiadający 8 rdzeni zrobi to 4 razy szybciej od jednostki posiadającej tylko 2 rdzenie.

Co więc zrobiłem źle?

Jak już pokazałem, dekodowanie audio jest procesem wielokrotnie szybszym od zapisu. W praktyce dekoder tworzył zapis PCM dużo szybciej, niż enkoder go odczytywał. Komputer wielordzeniowy nie miał więc niemal żadnej przewagi nad jednostką posiadającą rdzeń tylko jeden.
Nie jest możliwe ugranie żadnego znacznego przyspieszenia kompresji audio na wielu rdzeniach (tylko od ok. 5 procent do 15 procent zależnie od formatu). Nic nie stoi jednak na przeszkodzie, by kompresować w tym czasie wiele plików audio. Jeśli kompresujemy pliki o długości minuty, a rdzeni mamy na przykład 4, możemy albo w tej minucie skompresować tylko jeden plik, albo cztery na raz. Ale nie możemy skompresować jednego pliku w 15 sekund.

Przy okazji powstał znakomity test

Na potrzeby testu wydzieliłem tylko angielskie forum głosowe. To 9,69 GB danych, czyli ok. 241 godzin nagrań, co jednak ważniejsze, składających się z małych plików, łącznie tych plików jest 14535. Zawartość tych plików jest różna: prezentacje, notki, recytacje, muzyka, raz na mikrofonie wbudowanym, raz studyjnym. Czyli piękny przekrój wszystkiego.
Następnie wykonałem na trzech komputerach dwa testy. Pierwszy test polegał na uruchomieniu kompresji sekwencyjnie, jedną po drugiej. W podejściu drugim kompresowałem tyle nagrań na raz, iloma rdzeniami logicznymi dysponował procesor.

Test ma pewną słabość

Celowo użyłem zewnętrznego dysku SSD, by zmniejszyć wpływ samego nośnika na kompresję. Opisując jednak działanie enkodera, posłużyłem się pewnym uproszczeniem, przedstawiając cały proces jako zadanie dla procesora.
W praktyce, choć może się to wydawać paradoksem, w kompresji audio dość aktywnie wspomaga procesor układ graficzny, który dużo lepiej jest dostosowany do pracy na dużych danych, wyliczania sum kontrolnych, kompresji itp.
Nie jest to wpływ decydujący, szacuję go na kilka procent, jednak trzeba o sprawie pamiętać.

Dość gadania, czas na dane.

Platforma testowa

Komputery biorące udział w teście to:
1. Dell XPS 15 9550
Procesor Intel Core I7-6700HQ, 8 rdzeni logicznych, 3,5GHz.
2. MacBook Pro 13 2017
Procesor Intel Core I7-7560U, 4 rdzenie logiczne, 4,0GHz.
3. Dell XPS 15 9500
Procesor Intel Core I7-10750H, 12 rdzeni logicznych, 5,0GHz.

Przebieg sekwencyjny, jedno po drugim

Najszybciej z zadaniem poradził sobie mój nowy Dell XPS 9500, osiągając czas 6 godzin i 47 minut. Drugi był MacBook z czasem 7 godzin i 41 minut. Najniższe miejsce zajął XPS 9550, czas tutaj wyniósł 8 godzin i 3 minuty.
Jak warto tu zauważyć, MacBook teoretycznie posiadał gorszy procesor od XPS-a 9550, procesor kategorii U, czyli energooszczędnej. Miał mniej rdzeni, mniej pamięci podręcznej.
Jako jednak, że zadanie większościowo wymagało tylko jednego rdzenia, o wyniku decydowała niemal wyłącznie częstotliwość pracy i to dlatego MacBook przebił teoretycznie lepszego Della.

Przebieg równoległy, na raz tyle nagrań, ile rdzeni

Ponownie pierwsze miejsce osiągnął Dell XPS 9500, czas wyniósł 47 minut. Miejsce drugie należy do starszego XPS 9550, który dopiął swego w godzinę i 31 minut. MacBook sprawował się najgorzej, a cała kompresja zajęła mu aż 2 godziny i 42 minuty.
Jak widać, tutaj jednak zaważyła właśnie wielordzeniowość. XPS 9550, choć posiadający nieco wolniejszy procesor od MacBooka pod względem częstotliwości, właśnie dzięki ośmiu miast czterech rdzeni poradził sobie w czasie niemal dwukrotnie mniejszym.

A wnioski? Nie takie rdzenie piękne, jak je malują.

Wielordzeniowość procesora jest zagadnieniem bardzo złożonym. W tym wypadku podziałała na moją korzyść. Istnieją jednak dwa przypadki, w których by tak nie było.

Po pierwsze, gdybym miał kompresować tylko jeden, bardzo długi plik.

W tym wypadku mój nowy laptop okazał się najszybszy, ale mając wybrać między MacBookiem a poprzednim XPS-em, musiałbym wybrać komputer z Jabłuszkiem i to mimo faktu, że w teście drugim był niemal dwukrotnie wolniejszy.

Po drugie, gdybym nie miał odpowiedniego oprogramowania.

Pisanie aplikacji z myślą o procesorach wielordzeniowych jest zagadnieniem bardzo trudnym. Dzielenie aplikacji na równoległe wątki wymaga od programisty rozważenia sposobu zarządzania pamięcią czy wymianą danych. Nie wspominając o tym, że nawet jeśli jakiś problem da się podzielić na części, najpierw sprawę trzeba głęboko przemyśleć i wykombinować, jak to zrobić.
W rezultacie jestem przekonany, że wiele dostępnych na rynku narzędzi (nawet tych popularnych) do dzisiejszego mojego zadania podeszłoby w sekwencyjny sposób, czyli program miast kompresować dane od 47 minut, czyniłby to niemal 7 godzin.
Należy więc pamiętać, że naprawdę dla większości użytkowników lepiej wybrać 6/8 rdzeniowy procesor o lepszym taktowaniu, niż 24-rdzeniowy o gorszym. Bardzo niewiele zadań uda się podzielić na tak wiele części. A nawet jeśli się uda, mało który program weźmie to pod uwagę. I niestety nie zanosi się na to, by coś w tej kwestii miało się zmienić.

Dysk, drugi upgrade XPS-a

Druga z trzech zaplanowanych przeze mnie aktualizacji Della XPS 15 9550 to, jak zapowiadałem przy okazji wymiany kości RAM, nowy dysk.
Dość długo zastanawiałem się nad modelem, ostatecznie zdecydowałem się na dysk Adata XPG SX8200 Pro. Ryzyko o tyle, że jest to moja pierwsza styczność z dyskami SSD Adaty.
Prawdopodobnie mój wybór padłby na Samsunga 970 Evo Pro, ale testy donoszą, że model ten ma ogromny problem z przegrzewaniem się, zwłaszcza w laptopach, a doniesień tych jest tak wiele, że coś musi być niestety na rzeczy.

Parametry dysku (wg specyfikacji)

Pojemność: 1TB (właściwie 953GB)
Interfejs: PCI-E x4 Gen3 NVMe,
Szybkość odczytu: 3500MB/s,
Szybkość zapisu: 3000MB/s,
Odczyt Losowy: 390000 IOPS,
Zapis Losowy: 380000 IOPS,
TBW: 640TB.

Zaletą tu niewątpliwie są duże szybkości odczytu i zapisu, o praktycznym aspekcie za moment. Także nie można nie wspomnieć o bardzo niskiej cenie, 700zł za tej klasy SSD to naprawdę nie jest dużo.
Zawodem pewnym są wartości operacji losowych, 400 tysięcy IOPS w porównaniu z 500-550 osiągalnymi przez Samsunga to parametry tylko nieco powyżej przeciętnej.
Także TBW 640TB przy pojemności 1TB jest daną, która na kolana nie powala. Z drugiej strony mówimy o naprawdę tanim dysku, a parametry i tak klasują go w randze, że tak to ujmę, topowej, na stan roku 2019 w teście Benchmarkowym był to piąty dysk pod względem osiągów, a porównywalny z modelami kosztującymi nawet dwa razy tyle.

Jak to wygląda

Dyski NVMe, jeśli ktoś nie wie, są małe… Jak, nie wiem, pendrive, z tych mniejszych. Ważą tyle co piórko i przypominają bardziej kartę do jakiegoś chipu niż dysk.
Adata przyszła w wielkim kartonie, w którym był nieco mniejszy karton, w którym było małe pudełeczko, w którym była folia z dwiema częściami wielkości karty bankowej. Jeden element to dysk, drugi to radiator.
Od razu chciałbym wyjaśnić małą niepewność. Zwykło się mówić na wiatraczek procesora radiator, błędnie. Wiatraczek to wiatraczek, a radiator to radiator. Radiator to odpowiednio wyprofilowany przedmiot, który usprawnia krążenie powietrza, chłodząc to, do czego go przykleimy lub na czym nałożymy. Nie jest jednak w żadnym sensie elektryczny, nie ma wiatraczka, chłodzi wyłącznie kształtem zapewniającym przepływ powietrza.
Dokumentacja podaje, że radiator dla tego dysku jest elementem opcjonalnym i, jeśli nie zmieści się w gnieździe M.2, należy pozostawić dysk bez niego. U mnie w laptopie się jednak zmieścił.

Odczucia

Generalnie wydaje się, że dysk Adaty lepiej od fabrycznej Toshiby dogaduje się z procesorem. To oczywiście uproszczenie, ale znacznie mniej wysiłku kosztuje komputer zapis losowy, marudziłem przed momentem, że 400 tysięcy IOPS to nie jest dużo, ale poprzedni dysk miał IOPS tysięcy 250, więc jest to poprawa o ponad 50 procent.
Nie policzyłem niestety czasu wymaganego na instalację Windowsa 10 na poprzednim dysku, na tym jednak instalacja od momentu pierwszego restartu do ukazania się okna konfiguracji trwała 7 minut i 47 sekund, wliczając ponowne rozruchy, wkrótce zaktualizuję wpis o dane dotyczące Debiana, bo jeszcze go nie postawiłem.

Kilka danych

Najpierw będzie technicznie, a potem praktycznie.
Testy wykonane dla 16GB w 5 rundach programem Crystal Disk Mark wskazują:
Odczyt sekwencyjny: 3377.18MB/s
Zapis sekwencyjny: 2825.27MB/s
Odczyt losowy (blok 1MB): 1355.54MB/s
Zapis losowy (blok 1MB): 1273.36MB/s
Odczyt losowy (blok 4kB): 52.48MB/s
Zapis losowy (blok 4kB): 131.61MB/s
Wyniki są śliczne, zwłaszcza parametry odczytu. Co zaskakujące, dla bloków po 4kB odczyt okazał się mniejszy od zapisu, być może wynika to z czasu lokalizacji komórki.

Testy praktyczne

W testach tych kopiowałem między partycjami NTFS plik lub pliki zapełnione zupełnie losowymi bajtami. Poniżej wyniki.

Jeden plik, 25GB:
Prędkość średnia: 1017MB/s,
Czas kopiowania: 25,17s.

25 plików, 1GB każdy:
Prędkość średnia: 1039MB/s,
Czas kopiowania: 24,64s.

25000 plików, 1MB każdy
Uwaga! Rozmiar nieco mniejszy od powyższych, 25000MB to nie 25GB. 🙂
Prędkość średnia: 211MB/s,
Czas kopiowania: 118,48s.

Warto dodać, że w czasie wszystkich przeprowadzanych testów maksymalna temperatura wyniosła 49 stopni Celsjusza, a temperatura średnia to 43 stopnie Celsjusza, dane z programu Crystal Disk Info.

Podsumowanie

Adata XPG SX8200 Pro to z pewnością jeden z najlepszych dysków SSD na rynku, a przy tym o cenie, nie umiem inaczej tego nazwać, w segmencie śmiesznie niskiej. Konkurencyjny Samsung 970 Evo o tej samej pojemności kosztuje 300zł więcej, zaś warto tu wspomnieć, że w benchmarkach modele te idą łeb w łeb.
Nie będę tu listował zalet i wad, bo dysk mam zbyt krótko, by je podać. Na pewno jednak z zakupu na razie jestem bardzo zadowolony.
Warto jednak tu wspomnieć o bardzo ważnym, a przemilczanym w reklamach i opisach produktu kompromisie – kompromisie, który z resztą dotyczy i Samsungów, i Toshib, i Adat, i Corsairów. Obecnie nie posiadamy technologii umieszczania w sensownej cenie produkcyjnej 1-bitowych komórek w dyskach o pojemnościach liczonych w terabajtach. Używamy zatem 3 bitów na komórkę, technologia TLC. Żeby zaś usprawnić odczyt, zapełniamy te komórki tylko jednym bitem, by zachowywały się, jak dyski SLC. Dzięki temu możemy mówić o tak wielkich odczytach i zapisach, jak tutaj.
I wszystko działa ładnie, jak długo mamy do dyspozycji wolne komórki. Z czasem jednak danych zapisujemy więcej i więcej, a wolnych komórek zaczyna ubywać. Wciąż możemy przeprowadzać optymalizację dysku, jeśli jednak o tym zapomnimy, jakoś na poziomie jednej trzeciej zapelnienia, dysk zauważalnie zwolni.
Regularnie optymalizując go możemy ten proces odsunąć w czasie, ale mniej-więcej przy zapełnieniu 75-80 procent, już żadne optymalizacje cudów nie zdziałają i po prostu dysk będzie 3-4 razy wolniejszy. Dlatego ja sam unikam zapełniania dysku w tym zakresie, ostatnie 20-30 procent miejsca zachowując na dane bardzo tymczasowe.

Bo z programowaniem to jest tak

Siedzicie kilka godzin, dokładniej 2 godziny i 40 minut, nad jakimś kodem.
Wreszcie… Gotowe! Zostaje tylko to skompilować i poczytać jakąś książkę.
A potem…

Kompilacja rozpoczęła się 25.06.2020 22:07:58.
Szczegółowość rejestrowania została ustawiona na: Normal.Projekt „c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc.sln” w węźle 1 (domyślne elementy docelowe).
ValidateSolutionConfiguration:
Tworzenie konfiguracji rozwiązania „Release|Win32”.
Projekt „c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc.sln” (1) kompiluje „c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj” (2) w węźle 1 (domyślne elementy docelowe).
InitializeBuildStatus:
Modyfikowanie „Release\eltenvc.tlog\unsuccessfulbuild”.
ClCompile:
C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.26.28801\bin\HostX86\x86\CL.exe /c /I"C:\Program Files (x86)\OpenSSL-Win32\include" /I..\..\.. /Zi /nologo /W3 /WX- /diagnostics:column /O2 /Oi /Oy- /GL /D WIN32 /D NDEBUG /D _WINDOWS /D _USRDLL /D ELTENVC_EXPORTS /D _WINDLL /D _UNICODE /D UNICODE /Gm- /EHsc /MT /GS /Gy /fp:precise /Zc:wchar_t /Zc:forScope /Zc:inline /Fo"Release\\" /Fd"Release\vc142.pdb" /Gd /TP /analyze- /FC /errorReport:queue ..\..\..\dllmain.cpp
dllmain.cpp
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(478,12): warning C4101: "op": lokalna zmienna, do której nie istnieje odwołanie [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(479,5): warning C4101: "ret": lokalna zmienna, do której nie istnieje odwołanie [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(498,7): error C4430: brak specyfikatora typu – założono, że int. Uwaga: C++ nie obsługuje domyślnie typu int [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(498,19): error C2065: "opus": niezadeklarowany identyfikator [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(498,34): error C2065: "pcm_buf": niezadeklarowany identyfikator [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(498,48): error C2065: "opus": niezadeklarowany identyfikator [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(498,70): error C2660: "opus_encode": funkcja nie przyjmuje 4 argumentów [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\opus\opus.h(263,48): message : zobacz deklarację "opus_encode" [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(500,3): error C3927: "->": nie można używać końcowego typu zwracanego po deklaratorze niebędącym funkcją [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(500,3): error C3484: błąd składni: oczekiwano "->" przed zwracanym typem [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(500,10): error C3613: brak typu zwracanego po "->" (przyjęto typ "int") [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(500,10): error C4430: brak specyfikatora typu – założono, że int. Uwaga: C++ nie obsługuje domyślnie typu int [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(500,4): error C2146: błąd składniowy: brakuje ";" przed identyfikatorem "b_o_s" [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(501,3): error C3927: "->": nie można używać końcowego typu zwracanego po deklaratorze niebędącym funkcją [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(501,3): error C3484: błąd składni: oczekiwano "->" przed zwracanym typem [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(501,10): error C3613: brak typu zwracanego po "->" (przyjęto typ "int") [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(501,10): error C4430: brak specyfikatora typu – założono, że int. Uwaga: C++ nie obsługuje domyślnie typu int [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(501,1): error C2086: "int op": zmiana definicji [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(500): message : zobacz deklarację "op" [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(501,4): error C2146: błąd składniowy: brakuje ";" przed identyfikatorem "e_o_s" [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(502,3): error C3927: "->": nie można używać końcowego typu zwracanego po deklaratorze niebędącym funkcją [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(502,3): error C3484: błąd składni: oczekiwano "->" przed zwracanym typem [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(502,15): error C3613: brak typu zwracanego po "->" (przyjęto typ "int") [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(502,15): error C4430: brak specyfikatora typu – założono, że int. Uwaga: C++ nie obsługuje domyślnie typu int [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(502,1): error C2086: "int op": zmiana definicji [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(500): message : zobacz deklarację "op" [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(502,4): error C2146: błąd składniowy: brakuje ";" przed identyfikatorem "granulepos" [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(503,3): error C3927: "->": nie można używać końcowego typu zwracanego po deklaratorze niebędącym funkcją [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(503,3): error C3484: błąd składni: oczekiwano "->" przed zwracanym typem [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(503,13): error C3613: brak typu zwracanego po "->" (przyjęto typ "int") [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(503,13): error C4430: brak specyfikatora typu – założono, że int. Uwaga: C++ nie obsługuje domyślnie typu int [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(503,1): error C2086: "int op": zmiana definicji [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(500): message : zobacz deklarację "op" [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(503,4): error C2146: błąd składniowy: brakuje ";" przed identyfikatorem "packetno" [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(504,3): error C3927: "->": nie można używać końcowego typu zwracanego po deklaratorze niebędącym funkcją [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(504,3): error C3484: błąd składni: oczekiwano "->" przed zwracanym typem [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(504,11): error C3613: brak typu zwracanego po "->" (przyjęto typ "int") [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(504,11): error C4430: brak specyfikatora typu – założono, że int. Uwaga: C++ nie obsługuje domyślnie typu int [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(504,1): error C2086: "int op": zmiana definicji [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(500): message : zobacz deklarację "op" [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(504,4): error C2146: błąd składniowy: brakuje ";" przed identyfikatorem "packet" [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(505,3): error C3927: "->": nie można używać końcowego typu zwracanego po deklaratorze niebędącym funkcją [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(505,3): error C3484: błąd składni: oczekiwano "->" przed zwracanym typem [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(505,10): error C3613: brak typu zwracanego po "->" (przyjęto typ "int") [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(505,10): error C4430: brak specyfikatora typu – założono, że int. Uwaga: C++ nie obsługuje domyślnie typu int [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(505,1): error C2086: "int op": zmiana definicji [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(500): message : zobacz deklarację "op" [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(505,4): error C2146: błąd składniowy: brakuje ";" przed identyfikatorem "bytes" [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(507,5): error C3927: "->": nie można używać końcowego typu zwracanego po deklaratorze niebędącym funkcją [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(507,18): error C3613: brak typu zwracanego po "->" (przyjęto typ "int") [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(507,18): error C4430: brak specyfikatora typu – założono, że int. Uwaga: C++ nie obsługuje domyślnie typu int [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(507,7): error C2146: błąd składniowy: brakuje ";" przed identyfikatorem "granulepos" [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(509,36): error C4430: brak specyfikatora typu – założono, że int. Uwaga: C++ nie obsługuje domyślnie typu int [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(509,1): error C2365: "ogg_stream_packetin": zmiana definicji; definicja poprzednia była "funkcja" [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\ogg\ogg.h(157): message : zobacz deklarację "ogg_stream_packetin" [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(511,1): error C2059: błąd składniowy: "while" [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(511,53): error C2143: błąd składniowy: brakuje ";" przed "{" [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(511,53): error C2447: "{": brak nagłówka funkcji (stary styl formalnej listy?) [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(518,1): error C2059: błąd składniowy: "return" [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(519,1): error C2059: błąd składniowy: "}" [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(519,1): error C2143: błąd składniowy: brakuje ";" przed "}" [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
Kompilowanie projektu „c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj” wykonane (domyślne elementy docelowe) — NIEPOWODZENIE.

Kompilowanie projektu „c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc.sln” wykonane (domyślne elementy docelowe) — NIEPOWODZENIE.

Kompilacja NIE POWIODŁA SIĘ.

„c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc.sln” (domyślny element docelowy) (1)->
„c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj” (domyślny element docelowy) (2)->
(element docelowy ClCompile) ->
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(478,12): warning C4101: "op": lokalna zmienna, do której nie istnieje odwołanie [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(479,5): warning C4101: "ret": lokalna zmienna, do której nie istnieje odwołanie [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]

„c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc.sln” (domyślny element docelowy) (1)->
„c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj” (domyślny element docelowy) (2)->
(element docelowy ClCompile) ->
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(498,7): error C4430: brak specyfikatora typu – założono, że int. Uwaga: C++ nie obsługuje domyślnie typu int [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(498,19): error C2065: "opus": niezadeklarowany identyfikator [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(498,34): error C2065: "pcm_buf": niezadeklarowany identyfikator [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(498,48): error C2065: "opus": niezadeklarowany identyfikator [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(498,70): error C2660: "opus_encode": funkcja nie przyjmuje 4 argumentów [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(500,3): error C3927: "->": nie można używać końcowego typu zwracanego po deklaratorze niebędącym funkcją [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(500,3): error C3484: błąd składni: oczekiwano "->" przed zwracanym typem [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(500,10): error C3613: brak typu zwracanego po "->" (przyjęto typ "int") [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(500,10): error C4430: brak specyfikatora typu – założono, że int. Uwaga: C++ nie obsługuje domyślnie typu int [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(500,4): error C2146: błąd składniowy: brakuje ";" przed identyfikatorem "b_o_s" [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(501,3): error C3927: "->": nie można używać końcowego typu zwracanego po deklaratorze niebędącym funkcją [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(501,3): error C3484: błąd składni: oczekiwano "->" przed zwracanym typem [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(501,10): error C3613: brak typu zwracanego po "->" (przyjęto typ "int") [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(501,10): error C4430: brak specyfikatora typu – założono, że int. Uwaga: C++ nie obsługuje domyślnie typu int [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(501,1): error C2086: "int op": zmiana definicji [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(501,4): error C2146: błąd składniowy: brakuje ";" przed identyfikatorem "e_o_s" [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(502,3): error C3927: "->": nie można używać końcowego typu zwracanego po deklaratorze niebędącym funkcją [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(502,3): error C3484: błąd składni: oczekiwano "->" przed zwracanym typem [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(502,15): error C3613: brak typu zwracanego po "->" (przyjęto typ "int") [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(502,15): error C4430: brak specyfikatora typu – założono, że int. Uwaga: C++ nie obsługuje domyślnie typu int [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(502,1): error C2086: "int op": zmiana definicji [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(502,4): error C2146: błąd składniowy: brakuje ";" przed identyfikatorem "granulepos" [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(503,3): error C3927: "->": nie można używać końcowego typu zwracanego po deklaratorze niebędącym funkcją [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(503,3): error C3484: błąd składni: oczekiwano "->" przed zwracanym typem [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(503,13): error C3613: brak typu zwracanego po "->" (przyjęto typ "int") [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(503,13): error C4430: brak specyfikatora typu – założono, że int. Uwaga: C++ nie obsługuje domyślnie typu int [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(503,1): error C2086: "int op": zmiana definicji [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(503,4): error C2146: błąd składniowy: brakuje ";" przed identyfikatorem "packetno" [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(504,3): error C3927: "->": nie można używać końcowego typu zwracanego po deklaratorze niebędącym funkcją [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(504,3): error C3484: błąd składni: oczekiwano "->" przed zwracanym typem [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(504,11): error C3613: brak typu zwracanego po "->" (przyjęto typ "int") [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(504,11): error C4430: brak specyfikatora typu – założono, że int. Uwaga: C++ nie obsługuje domyślnie typu int [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(504,1): error C2086: "int op": zmiana definicji [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(504,4): error C2146: błąd składniowy: brakuje ";" przed identyfikatorem "packet" [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(505,3): error C3927: "->": nie można używać końcowego typu zwracanego po deklaratorze niebędącym funkcją [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(505,3): error C3484: błąd składni: oczekiwano "->" przed zwracanym typem [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(505,10): error C3613: brak typu zwracanego po "->" (przyjęto typ "int") [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(505,10): error C4430: brak specyfikatora typu – założono, że int. Uwaga: C++ nie obsługuje domyślnie typu int [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(505,1): error C2086: "int op": zmiana definicji [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(505,4): error C2146: błąd składniowy: brakuje ";" przed identyfikatorem "bytes" [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(507,5): error C3927: "->": nie można używać końcowego typu zwracanego po deklaratorze niebędącym funkcją [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(507,18): error C3613: brak typu zwracanego po "->" (przyjęto typ "int") [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(507,18): error C4430: brak specyfikatora typu – założono, że int. Uwaga: C++ nie obsługuje domyślnie typu int [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(507,7): error C2146: błąd składniowy: brakuje ";" przed identyfikatorem "granulepos" [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(509,36): error C4430: brak specyfikatora typu – założono, że int. Uwaga: C++ nie obsługuje domyślnie typu int [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(509,1): error C2365: "ogg_stream_packetin": zmiana definicji; definicja poprzednia była "funkcja" [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(511,1): error C2059: błąd składniowy: "while" [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(511,53): error C2143: błąd składniowy: brakuje ";" przed "{" [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(511,53): error C2447: "{": brak nagłówka funkcji (stary styl formalnej listy?) [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(518,1): error C2059: błąd składniowy: "return" [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(519,1): error C2059: błąd składniowy: "}" [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]
c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\dllmain.cpp(519,1): error C2143: błąd składniowy: brakuje ";" przed "}" [c:\Users\dawid\Documents\rpgxp\ELTEN\C++\elten.dll\eltenvc\eltenvc\eltenvc\eltenvc.vcxproj]

Ostrzeżenia: 2
Liczba błędów: 52

Czas, który upłynął: 00:00:01.56

Asus RT-AC 1200G+, czyli tego po prostu nie da się opisać

Jest u mnie od dwóch tygodni, a ja uznałem, że czas o nim napisać. To router Asus RT-AC 1200G+.

Specyfikacja cudeńka

RT-AC 1200G+ obsługuje standardy 802.11 A/B/G/N/AC, 2,4GHz do 300mbps i 5GHz do 867mbps.
Nie jest to może szczytowa technologia AC, o AX nie wspominając, ale więcej niż wystarczająca w większości łączy nawet światłowodowych.
Napędzają go dwa procesory: BCM47189 i BCM43217, wyposażony jest w 16MB pamięci Flash i 128MB RAM.
Posiada 5 portów RJ45 10/100/1000 (1 WAN i 4 LAN) oraz jeden port USB 2.0.
I chyba tyle powinno nas interesować.

Wygląd cudeńka

Generalnie Router jest dość spory (207 x 148 x 35 mm). Na obudowie znajdziemy trzy przyciski (Power, Reset, WPS), pięć złączy RJ45, jedno USB, wejście zasilacza i 4 anteny 5DBi ? po dwie z każdej strony. I to właściwie tyle.
Warto tu wspomnieć, że anteny są niewymienialne, co z początku uważałem za największą wadę tego modelu.

Konfiguracja

Mroczne widmo.

Gdy kurier przyniósł router, przystąpiłem do pierwszego uruchomienia. Urządzenie stworzyło sieć o porywającej nazwie Asus i bez żadnego hasła. Dane dostępowe do panelu: admin/admin. Generalnie dla niewidomych to dobrze, ale podejrzewam, że przynajmniej co drugi klient tak to zostawia, a potem efekty są łatwe do przemyślenia. Przemyślenia, dla hakera oczywiście.
Zalogowałem się do panelu i? o koronawirus! Teraz wrażenie nieco minęło, nieco się przyzwyczaiłem, ale jeśli ktoś chce stworzyć jak najbardziej wkurzający panel administracyjny do używania z czytnikiem ekranu, to powinien natychmiastowo zapisać się na intensywny kurs do Asusa!
Niektóre opcje są widoczne tylko w Firefoxie, inne tylko w Chromie. Na tabelach, by dostać się do przysisku usunięcia pozycji, trzeba w Chromie doszukać się nawigacją obiektową, ale za to przycisk dodawania nowego jest oznajmiany tylko w FF. Do tego jeszcze dochodzi strona, która gdzieś tam w tle próbuje się odświeżać, zapewniając nam miłe przeskoki kursora w najmniej oczekiwanym momencie.
Myślę, że w 2020 roku już większość urządzeń obsługuje przynajmniej standard 802.11N. Robi to nawet moja Nokia E51. Postanowiłem więc się nie bawić i ustawiłem sieć: 2,4GHz: tylko 802.11N, 5GHz: tylko 802.11AC.
Oczywiście wspaniały panel Asusa nie potrafi zapisywać ustawień do restartu, on przy każdej jednej zmianie musi wykonywać minutowy reboot, a więc kiedy zakończymy edycję jednej karty, mamy czas na wstawienie sobie herbaty? Najlepiej Melisy.
W końcu pojawiły się sieci o odpowiednich nazwach i hasłach. Warto tu wspomnieć, że Router ten obsługuje szyfrowanie WPA2 Enterprise, które pozwala zamiast wspólnego hasła do sieci tworzyć dla użytkowników osobne konta ? przydatne, kiedy chcemy mieć większą kontrolę nad tym, kto się łączy i upewnić, że jedno wspólne hasło nie wycieknie. Standard jest już obsługiwany w zasadzie we wszystkich urządzeniach powstałych po roku 2010, iPhoneach, Windowsie, Macu, tylko wciąż jedynie Routery z wyższej półki go oferują.
Następnie ustawiłem login i hasło dostępowe do panelu, dodałem klucz SSH (tak, ten Router obsługuje SSH, o tym dalej) i postanowiłem sprawdzić, czy są aktualizacje systemu. I, owszem, były. Tylko zamiast radosnego przycisku ?Update now?, odnalazłem odnośnik do strony Asusa z prośbą o pobranie najnowszego Firmware. Generalnie to naprawdę nie jest wielki problem, tak działały Routery kiedyś i nikomu to nie przeszkadzało. Rzecz raczej w tym, że miast do strony produktu, link przekierował mnie do głównej strony Asusa, gdzie już musiałem znaleźć model, numer rewizji, pobrać sterownik? Podejrzewam, że 99,999 procent użytkowników, którym nawet zechce się sprawdzić dostępność aktualizacji, uzna, że gra nie jest warta świeczki. A przecież firmware często zawiera łatki zabezpieczeń.
Wreszcie przyszedł czas na test ostateczny. I przyniósł lekki zawód. Prędkość pobierania, miast 120mbps, pokazała 65mbps. Sprawa o tyle przykra, że Router był za jedną ścianą. Po chwili jednak zagadka postanowiła się rozwiązać. Mój laptop podłączył się do sieci 2,4GHz (ten sam SSID). Sprawa była jednak o tyle ciekawa, że Router był za jedną ścianą, w odległości może dwóch metrów.
Wymusiłem z Ustawień karty sieciowej podłączenie 5GHz i zauważyłem nagły spadek sygnału, z -54 do -77 DBm. No nie, to już była przesada. Cztery anteny, kształtowanie wiązki, a lepszy okazywał się TP-Link Archer C20 za 80zł?

Durnota, za przeproszeniem, ustawień fabrycznych.

Wróciłem do Panelu (od powietrza, ognia?) i zerknąłem na kartę Wireless, podkarta ?Professional?, 5GHz. O matko i córko!
Wyglądało to tak:
Enable radio: Yes
SSID: grzes
Hide SSID: No
Wireless mode: Ac
Optimize for Xbox: No
Channel bandwidth: 20/40/80 MHz
Channel: Auto
Extended channel: Disabled
Connection authentication: WPA2 Enterprise
WPA encryption: AES
WPA Server IP Address: 127.0.0.1
WPA Server port: 1812
Wpa Server Password: ********
Protected Management Frames: Disabled
Key exchange time: 3600.
Enable Wireless Scheduler: No
SET AP isolated: No
Roaming assistant: No
Enable IGMP Snooping: disable
Multicast rate (Mbps): Auto
Preamble Type: Long
AMPDU RTS: enable
RTS Treshold: 2347
DTIM Interval: 3
Beacon Interval: 100
Enable TX bursting: disable
WMM: Disable
WMM-No acknowledgement: Disable
WMM APSD: Enable
Priority definition: Enable
Optimize AMPDU Aggregation: Enable
Optimize ACK Suppression: Enable
Turbo QAM: Enabled
802.11ac beamforming: disabled
Universal beamforming: Disabled
Generalnie od razu na plus zapisuję, że poziom dostosowywania sieci jest ogromny. Jednak to, jak ona była dostosowana mnie leciuteńko zmroziło. Uruchomiłem Wi-Fi Analyzer i?
Sieć grzes ustawiła się na kanał auto, tak?
W paśmie 2,4GHz znajdowały się dwie, już trzy sieci na kanale 7. Były 4 kanały wolne, na siódemce sieć sąsiadów i z Piekarni na przeciwko, ale ten Asus i tak wybrał siódemkę. Bo tak.
W zasięgu była tylko jedna, jedyna sieć 5GHz, ta z Archera C20, na kanale 36. Zgadnijcie, jaki autokanał wybrał Asus. Oczywiście, że 36!
Zmieniłem więc nieco konfigurację dla 2,4 i 5 GHz.
Channel: z auto na 3 dla 2,4GHz i 44 dla 5GHz.
Akurat były wolne.
Channel bandwidth: z 20/40 MHz mixed na 40MHz dla 2,4GHz i z 20/40/80 MHz Mixed na 80MHz dla 5GHz.
I nie, nie pojmę, czemu przy przełączeniu trybu nadawania to się nie zmieniło samo. Po prostu nie pojmę.
Protected Management Frames: z disabled na enabled.
powiedzcie mi, kto w ogóle wpadł na pomysł, by tak ważny element zabezpieczania sieci bezprzewodowych,jak PMF, domyślnie pozostawiać wyłączony?
IGMP Snooping: z disable na enable.
Generalnie wielkiego ruchu Multicast tu nie przewiduję, ale skoro Router umie, żal marnować. Mimo wszystko nie dziwię się, że domyślnie to wyłączyli.
WMM: disable na enable.
Ja nawet się nie pytam, czemu tego nie włączają. Chyba lepiej się nie zastanawiać.
Multicast Rate (Mbps): z auto na OFDM 54.
Tylko dlatego, że straciłem zaufanie do jakiegokolwiek auto w tym routerze.
Preamble Type: z Long na Short.
Bo poco?
Beacon Interval: ze 100 na 300.
Generalnie to był mały eksperyment, bałem się, czy nie za dużo, ale działa, więc?
Beamforming: z Disabled na Enabled.
Reklamujesz Router jako posiadający super kształtowanie wiązki, a potem je z domyślnych ustawień? wyłączasz. Pozdrawiam.
Enable TX bursting: z disable na Enable.
Nie do końca wiem, na czym ten hipotetyczny bursting polega, ale skoro bursting, to burstujemy.
Po restarcie Routera wykonałem ponowny test i, magia. Na 2,4GHz z -54DBm zrobiło się -49DBm, a na 5GHz z -77DBm całe -63DBm. Da się? Da się.
MBP-Dawid:~ dpieper$ speedtest-cli
Retrieving speedtest.net configuration?
Testing from Chopin Telewizja Kablowa spolka z ograniczona odpo (46.29.144.209)?
Retrieving speedtest.net server list?
Selecting best server based on ping?
Hosted by Maxnet (Gdynia) [17.06 km]: 16.977 ms
Testing download speed??????????????????????????..
Download: 118.27 Mbit/s
Testing upload speed??????????????????????????????????
Upload: 12.25 Mbit/s

Rekonfiguracja LAN

Z bijącym sercem przeszedłem do zakładki LAN, bojąc się, jakie potworki tu się nagromadziły.
Zmieniłem adres IP ze 192.168.1.1 na 192.168.0.1 (bo mogłem) i zostawiłem maskę 255.255.255.0. Innych ustawień tu nie było.
Przechodzimy na zakładkę DHCP.
Oszczędzam wam cytowania wszystkich ustawień, bo sporo ich tutaj i większość nieważna, skupię się na najważniejszych.
RT-AC1200G+ Domain Name
I znowu puste pole. A potem pewnie pojawiają się pytania na forach, czemu moje urządzenia się z sobą nie łączą. Wpisałem zwyczajowe lan uznając, że już nic mnie nie zdziwi.
Pula DHCP była ustawiona od 192.168.0.2 dziwnym nowym zwyczajem, do którego nigdy się nie przyzwyczaję. Zawsze było tak, że pulę ustawia się od adresu 100 do 200/254, a adresy od 2 do 99 rezerwuje. Ale widać nowa moda nadchodzi. Ja jednak jestem staroświecki i przekonfigurowałem pulę na zakres 100-254. A przy okazji zmieniłem czas dzierżawy z 12 do 24 godzin. Ponownie bo mogę.
Default gateway, nie wiedzieć czemu, pusty? Eeee, nie, zaaraz, ja się nie miałem niczemu dziwić! To wpisałem 192.168.0.1
Adres DNS skonfigurowałem na Cloudflarowego 1.1.1.1, moim zdaniem najlepsza możliwa opcja, w zapasie Googleowy 8.8.8.8.
Następnie poustawiałem stałe dzierżawy dla podrzędnego Routera dla biura, Macbooka, mojego laptopa, komputera brata i serwera sklepowego, co chwila mając ochotę ukręcić komuś z Asusa łeb za ten interfejs.
Ostatnia interesująca mnie zakładka to Route. Chciałem ustawić Routing statyczny na Router TL-MR3420, który w nowej konfiguracji sieci stanął w biurze do obsługi tamtejszych komputerów, drukarki i innych takich. I męczyłem się z tym dobre pół godziny.
Dlaczego? Bo wpisałem przypadkowo jako adres bramy 192.168.1.2 zamiast 192.168.0.2 i potem 20 minut głowiłem się, jak to koronawiruserstwo wywalić.
Na koniec jeszcze w ustawieniach NAT włączyłem przekazywanie CTF. Sieć LAN skonfigurowana.

SSH

Poustawiałem jeszcze serwer VPN, Firewalla i QoS, ale nie są to na tyle zaskakujące i różniące się wobec innych Routerów elementy, by warto było to opisywać. O wiele ciekawszą kwestią jest dostęp SSH do tego routera.
MBP-Dawid:~ dpieper$ ssh admin@192.168.0.1 -i /home/dpieper/.ssh/home_1200g_rsa
Enter passphrase for key ‚/home/dpieper/.ssh/home_1200g_rsa’:
admin@RT-AC1200G+:/tmp/home/root# uname -a
Linux RT-AC1200G+ 2.6.36.4brcmarm #1 PREEMPT Tue Mar 17 10:30:28 CST 2020 armv7l GNU/Linux
Jak widać, Router działa pod kontrolą Linuxa, co prawda w jakiejś archaicznej wersji, ale Linuxa.
Generalnie, jego pamięć jest readonly, prócz małego ramdysku podmontowanego jako /tmp i folderu /etc. Jednak po podłączeniu pendrive?a do portu USB 2.0, pojawia się przed nami możliwość jego zamontowania. Ustawiłem więc odpowiednio fstab.
Nieco bawiąc się catem (dalej nie odkryłem, jaki jest tu zainstalowany edytor tekstu, bo ni vima, ni nano), udało się skonfigurować Router tak, by montował pendrive w odpowiednich miejscach. Teraz wystarczyło odpowiednio zaktualizować zmienne środowiskowe i? Udało się tu zainstalować OPKG, menedżer pakietów z Open-WRT.
To zaś pozwoliło mi zainstalować nawet Rubiego i Pythona, i obsługę SQLite, która obecnie służy do zapisywania logów pingu i prędkości łącza co 15 minut. ?
Ustawiłem też na Routerze tunel SSH z Raspberrym Pi na Powiślu, z przekazaniem kilku portów, co bardzo dobrze działa i nie zerwało się od tygodnia, więc?

Aplikacja mobilna

Jako dodatek wspomnę, że istnieje aplikacja dla iOS o wdzięcznej nazwie ?Asus Router?, która służy do zarządzania ich, no, routerami. Generalnie jest cudowna.
Działa świetnie z Voice Overem i, w przeciwieństwie do panelu administracyjnego, jest bardzo dostępna i czytelna. Zwłaszcza przydaje się karta urządzeń, na której możemy sprawdzać adresy IP/MAC urządzeń i siłę sygnału, ale także i przede wszystkim na bieżąco ustawiać dla każdego urządzenia limity prędkości łącza.
Można też w niej zmieniać ustawienia Routera, niestety zakres dostępnej konfiguracji jest bardzo podstawowy, nie ma nawet Routingu Statycznego czy wyboru kanału sieci.

I jak to działa?

Przyznam, że z początku bardzo przeraziły mnie pierwsze wyniki Routera ? strach, który szybko minął. Kształtowanie wiązki działa bardzo dobrze, a 4 anteny, choć tylko 5DBi, to jednak 4 anteny. Jest to pierwszy Router, który osiągnął pokrycie sygnału całego domu, choć szczerość nakazuje przyznać, że w salonie mój iPhone pokazywał -82DBm i często się rozłączał, XPS działał stabilnie, choć ze 120mbps zostawało marne 16.
Rozwiązałem ten problem, podłączając w kuchni wzmacniacz sygnału odkupiony kiedyś od Papierka, także Asusa, RP-AC66, co ostatecznie dało moc sygnału taką, że w każdym pomieszczeniu prędkość wynosi przynajmniej 60mbps.
Do Routera podłączyłem starego TP-Linka TL-MR3420 i ustawiłem na niego Routing statyczny. Nie szło jednak tu o zwiększenie zasięgu tym razem, a po prostu o brakujące porty LAN.

Podsumowanie?

Asus RT-AC 1200G+, zwłaszcza w swojej półce cenowej (300zł) to naprawdę perełka. Oczywiście niższa cena bierze się z kompromisu na standardzie AC (AC1200 zamiast AC1750), ale zwykle nie stanowi to problemu.
Router ma naprawdę ogromne możliwości, a dzięki połączeniu SSH i instalacji OPKG, praktycznie nieograniczone.
Kształtowanie wiązki i naprawdę zaawansowane ustawienia nadajnika pozwalają na bardzo dobrą optymalizację sieci Wi-Fi, z uzyskaniem zasięgu na całej działce, sprawdzone.
Jest jednak kilka wad, o których trzeba wspomnieć. Pierwszą zauważoną są nieodkręcalne anteny. Ta jednak zmalała po uruchomieniu kształtowania wiązki, która czyni tu cuda. Dzięki niej Router z dwiema antenami 2,4GHz 5DBi osiąga lepszy zasięg, niż TL-MR3420 z dwiema 20DBi w tym samym paśmie.
Druga, dotycząca czytników ekranu, to tragiczny panel konfiguracji. Tego po prostu nie da się opisać.
Jednak największą bolączką tego Routera jest konfiguracja domyślna, która jest po prostu tragiczna. Wyłączenie pewnych technologii i zły dobór kanału sprawiają, że Router traci kilkukrotnie na zasięgu. Jak się okazało, drobna korekta ustawień z najsłabszego uczyniła najlepszy pod względem siły sygnału Router, z jakim kiedykolwiek miałem do czynienia ? na co liczyłem, kupując go.

Routerowa dobra zmiana

Zaryzykuję twierdzenie, że sieć jest jednym z najdziwniejszych elementów budowy domowej infrastruktury informatycznej. Bo większość osób wie o procesorach, a przynajmniej coś im mówią określenia Intel Core I7, AMD Ryzen itp. Może nie do końca niektórzy wiedzą, co to albo że w ogóle tych coreów I7 jest więcej, ale mniej-więcej wiadomo, w którym dzwoni kościele. Tym bardziej wiele osób jest świadomych, co to jest pamięć RAM i że można jej mieć więcej, a już zwłaszcza, czym jest dysk twardy.
Może przeciętny użytkownik nie złoży sam komputera albo nie zapozna się wnikliwie ze specyfikacją kupowanego, ale określenia typu 256GB SSD, 8GB RAM czy procesor Core I5 coś mu powiedzą, dadzą jakiś ogląd na sprawę. Router jest jednak nadal takim dziwnym stworzeniem, przy którym nawet nieco bardziej zaawansowany człowiek pójdzie do sklepu i powie, że chce mieć, no właśnie, "Router". I na tym się kończy.
Muszę z resztą przyznać, że zasadniczo nie ma tu czemu się dziwić. Routera nie przelicza się zwykle na klatki grafiki, a w Internecie ważniejsze jest nasze łącze, niż używany sprzęt. Bo jeśli nasz dostawca daje nam 24mbps, chociażbyśmy klękali przed Routerem i całowali go po porcie WAN, cudu nie będzie.
Potrzeba lepszego sprzętu sieciowego zasadniczo w wypadku, gdy nie chcemy w domu odpalać serwerowni, ma tylko kilka przychodzących mi na myśl motywacji:
* Gdy mamy szybkie złącze światłowodowe i radio 2,4GHz lub 5GHz AC 433 nie pozwala osiągnąć pełnej prędkości,
* Gdy mamy w domu naprawdę dużo urządzeń Wi-Fi i proste modele nie wyrabiają,
* Gdy mamy naprawdę duży dom i musimy stworzyć siatkę punktów łączności typu Mesh,
* Gdy chcemy mieć naprawdę dużą kontrolę nad tym, kto się nam do sieci przyłącza i decydujemy się na protokół WPA2 Enterprise,
* Gdy mamy fioła na punkcie sprzętu i szczytem naszych marzeń jest konfigurowanie ustawień WMM i innych dziwadeł tego typu.

Generalnie, jak widać, prawdopodobieństwo wystąpienia któregoś z powyższych przypadków jest stosunkowo niewielkie. Mnie jednak, co ciekawe, ostatnio zaatakowały – w mniejszym lub większym stopniu – wszystkie z nich.

Szkic tego, jak to było

Jest sobie dom. Dom może z większych, ale na pewno nie gigant. Dom ten był jednak kilkukrotnie rozbudowywany, a w dodatku budowany kilkadziesiąt lat temu bez szczególnej dbałości o dokładność. W rezultacie ściany tego domu dla sygnału radiowego są niczym czarne dziury – niby był, a nie ma. Próbowałem wszystkiego, włącznie z wymianą anten na dwie 20dBi. Nic nie działało, zawsze potrzebny był drugi Router do zwiększenia pokrycia sygnału!
Sprawę jeszcze bardziej komplikowało to, co w tej sieci siedzi. A obsługuje ona jednocześnie firmę taty, nasze urządzenia domowe, wszystkich gości, a na dodatek spora część z tego musi gadać i słuchać świata zewnętrznego, bo tu jest biuro, z którego pozostałe sklepy pobierają dane o produktach. Po prostu miodzio.

Dotychczasowa konfiguracja

Generalnie sprawa wyglądała tak, że były trzy Routery.
Pierwszy z nich to dLink Dir300 2B. To urządzenie było w ogóle pierwszym routerem, jaki kupowany był dla tego domu i podtrzymuję opinię, że jest nieśmiertelne. Tyle, ile musiało przy mnie przeżyć, a do dziś działa.
Jak na rok zakupu, całkiem sporo umie ten Router, bo obsługuje już pasmo 802.11N, co w momencie jego produkcji wcale nie było standardem.
Technicznie dodam, że działał w podsieci 192.168.1.0, maska 255.255.255.0.
Do niego podpięte były dwa routery. Pierwszy z nich to stary dobry TP-Link TL-MR3420 rev 2.
A historia tego TP-Linka była następująca: kiedy pierwszy raz pojawiła się potrzeba kupienia drugiego Routera, przygotowałem sobie listę urządzeń, które mnie ciekawiły. Pojechałem do sklepu Komputronik w Gdyni i spytałem o nie. Odpowiedź brzmiała: "Żadnego nie ma".
– Co więc jest? – Zapytałem, nieco zdumiony.
Tak o to zaproponowano mi tego TP-Linka, a ja tylko upewniłem się, czy aby na pewno obsługuje Open-WRT.
I, owszem, obsługuje, śmiga na tym nawet. Co prawda ma porywającą pojemność Flash 4MB, co wymusza przy każdej zmianie oprogramowania ręczną rekompilację systemu i update z obrazu zamiast używania menedżera pakietów, ale działa.
Działał on w podsieci 192.168.0.0, maska 255.255.255.0 oczywiście.
Ostatni Router… No cóż, ostatni Router był prezentem – i to jednym z bardziej udanych. To TP-Link Archer C20, pierwszy sprzęt w tym domu, który działał 2-zakresowo, 2,4GHz B/G/N i 5GHz N/AC, niestety tylko 433mbps. Oszczędziłem mu instalacji Open-WRT, w owym czasie bowiem wersji dla niego jeszcze nie było, a potem nawet już tego nie sprawdzałem, skoro jego możliwości fabryczne są dostateczne. Ten router obsługiwał cały sklep.
Działał w podsieci 192.168.128.0, maska 255.255.128.0, która była dalej nieco rozbita, ale to już nie temat tego artykułu.
Dodam jeszcze, że z pierwszego routera ustawiłem statyczny Routing na TP-Linka TL-MR3420. Na Archera C20 nigdy się mi nie udało, to znaczy Routing działa, ale nigdy nie odkryłem, jak w nim przekonfigurować Firewalla, a że miałem wtedy gorszy dzień, rzuciłem to w, no, koronawirusa, i uznałem, że sobie poradzę, korzystając ze zwykłego przekierowania odpowiednich portów z komputera do Archera i z Archera na świat.

"Wind of change"

Zawsze uważałem, że cuda istnieją, a największym dowodem na to jest fakt, że tak porozwalana sieć domowa bezawaryjnie działała jakoś ze 7 lat. Sprawa jednak skomplikowała się, gdy rozpoczęło się koronawirusowe nauczanie zdalne. Dwa komputery w biurze, po jednym komputerze na dwóch braci, mój laptop i Macbook, telewizor z Netflixem, serwer sklepowy, terminale płatnicze, monitoring, kasy Wi-Fi, domowy Raspberry Pi. Router jęczał w agonii. I, co ciekawe, nie z powodu konfiguracji, którą dawno powinien szlag trafić, a przepustowości naszego łącza, która wynosiła 60mbps download i 6mbps upload. Nie uważałem nigdy, że to mało, ale widać istnieją granice. Doszło wreszcie do tego, że wykonałem SpeedTest, a wynik wynosił: download 3,11mbps, upload 0,13mbps.
Jako, że wszystko się pięknie ścinało, pojawiały się problemy i domowe i firmowe, a mała cyfrowa burza rosła miast maleć, postanowiłem dokonać lepszej zmiany. I wpadłem na genialny pomysł, który nazywał się QOS. Nigdy jakoś go nie ustawiałem u nas w domu, a może przyszedł czas?
I tu wielki zawód numer 1. dLink Dir300 nie obsługuje QOS.
No to może poprosimy o lepsze parametry łącza? Zaproponowano nam niewiele drożej łącze 120mbps download i 12mbps upload. Najpierw się zgodziliśmy, a potem… doznałem oświecenia.

Oświecenie

Co prawda jeden z Routerów, TP-Link Archer C20, obsługiwał pasmo 5GHZ 433mbps, co jednak z tego, skoro był podpięty do dLinka przez port LAN 100mbps? I co z tego, że ten dLink miał teoretyczną prędkość transmisji 150mbps (to jeszcze nie były czasy 300), skoro połowa tego szła na synchronizację danych w firmie? Niby mogłem poprzełączać Routery, pobawić się kablami i zreorganizować sieć, ale i tak nie dysponowałem niczym o gigabitowym złączu, niczym prócz Raspberrego Pi, który, jak na złość, nie ma Switcha.
Żeby sprawę jeszcze bardziej pokomplikować, pod koniec roku do Bolszewa trafia Światłowód, a my już mamy umowę, że od stycznia dostaniemy Internet 650mbps. Super perspektywa! Zwłaszcza, gdy najlepsze urządzenie sieciowe w domu ma prędkość maksymalną 433mbps.

Dobra zmiana.

Usiadłem więc do komputera i zacząłem przyglądać się ofercie Routerów na stronie morele.pl. Minęły już czasy, gdy liczące się firmy to tylko Cisco i Mikrotik, zwłaszcza w zastosowaniach nieserwerowych. Dziś wiele tańszych Routerów sprawdzi się w zadaniach, w których Cisco krztusił się 10 lat temu.
Poczytałem recenzje, poporównywałem produkty i ostatecznie uznałem, że najlepszą opcją jest Asus RT AC 1200G+. Planowałem na Router przeznaczyć do 500zł, model ten kosztował 300, więc był sporo poniżej granicy budżetu, a spelniał, wedle opisu i recenzji, wszystkie moje oczekiwania.
Router ten ma 2-zakresową sieć Wi-Fi z obsługą 802.11AC 1167mbps. Nie jest to najnowsze AC 1750, ale, bądźmy szczerzy, nie będziemy mieli łącza 1750mbps. Nie ma mowy. A w wewnętrznej sieci nawet 150mbps dawało radę.
Jest także wyposażony w 4 porty LAN 10/100/1000, obsługę kształtowania wiązki, QOS, własny serwer VPN i jeszcze kilka mądrych funkcji, które były rajem dla mych oczu.
A jak się sprawdził, jak działa i czym jest w rzeczywistości? O tym napiszę w recenzji jakoś wkrótce. 🙂