[{"content":"Чи бувало у вас таке, що в неділю ввечері вас починає мучити питання: \u0026ldquo;А скільки ж все-таки способів надрукувати \u0026quot;Hello World\u0026quot; у консоль в C++?\u0026rdquo;. Сподіваюсь, ні, бо це вже як мінімум профдеформація. Але я задався таким питанням, і зрозумів, що немає жодного джерела, яке відповідає на нього. Тому я вирішив зробити таке джерело сам.\nОскільки C++ - це тепер не просто мова програмування, а ще й мова метапрограмування, розглянемо два окремі випадки: друк \u0026quot;Hello World\u0026quot; в рантаймі та на етапі компіляції.\nМоєю метою було порахувати всі справді різні способи. І це складніше, ніж здається. Бо врешті майже все зводиться до чогось, що викликає write(2) через різну кількість шарів абстракцій, причому часто різниця між способами мікроскопічна. Я використав наступний підхід:\n1 Спосіб - це окрема іменована сутність, яка напряму друкує рядок. Весь код протестований на Fedora 44 (ядро 7.0, x86-64) зі стеком GCC 16.1, Clang 22.1, glibc 2.43. Усі сніпети лежать в репозиторії блогу, разом зі скриптами для запуску.\nЯ тут розглянув як методи, що чітко відповідають C++26, так і методи, які працюють специфічно на Linux x86-64.\nРантайм Спочатку код, який повністю відповідає C++26, потім код, який працює тільки на POSIX системах, далі POSIX розширення, а наостанок Linux-специфічний код.\nСтандартний C++ Канонічний набір Це ті способи, які зустрічаються в 99% випадків.\n#1. std::cout::operator\u0026lt;\u0026lt;\n1 2 3 4 5 6 #include \u0026lt;iostream\u0026gt; int main() { std::cout \u0026lt;\u0026lt; \u0026#34;Hello World\\n\u0026#34;; } Всім відомо, що \\n сам по собі не флашить буфер. Якщо треба flush, то треба замінити \\n на std::endl:\n1 std::cout \u0026lt;\u0026lt; \u0026#34;Hello World\u0026#34; \u0026lt;\u0026lt; std::endl; // \u0026#39;\\n\u0026#39; + flush або викликати явний flush():\n1 std::cout.flush(); Чомусь у більшості туторіалів для початківців використовують std::endl. Хоча, на мою думку, в більшості випадків він не потрібен, і краще використати просто \\n.\n#2. printf()\n1 2 3 4 5 6 #include \u0026lt;cstdio\u0026gt; int main() { printf(\u0026#34;Hello World\\n\u0026#34;); } printf парсить format string у пошуках %, тому технічно тут є зайва робота. Хоча компілятори давно це оптимізують до puts(\u0026quot;Hello World\u0026quot;).\nВідступ про std::ios_base::sync_with_stdio(false)\nМаленький відступ, знайомий людям, що займались competitive programming. \u0026ldquo;Нове\u0026rdquo; iostream-based API в C++ додавали із сумісністю з C в пріоритеті. У тому плані, що код, який вже використовував printf (або будь-яке інше C stdio I/O) міг без зайвих налаштувань почати використовувати std::cout з гарантованим очікуваним порядком виводу. Це важливо, бо C та C++ код часто лінкується разом.\nТому, за замовчуванням, стандартні C++ потоки синхронізовані з відповідними C потоками: std::cout з stdout, std::cin з stdin, std::cerr і std::clog з stderr (плюс їхні wide-аналоги). Коли синхронізація увімкнена, C++ потоки можуть ділити буфер з відповідним FILE*. Два послідовні різні виклики operator\u0026lt;\u0026lt; та printf відпрацюють у тому порядку, в якому написані. Тут, до речі, на мою думку, C++ ламає принцип zero-overhead.\nЦю поведінку можна виключити, викликавши std::ios_base::sync_with_stdio(false). Важливо: виклик має бути до будь-яких I/O операцій, інакше поведінка implementation-defined. Після цього C++ потоки отримують власний незалежний буфер. Але якщо після цього міксувати operator\u0026lt;\u0026lt; та printf, порядок виведення більше не буде гарантований, бо кожен механізм буферизує незалежно.\n#3. fprintf()\n1 2 3 4 5 6 #include \u0026lt;cstdio\u0026gt; int main() { fprintf(stdout, \u0026#34;Hello World\\n\u0026#34;); } До речі, стандартом напряму чітко визначено, що printf - це fprintf(stdout, ...).\n#4. puts()\n1 2 3 4 5 6 #include \u0026lt;cstdio\u0026gt; int main() { puts(\u0026#34;Hello World\u0026#34;); } Як на мене, це найкращий спосіб, коли треба просто надрукувати рядок у термінал без форматування. Функція сама додає \\n.\n#5. fputs()\n1 2 3 4 5 6 #include \u0026lt;cstdio\u0026gt; int main() { fputs(\u0026#34;Hello World\\n\u0026#34;, stdout); } На відміну від puts(), ця функція сама не додає \\n.\n#6. std::print() - C++23\n1 2 3 4 5 6 #include \u0026lt;print\u0026gt; int main() { std::print(\u0026#34;Hello World\\n\u0026#34;); } Функція побудована на базі std::format, що є типобезпечним. Також вона не тягне за собою весь iostream. На мою думку, це те, що мало бути в мові вже дуже давно, а не з 2023 року.\nМаленька деталь: це перевантаження std::print пише в FILE* stdout, а не в std::cout. Тут можна побачити, що комітет розуміє, що iostream не був дуже вдалим рішенням у ретроспективі.\n#7. std::println() - C++23\n1 2 3 4 5 6 #include \u0026lt;print\u0026gt; int main() { std::println(\u0026#34;Hello World\u0026#34;); } Те саме, що std::print, але з автоматично доданим \\n наприкінці.\n#8. std::print(stdout, …) - C++23\n1 2 3 4 5 6 7 #include \u0026lt;cstdio\u0026gt; #include \u0026lt;print\u0026gt; int main() { std::print(stdout, \u0026#34;Hello World\\n\u0026#34;); } Це перевантаження приймає будь-який FILE*.\n#9. std::println(stdout, …) - C++23\n1 2 3 4 5 6 7 #include \u0026lt;cstdio\u0026gt; #include \u0026lt;print\u0026gt; int main() { std::println(stdout, \u0026#34;Hello World\u0026#34;); } Те саме, що #8, але з автоматично доданим \\n наприкінці.\n#10. std::print(std::ostream\u0026amp;, …) - C++23\n1 2 3 4 5 6 7 #include \u0026lt;iostream\u0026gt; #include \u0026lt;print\u0026gt; int main() { std::print(std::cout, \u0026#34;Hello World\\n\u0026#34;); } А це перевантаження приймає std::ostream\u0026amp;.\n#11. std::println(std::ostream\u0026amp;, …) - C++23\n1 2 3 4 5 6 7 #include \u0026lt;iostream\u0026gt; #include \u0026lt;print\u0026gt; int main() { std::println(std::cout, \u0026#34;Hello World\u0026#34;); } Те саме, що #10, але з автоматично доданим \\n наприкінці.\nРівень нижче: байти й символи #12. std::ostream::write()\n1 2 3 4 5 6 7 8 #include \u0026lt;iostream\u0026gt; #include \u0026lt;string_view\u0026gt; int main() { constexpr std::string_view msg = \u0026#34;Hello World\\n\u0026#34;; std::cout.write(msg.data(), msg.size()); } Прямий запис n байтів. Працює на будь-якому std::ostream, і все аналогічно йде через буфер.\n#13. std::streambuf::sputn()\n1 2 3 4 5 6 7 8 #include \u0026lt;iostream\u0026gt; #include \u0026lt;string_view\u0026gt; int main() { constexpr std::string_view msg = \u0026#34;Hello World\\n\u0026#34;; std::cout.rdbuf()-\u0026gt;sputn(msg.data(), msg.size()); } Те, що std::cout.write робить усередині. std::ostream::write спершу створює sentry (перевіряє стан потоку), а тоді викликає sputn на std::streambuf.\n#14. std::ostream::put()\n1 2 3 4 5 6 7 8 9 10 #include \u0026lt;iostream\u0026gt; #include \u0026lt;string_view\u0026gt; int main() { for (char c : std::string_view(\u0026#34;Hello World\\n\u0026#34;)) { std::cout.put(c); } } Це кладе в буфер по одному символу за раз. У ядро це піде одним write під час флашу.\n#15. std::streambuf::sputc()\n1 2 3 4 5 6 7 8 9 10 #include \u0026lt;iostream\u0026gt; #include \u0026lt;string_view\u0026gt; int main() { for (char c : std::string_view(\u0026#34;Hello World\\n\u0026#34;)) { std::cout.rdbuf()-\u0026gt;sputc(c); } } Те саме, що sputn, тільки посимвольно, і рівно те, як cout.put імплементовано всередині.\n#16. fwrite()\n1 2 3 4 5 6 7 8 #include \u0026lt;cstdio\u0026gt; #include \u0026lt;string_view\u0026gt; int main() { constexpr std::string_view msg = \u0026#34;Hello World\\n\u0026#34;; fwrite(msg.data(), 1, msg.size(), stdout); } #17. putchar()\n1 2 3 4 5 6 7 8 9 10 #include \u0026lt;cstdio\u0026gt; #include \u0026lt;string_view\u0026gt; int main() { for (char c : std::string_view(\u0026#34;Hello World\\n\u0026#34;)) { putchar(c); } } #18. putc()\n1 2 3 4 5 6 7 8 9 10 #include \u0026lt;cstdio\u0026gt; #include \u0026lt;string_view\u0026gt; int main() { for (char c : std::string_view(\u0026#34;Hello World\\n\u0026#34;)) { putc(c, stdout); } } Те саме, що putchar, але з явним FILE*. putc за стандартом може бути макросом.\n#19. fputc()\n1 2 3 4 5 6 7 8 9 10 #include \u0026lt;cstdio\u0026gt; #include \u0026lt;string_view\u0026gt; int main() { for (char c : std::string_view(\u0026#34;Hello World\\n\u0026#34;)) { fputc(c, stdout); } } Те саме, що putc, але гарантовано функція.\nФорматування як окрема операція: \u0026lt;format\u0026gt; std::cout \u0026lt;\u0026lt; std::format(\u0026quot;…\u0026quot;) я не рахую, бо по суті це просто operator\u0026lt;\u0026lt;.\n#20. std::format_to()\n1 2 3 4 5 6 7 8 9 10 #include \u0026lt;format\u0026gt; #include \u0026lt;iostream\u0026gt; #include \u0026lt;iterator\u0026gt; #include \u0026lt;string_view\u0026gt; int main() { constexpr std::string_view message = \u0026#34;Hello World\u0026#34;; std::format_to(std::ostream_iterator\u0026lt;char\u0026gt;(std::cout), \u0026#34;{}\\n\u0026#34;, message); } Форматує одразу в output iterator.\n#21. std::format_to_n()\n1 2 3 4 5 6 7 8 9 10 11 12 #include \u0026lt;format\u0026gt; #include \u0026lt;iostream\u0026gt; #include \u0026lt;iterator\u0026gt; #include \u0026lt;string_view\u0026gt; int main() { constexpr std::string_view message = \u0026#34;Hello World\u0026#34;; // +1 accounts for the \u0026#39;\\n\u0026#39; appended by the format string constexpr auto cap = message.size() + 1; std::format_to_n(std::ostream_iterator\u0026lt;char\u0026gt;(std::cout), cap, \u0026#34;{}\\n\u0026#34;, message); } Те саме, що format_to, але з обмеженням на кількість символів. Використовується, коли повідомлення форматується в буфер фіксованого розміру.\niostream + алгоритми STL Навіщо писати цикл, якщо можна зібрати докупи три шаблони?\n#22. STL-алгоритм + output iterator\n1 2 3 4 5 6 7 8 9 10 #include \u0026lt;algorithm\u0026gt; #include \u0026lt;iostream\u0026gt; #include \u0026lt;iterator\u0026gt; #include \u0026lt;string_view\u0026gt; int main() { constexpr std::string_view msg = \u0026#34;Hello World\\n\u0026#34;; std::ranges::copy(msg, std::ostream_iterator\u0026lt;char\u0026gt;(std::cout)); } Замість std::ranges::copy тут так само спрацюють std::copy, std::for_each, std::ranges::for_each.\nА ось важлива деталь: який саме output iterator взяти, визначає, через що будуть передаватись байти. std::ostream_iterator\u0026lt;char\u0026gt; передає кожен символ через operator\u0026lt;\u0026lt; (тобто formatted output, як у #1). А std::ostreambuf_iterator\u0026lt;char\u0026gt; пише прямо в streambuf через sputc, оминаючи весь шар форматування (як sputc, #15):\n1 std::ranges::copy(msg, std::ostreambuf_iterator\u0026lt;char\u0026gt;(std::cout)); А якщо в cout пишуть кілька потоків одночасно? #23. std::osyncstream - C++20\n1 2 3 4 5 6 7 #include \u0026lt;iostream\u0026gt; #include \u0026lt;syncstream\u0026gt; int main() { std::osyncstream(std::cout) \u0026lt;\u0026lt; \u0026#34;Hello World\\n\u0026#34;; } osyncstream накопичує output у власному буфері й атомарно передає його в цільовий потік під час деструкції. Це зроблено тому, що якщо у вас кілька потоків пишуть у ostream без синхронізації, то їхні рядки можуть перемішатися.\nВиклик printf() атомарний (stdio бере лок FILE* на час виклику), і std::cout::operator\u0026lt;\u0026lt; на практиці теж, бо libstdc++ за замовчуванням ходить через той самий лок. Щоправда, стандарт атомарності одного \u0026lt;\u0026lt; не гарантує, а гарантує лише відсутність data race. Але std::cout \u0026lt;\u0026lt; \u0026quot;Hello\u0026quot; \u0026lt;\u0026lt; \u0026quot;World\u0026quot; - це вже 2 окремі виклики оператора, і між ними може вклинитись std::cout::operator\u0026lt;\u0026lt;, виконаний в іншому потоці.\nstd::osyncstream склеює всю послідовність operator\u0026lt;\u0026lt; в один атомарний виклик. По суті це те саме, що зібрати рядок у std::ostringstream, а потім один раз зробити std::cout \u0026lt;\u0026lt; stream.str().\nstderr і широкі потоки #24. std::cerr::operator\u0026lt;\u0026lt;\n1 2 3 4 5 6 #include \u0026lt;iostream\u0026gt; int main() { std::cerr \u0026lt;\u0026lt; \u0026#34;Hello World\\n\u0026#34;; } Той самий operator\u0026lt;\u0026lt;, але інший потік. std::cerr - це стандартний потік помилок (дескриптор 2 на Linux). Існує він для зручності, щоб можна було легко розділяти через перенаправлення потоку помилки і звичайний output.\nУ cerr виставлений прапор unitbuf, тому він флашиться після кожної операції виведення. Бо зазвичай ми хочемо дізнатися про помилку одразу, як вона виникла, а не коли буфер вирішить зафлешитись.\n#25. std::clog::operator\u0026lt;\u0026lt; у stderr, але з буфером\n1 2 3 4 5 6 #include \u0026lt;iostream\u0026gt; int main() { std::clog \u0026lt;\u0026lt; \u0026#34;Hello World\\n\u0026#34;; } Той самий cerr, але без виставленого unitbuf. Задуманий він для діагностичних повідомлень, які не є \u0026ldquo;терміновими\u0026rdquo;.\nТут можна було ще окремо зарахувати кожен перелічений вище метод cout ще 2 рази (один для cerr, один для clog), але я не буду.\n#26. fprintf(stderr, …) - C stdio у stderr\n1 2 3 4 5 6 #include \u0026lt;cstdio\u0026gt; int main() { fprintf(stderr, \u0026#34;Hello World\\n\u0026#34;); } Аналогічна функціональність до cerr, але в cstdio.\n#27-29. Широкі потоки: std::wcout, std::wcerr, std::wclog\n1 2 3 4 5 6 #include \u0026lt;iostream\u0026gt; int main() { std::wcout \u0026lt;\u0026lt; L\u0026#34;Hello World\\n\u0026#34;; } Ця трійця віддзеркалює cout/cerr/clog: wcout пише в stdout, wcerr/wclog - у stderr. Різниця у тому, що широкі потоки приймають wchar_t і конвертують його в char через codecvt локалі, щось типу use_facet\u0026lt;codecvt\u0026lt;...\u0026gt;\u0026gt;(getloc()). Для ASCII конвертація тривіальна. На виході той самий write(1, \u0026quot;Hello World\\n\u0026quot;, 12).\nВзагалі локалі - це одна з проблем C++, яка, серед іншого, особливо боляче вистрілила в регулярних виразах. Є мем, що для деяких регулярних виразів швидше запустити Python script, ніж чекати, поки відпрацює std::regex. Це черговий приклад порушення принципу zero-overhead.\n#30-33. Широкі потоки: нижчі точки входу (wcout.write тощо)\nУсе, що має cout (#12-#15), має й wcout, просто шаблонізоване на wchar_t. Наступні чотири методи відповідають #12-#15: wcout.write (#30), wcout.rdbuf()-\u0026gt;sputn (#31), wcout.put (#32), wcout.rdbuf()-\u0026gt;sputc (#33). Тут можна було б ще зарахувати wcerr/wclog і відповідні методи окремо, але я не буду.\n#34-39. Широкий C stdio: wprintf і компанія\n1 2 3 4 5 6 #include \u0026lt;cwchar\u0026gt; int main() { wprintf(L\u0026#34;Hello World\\n\u0026#34;); } Аналогічно до iostream, cstdio має власну шістку широких функцій (точніше, навпаки): wprintf (#34), fwprintf (#35), fputws (#36), putwchar (#37), putwc (#38), fputwc (#39). Усі конвертують wchar_t через wcrtomb локалі. Для ASCII на виході знову write(1, …, 12).\nНехай надрукує інший процес (стандартний C) #40. system()\n1 2 3 4 5 6 #include \u0026lt;cstdlib\u0026gt; int main() { return system(\u0026#34;echo Hello World\u0026#34;); } Це запускає echo, яке друкує \u0026ldquo;Hello World\u0026rdquo;. Так робити не треба, бо це довго і небезпечно через shell injection.\noutput як побічний ефект діагностики Тут \u0026ldquo;Hello World\u0026rdquo; опиняється в терміналі не тому, що ми його друкуємо, а тому, що бібліотека чи рантайм ним повідомляє про щось у stderr.\n#41. Непійманий throw\n1 2 3 4 5 6 #include \u0026lt;stdexcept\u0026gt; int main() { throw std::runtime_error(\u0026#34;Hello World\u0026#34;); } Виняток ніхто не ловить -\u0026gt; викликається std::terminate -\u0026gt; рантайм друкує what() у stderr і вбиває процес:\n1 2 terminate called after throwing an instance of \u0026#39;std::runtime_error\u0026#39; what(): Hello World Це вже хак, але технічно наш рядок опинився в терміналі. Точний текст цього повідомлення implementation-defined. Його видає стандартна бібліотека.\n#42. assert()\n1 2 3 4 5 6 #include \u0026lt;cassert\u0026gt; int main() { assert(false \u0026amp;\u0026amp; \u0026#34;Hello World\u0026#34;); } 1 a.out: hello.cpp:5: int main(): Assertion `false \u0026amp;\u0026amp; \u0026#34;Hello World\u0026#34;\u0026#39; failed. Працює, тільки поки не задефайнено NDEBUG. Інакше assert розкривається у ніщо і Hello World зникає.\n#43-45. contract_assert, pre, post - C++26 Contracts\n1 2 3 4 5 // build with -fcontracts int main() { contract_assert(false \u0026amp;\u0026amp; \u0026#34;Hello World\u0026#34;); } Три нові конструкції з Contracts в C++26. contract_assert - це еволюція assert.\n1 2 int f(int x) pre(false \u0026amp;\u0026amp; \u0026#34;Hello World\u0026#34;) { return x; } // #44 int g(int x) post(false \u0026amp;\u0026amp; \u0026#34;Hello World\u0026#34;) { return x; } // #45 Усі три проходять через обробник порушень контракту, а не через abort, і його поведінку можна перемикати прапором компілятора -fcontract-evaluation-semantic=[ignore|observe|enforce|quick_enforce]. Загалом система дуже гнучка і заслуговує на окремий топік, яких зараз безліч. GCC 16 (з -fcontracts) під дефолтним enforce друкує в stderr:\n1 2 3 contract violation in function int main() at hello.cpp:4: false \u0026amp;\u0026amp; \u0026#34;Hello World\u0026#34; [assertion_kind: assert, semantic: enforce, mode: predicate_false, terminating: yes] terminate called without an active exception Ключова відмінність між трьома - це поле assertion_kind: assert, pre чи post. Поки що Contracts вміє лише GCC. В стабільній версії Clang їх ще немає.\n#46. perror()\n1 2 3 4 5 6 7 8 #include \u0026lt;cerrno\u0026gt; #include \u0026lt;cstdio\u0026gt; int main() { errno = 0; perror(\u0026#34;Hello World\u0026#34;); } perror друкує в stderr рядок, двокрапку й опис поточного errno. Оскільки у прикладі errno обнулено, то опис буде \u0026ldquo;Success\u0026rdquo;:\n1 Hello World: Success Хак? Хак. Але що ви мені зробите)\nПроміжний підсумок: 46 способів. І це все стандартний C++26.\nБонус Хочеться ще розглянути методи, які не належать до стандарту, але також можуть використовуватись.\nPOSIX Код, що відповідає стандарту POSIX та працює на всіх системах, що його реалізують.\nПрямий I/O повз буфери #47. write()\n1 2 3 4 5 6 7 8 #include \u0026lt;string_view\u0026gt; #include \u0026lt;unistd.h\u0026gt; int main() { constexpr std::string_view msg = \u0026#34;Hello World\\n\u0026#34;; write(STDOUT_FILENO, msg.data(), msg.size()); } Це той самий write(2), до якого зводиться майже все інше в цій статті.\n#48. writev()\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include \u0026lt;iterator\u0026gt; #include \u0026lt;sys/uio.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; int main() { char hello[] = \u0026#34;Hello \u0026#34;; char world[] = \u0026#34;World\\n\u0026#34;; iovec iov[] = { { hello, sizeof(hello) - 1 }, { world, sizeof(world) - 1 }, }; writev(STDOUT_FILENO, iov, std::size(iov)); } Збирає дані з кількох окремих буферів в один системний виклик.\n#49. dprintf()\n1 2 3 4 5 6 7 #include \u0026lt;cstdio\u0026gt; #include \u0026lt;unistd.h\u0026gt; int main() { dprintf(STDOUT_FILENO, \u0026#34;Hello World\\n\u0026#34;); } printf для файлових дескрипторів. Форматує як printf, але пише напряму у fd, без FILE*.\nА тепер почесна згадка, яка не йде в залік. Є ще pwrite() - позиціонований запис за зміщенням:\n1 pwrite(STDOUT_FILENO, \u0026#34;Hello World\\n\u0026#34;, 12, 0); Проблема в тому, що pwrite вимагає seekable дескриптор. Якщо записувати у файл (./a.out \u0026gt; out.txt), то це спрацює. А якщо в термінал чи pipe, то буде ESPIPE (Illegal seek), і нічого не надрукується, тому цей спосіб не зараховується.\nЧерез файлову систему До дескриптора можна дотягнутися й через файлову систему, просто відкривши потрібний шлях.\n#50. open(\u0026quot;/dev/tty\u0026quot;) + write()\n1 2 3 4 5 6 7 8 9 10 11 #include \u0026lt;fcntl.h\u0026gt; #include \u0026lt;string_view\u0026gt; #include \u0026lt;unistd.h\u0026gt; int main() { constexpr std::string_view msg = \u0026#34;Hello World\\n\u0026#34;; int fd = open(\u0026#34;/dev/tty\u0026#34;, O_WRONLY); write(fd, msg.data(), msg.size()); close(fd); } /dev/tty - це контролюючий термінал процесу, незалежно від того, куди перенаправлено stdout. Запустіть ./a.out \u0026gt; /dev/null, і ви все одно побачите \u0026ldquo;Hello World\u0026rdquo; у терміналі, бо він пише повз перенаправлення. Цей спосіб вимагає наявності контролюючого термінала. Під час тесту в headless-середовищі без tty open повертає -1, тому я перевіряв його під справжнім псевдотерміналом.\nСпоріднена цікавинка, що не йде в перелік, бо вже й не працює: ioctl(fd, TIOCSTI, \u0026amp;c) додає символ не у output термінала, а в його чергу вводу. Сучасні ядра вимикають TIOCSTI за замовчуванням (CONFIG_LEGACY_TIOCSTI) і вимагають CAP_SYS_ADMIN.\nНехай надрукує інший процес (POSIX) #51. execlp()\n1 2 3 4 5 6 #include \u0026lt;unistd.h\u0026gt; int main() { execlp(\u0026#34;echo\u0026#34;, \u0026#34;echo\u0026#34;, \u0026#34;Hello World\u0026#34;, static_cast\u0026lt;char*\u0026gt;(nullptr)); } Це замінює процес на echo. Після вдалого exec \u0026ldquo;нашого\u0026rdquo; коду буквально більше не існує.\n#52. fork() + execvp()\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include \u0026lt;sys/wait.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; int main() { if (fork() == 0) { char arg0[] = \u0026#34;echo\u0026#34;; char arg1[] = \u0026#34;Hello World\u0026#34;; char* args[] = { arg0, arg1, nullptr }; execvp(\u0026#34;echo\u0026#34;, args); _exit(127); // only reached if exec failed } wait(nullptr); } Класичний Unix-патерн і принципова відмінність від попереднього: ми форкаємось, child стає echo, а parent залишається живим і чекає на завершення виконання child. Зверніть увагу на _exit(127) (не exit()) після exec. Якщо exec раптом зафейлиться, то child не має провалитися далі в parent логіку.\nВи можете мене спитати. Чому exec способів лише два, а не шість? Сімейство exec* (execl, execlp, execle, execv, execvp, execve) різниться тільки тим, як передаються аргументи. Усі вони зводяться до одного системного виклику execve. Тому я вирішив не нагліти тут і не рахувати види exec, а рахувати тільки патерн роботи з процесом: замінити себе (#51) чи форкнутись і пережити child (#52).\n#53. posix_spawn()\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include \u0026lt;spawn.h\u0026gt; #include \u0026lt;sys/wait.h\u0026gt; extern char** environ; int main() { char arg0[] = \u0026#34;/bin/echo\u0026#34;; char arg1[] = \u0026#34;Hello World\u0026#34;; char* args[] = { arg0, arg1, nullptr }; pid_t pid{}; posix_spawn(\u0026amp;pid, \u0026#34;/bin/echo\u0026#34;, nullptr, nullptr, args, environ); waitpid(pid, nullptr, 0); } Стандартизована альтернатива зв\u0026rsquo;язці fork + exec в одному виклику. На додачу, у випадку, коли fork дорогий, posix_spawn може бути ефективнішим, бо реалізований через легші примітиви (на Linux - через clone/vfork).\nЧому fork може бути дорогим? Інтуїтивно fork майже безкоштовний, через те, що він не копіює фізичну пам\u0026rsquo;ять, бо працює через copy-on-write (COW). Водночас його вартість залежить від розміру page tables: ядро мусить продублювати всі батьківські PTE, позначити кожну writable сторінку як read-only для COW і зробити TLB shootdown по всіх ядрах, на яких виконувався процес. Для процесу з великим адресним простором це вже відчутно. posix_spawn через vfork/clone(CLONE_VM|CLONE_VFORK) усього цього уникає: child позичає адресний простір parent, тож дублювати таблиці сторінок не треба.\nАсинхронний input-output #54. aio_write() - POSIX AIO\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 // link with -lrt #include \u0026lt;aio.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; int main() { static char msg[] = \u0026#34;Hello World\\n\u0026#34;; aiocb cb{}; cb.aio_fildes = STDOUT_FILENO; cb.aio_buf = msg; cb.aio_nbytes = sizeof(msg) - 1; aio_write(\u0026amp;cb); const aiocb* list[] = { \u0026amp;cb }; aio_suspend(list, 1, nullptr); // block until the write completes } Асинхронний input-output. Це ставить запис у чергу й чекає завершення через aio_suspend. На glibc POSIX AIO реалізований пулом helper-тредів, що роблять звичайний синхронний I/O: на не-seekable дескриптор (термінал, pipe) тред намагається зробити pwrite, що призводить до ESPIPE, а тоді робить fall-back на той самий write(1, \u0026quot;Hello World\\n\u0026quot;, 12).\n#55. lio_listio() - батч POSIX AIO\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 // link with -lrt #include \u0026lt;aio.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; int main() { static char msg[] = \u0026#34;Hello World\\n\u0026#34;; aiocb cb{}; cb.aio_fildes = STDOUT_FILENO; cb.aio_buf = msg; cb.aio_nbytes = sizeof(msg) - 1; cb.aio_lio_opcode = LIO_WRITE; aiocb* list[] = { \u0026amp;cb }; lio_listio(LIO_WAIT, list, 1, nullptr); } Аналогічно до попереднього методу, але замість однієї операції цей метод приймає цілий список aiocb і сабмітить його одним викликом. LIO_WAIT ще змушує цей потік заблокуватись, доки весь список не відпрацює.\nІ ще одна почесна згадка, яка не йде в залік: send().\n1 send(STDOUT_FILENO, \u0026#34;Hello World\\n\u0026#34;, 12, 0); send - це write для сокетів. Якщо ваш stdout - це сокет (наприклад, програму запустили з-під inetd чи socat), це спрацює. Я перевірив, підставивши сокет на дескриптор 1, і це спрацювало. Але в звичайному терміналі це призводить до ENOTSOCK. Тому як окремий спосіб - не зараховую.\n_unlocked - ті самі функції без внутрішнього локу Кожен виклик stdio за замовчуванням блокує FILE* заради потокобезпечності. Родина _unlocked блокування не робить, у цьому вся різниця. Це швидше, але треба гарантувати, що в цей потік ніхто інший не пише одночасно.\nputc_unlocked/putchar_unlocked - це частина POSIX. Решта (зокрема всі широкі) - це розширення glibc, але перелічу я все тут, бо, знову ж таки, що ви мені зробите.\n#56-60. Вузькі: putchar_unlocked (#56), putc_unlocked (#57), fputc_unlocked (#58), fputs_unlocked (#59), fwrite_unlocked (#60) - двійники #17/#18/#19/#5/#16 без локу.\n1 2 3 4 5 6 #include \u0026lt;cstdio\u0026gt; int main() { fputs_unlocked(\u0026#34;Hello World\\n\u0026#34;, stdout); } #61-64. Широкі (glibc): fputws_unlocked (#61), putwchar_unlocked (#62), putwc_unlocked (#63), fputwc_unlocked (#64) - двійники #36/#37/#38/#39 без локу.\n1 2 3 4 5 6 #include \u0026lt;cwchar\u0026gt; int main() { fputws_unlocked(L\u0026#34;Hello World\\n\u0026#34;, stdout); } Проміжний підсумок: 64 способи.\nРозширення POSIX - це не вся Unix-екосистема. Є ще купа розширень, яких немає в жодному стандарті.\n\u0026lt;err.h\u0026gt; (BSD) і \u0026lt;error.h\u0026gt; (GNU) BSD-родина \u0026lt;err.h\u0026gt; дає чотири такі функції, а GNU-розширення \u0026lt;error.h\u0026gt; дає ще дві.\n#65-68. err(), warn(), errx(), warnx() - BSD \u0026lt;err.h\u0026gt;\n1 2 3 4 5 6 #include \u0026lt;err.h\u0026gt; int main() { warnx(\u0026#34;Hello World\u0026#34;); // \u0026#34;\u0026lt;progname\u0026gt;: Hello World\u0026#34; to stderr } Четвірка різниться двома моментами: чи додавати : strerror(errno) (як perror) і чи виходити з програми. warn/err додають strerror, warnx/errx - ні. err/errx наприкінці викликають exit(), warn/warnx - ні.\n#69-70. error(), error_at_line() - GNU \u0026lt;error.h\u0026gt;\n1 2 3 4 5 6 #include \u0026lt;error.h\u0026gt; int main() { error(0, 0, \u0026#34;Hello World\u0026#34;); // \u0026#34;\u0026lt;progname\u0026gt;: Hello World\u0026#34; to stderr } error(status, errnum, …) за errnum != 0 додає strerror, за status != 0 виходить. error_at_line робить те саме, плюс додає префікс файл:рядок:.\nПроміжний підсумок: 70 способів.\nСуто Linux Через procfs stdout - це файл, і його можна відкрити за шляхом у файловій системі. На Linux /dev/stdout - це симлінк на /proc/self/fd/1. Сам POSIX цього шляху не стандартизує. На BSD, наприклад, /dev/stdout теж є, але через інший механізм.\n#71. std::ofstream(\u0026quot;/dev/stdout\u0026quot;)\n1 2 3 4 5 6 #include \u0026lt;fstream\u0026gt; int main() { std::ofstream(\u0026#34;/dev/stdout\u0026#34;) \u0026lt;\u0026lt; \u0026#34;Hello World\\n\u0026#34;; } Те саме можна зробити мовою C через fopen(\u0026quot;/dev/stdout\u0026quot;, \u0026quot;w\u0026quot;) + fprintf або через інші шляхи до того ж дескриптора: /dev/fd/1 чи /proc/self/fd/1. Я зараховую це як 1 метод \u0026ldquo;відкрити fd через файлову систему\u0026rdquo;.\nsyscall #72. syscall(SYS_write, …)\n1 2 3 4 5 6 7 8 9 #include \u0026lt;string_view\u0026gt; #include \u0026lt;sys/syscall.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; int main() { constexpr std::string_view msg = \u0026#34;Hello World\\n\u0026#34;; syscall(SYS_write, 1, msg.data(), msg.size()); } Це обходить навіть libc обгортку write() і викликає системний виклик за його номером.\n#73. Inline assembly - x86-64\n1 2 3 4 5 6 7 8 9 10 11 12 13 int main() { const char msg[] = \u0026#34;Hello World\\n\u0026#34;; asm volatile( \u0026#34;mov $1, %%rax\\n\u0026#34; // syscall number: write \u0026#34;mov $1, %%rdi\\n\u0026#34; // fd: stdout \u0026#34;mov %0, %%rsi\\n\u0026#34; // buf: msg \u0026#34;mov %1, %%rdx\\n\u0026#34; // count: msg length, without the \u0026#39;\\0\u0026#39; \u0026#34;syscall\u0026#34; : : \u0026#34;r\u0026#34;(msg), \u0026#34;i\u0026#34;(sizeof(msg) - 1) : \u0026#34;rax\u0026#34;, \u0026#34;rdi\u0026#34;, \u0026#34;rsi\u0026#34;, \u0026#34;rdx\u0026#34;, \u0026#34;rcx\u0026#34;, \u0026#34;r11\u0026#34;, \u0026#34;memory\u0026#34;); } Найнижчий рівень, доступний з C++: сама інструкція syscall. rcx і r11 у списку clobber-ів не випадкові: інструкція syscall затирає їх, зберігаючи в них RIP і RFLAGS відповідно. memory у clobber-ах каже компілятору не тримати значення пам\u0026rsquo;яті в регістрах через межу asm.\nПеренесення даних силами ядра Наступні три способи цікаві тим, що дані рухаються до stdout усередині ядра, майже не торкаючись нашого userspace, а фінальний output робить не write, а власний системний виклик.\n#74. sendfile() з memfd_create()\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include \u0026lt;string_view\u0026gt; #include \u0026lt;sys/mman.h\u0026gt; #include \u0026lt;sys/sendfile.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; int main() { constexpr std::string_view msg = \u0026#34;Hello World\\n\u0026#34;; constexpr auto len = msg.size(); int fd = memfd_create(\u0026#34;hello\u0026#34;, 0); // \u0026#34;hello\u0026#34; is just a debug label, not output write(fd, msg.data(), len); lseek(fd, 0, SEEK_SET); sendfile(STDOUT_FILENO, fd, nullptr, len); close(fd); } memfd_create робить анонімний файл з ім\u0026rsquo;ям \u0026ldquo;hello\u0026rdquo;, що живе в RAM і видимий в /proc/self/fd. write заповнює цей файл, а тоді sendfile копіює дані з нього в stdout в kernel-space. Для більшого приколу, цей memfd можна заповнити не через write, а через mmap + memcpy.\n#75. splice() через pipe\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include \u0026lt;fcntl.h\u0026gt; #include \u0026lt;string_view\u0026gt; #include \u0026lt;unistd.h\u0026gt; int main() { constexpr std::string_view msg = \u0026#34;Hello World\\n\u0026#34;; constexpr auto len = msg.size(); int pfd[2]{}; pipe(pfd); write(pfd[1], msg.data(), len); splice(pfd[0], nullptr, STDOUT_FILENO, nullptr, len, 0); close(pfd[0]); close(pfd[1]); } splice переміщує дані між дескрипторами через ядро, без копіювання в userspace. Один з дескрипторів обов\u0026rsquo;язково має бути pipe. Output у stdout тут робить сам системний виклик splice. Є схожі методи типу vmsplice (мапить сторінки userspace в pipe) і tee (дублює дані між двома pipe).\nЩе є copy_file_range, який теж копіює дані між двома дескрипторами, але обидва дескриптори мусять бути звичайними файлами. У термінал чи pipe цей метод копіювати не вміє.\n#76. io_uring\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 // link with -luring #include \u0026lt;liburing.h\u0026gt; #include \u0026lt;string_view\u0026gt; #include \u0026lt;unistd.h\u0026gt; int main() { constexpr std::string_view msg = \u0026#34;Hello World\\n\u0026#34;; io_uring ring{}; io_uring_queue_init(1, \u0026amp;ring, 0); io_uring_sqe* sqe = io_uring_get_sqe(\u0026amp;ring); io_uring_prep_write(sqe, STDOUT_FILENO, msg.data(), msg.size(), 0); io_uring_submit(\u0026amp;ring); io_uring_cqe* cqe = nullptr; io_uring_wait_cqe(\u0026amp;ring, \u0026amp;cqe); io_uring_cqe_seen(\u0026amp;ring, cqe); io_uring_queue_exit(\u0026amp;ring); } Найсучасніший Linux I/O API. Submission queue, completion queue, buffer ring, спільні між ядром і userspace - усе це придумано, щоб максимально ефективно робити велику кількість I/O операцій. Для \u0026ldquo;Hello World\u0026rdquo;, як бачимо, воно також підходить. Тут немає синхронного write взагалі. I/O ядро виконує з нашого SQE, а ми лише сабмітимо й чекаємо завершення. Це чудово видно в strace: жодного write(1, …) там немає, натомість лише io_uring_setup і io_uring_enter, усередині якого ядро саме й робить запис:\n1 2 3 $ strace -e io_uring_setup,io_uring_enter,write ./io-uring io_uring_setup(1, {...}) = 3 io_uring_enter(3, 1, 0, 0, NULL, 8) = 1 # SQE submitted; the write happens in-kernel Підсумок рантайму: 76 способів.\nCompile-time - програма навіть не запускається У цьому розділі розглянемо, як змусити \u0026ldquo;Hello World\u0026rdquo; з\u0026rsquo;явитися під час компіляції, а не виконання.\nСтандартний C++ #77. static_assert - C++11\n1 static_assert(false, \u0026#34;Hello World\u0026#34;); 1 error: static assertion failed: Hello World Найбільш прямий спосіб змусити компілятор надрукувати те, що ти хочеш. Також це можна відкласти до інстанціації шаблону через value-dependent вираз:\n1 2 3 4 5 6 7 template \u0026lt;int N\u0026gt; struct HelloWorld { static_assert(N != N, \u0026#34;Hello World\u0026#34;); }; template struct HelloWorld\u0026lt;42\u0026gt;; N != N залежить від параметра шаблону, тому перевірку відкладено до інстанціації. Сучасні GCC/Clang завдяки CWG2518 уже не падають і на незалежному static_assert(false).\n#78. [[deprecated]] - C++14\n1 2 3 4 [[deprecated(\u0026#34;Hello World\u0026#34;)]] void f() {} int main() { f(); } Компіляція проходить, але з попередженням:\n1 warning: \u0026#39;void f()\u0026#39; is deprecated: Hello World [-Wdeprecated-declarations] #79. [[nodiscard(\u0026quot;…\u0026quot;)]] - C++20\n1 2 3 4 [[nodiscard(\u0026#34;Hello World\u0026#34;)]] int f() { return 0; } int main() { f(); } Компіляція проходить, але якщо проігнорувати значення, що повертається (а ми саме це й робимо), то буде попередження:\n1 warning: ignoring return value of \u0026#39;int f()\u0026#39;, declared with attribute \u0026#39;nodiscard\u0026#39;: \u0026#39;Hello World\u0026#39; [-Wunused-result] Можливість додавати причину для [[nodiscard]] додали в C++20, сам [[nodiscard]] в C++17.\n#80. = delete(\u0026quot;…\u0026quot;) - C++26\n1 2 3 void f() = delete(\u0026#34;Hello World\u0026#34;); int main() { f(); } 1 error: use of deleted function \u0026#39;void f()\u0026#39;: Hello World Можливість указати причину видалення функції прийняли аж у C++26, GCC 16 це вже підтримує.\n#81. throw під час компіляції - C++26\nУ C++26 кидати винятки можна вже під час компіляції, і якщо виняток виходить за межі constexpr-виразу, то компілятор зобов\u0026rsquo;язаний це продіагностувати. GCC 16 при цьому виводить what() просто в текст помилки:\n1 2 3 4 5 #include \u0026lt;stdexcept\u0026gt; constexpr int hello() { throw std::runtime_error(\u0026#34;Hello World\u0026#34;); } constexpr int x = hello(); // forces constant evaluation -\u0026gt; the throw escapes 1 error: uncaught exception of type \u0026#39;std::runtime_error\u0026#39;; \u0026#39;what()\u0026#39;: \u0026#39;Hello World\u0026#39; Фактично, компілятори вже вміють виконувати велику частину C++ коду під час компіляції. Невеличке застереження: це поки що вміє лише GCC. Clang 22 ще не реалізував кидання винятків у константних обчисленнях (P3068). Він просто відкидає throw як неконстантний вираз, не доходячи до what().\n#82. #warning - C++23\n1 #warning \u0026#34;Hello World\u0026#34; 1 warning: #warning \u0026#34;Hello World\u0026#34; [-Wcpp] До C++23 це було розширенням GCC і Clang; тепер це стандарт (P2437R1).\n#83. #error - C++98\n1 #error \u0026#34;Hello World\u0026#34; 1 error: #error \u0026#34;Hello World\u0026#34; Стандартна директива препроцесора з C++98.\n#84. #include \u0026quot;Hello World\u0026quot;\n1 2 #include \u0026#34;Hello World\u0026#34; int main() {} 1 fatal error: Hello World: No such file or directory Ще один output як побічний ефект діагностики, тільки тепер від препроцесора. Препроцесор шукає файл із таким іменем, не знаходить і падає з фатальною помилкою. Ім\u0026rsquo;я в лапках може містити пробіл, тож \u0026quot;Hello World\u0026quot; - це цілком легальний хедер. Теж трохи хак, але що поробиш)\nКомпілятор-специфічні #85. #pragma message\n1 #pragma message(\u0026#34;Hello World\u0026#34;) 1 note: \u0026#39;#pragma message: Hello World\u0026#39; На відміну від #warning і #error, #pragma message у стандарті немає. Це розширення, яке підтримують GCC, Clang і MSVC.\n#86. __attribute__((warning(...))) - лише GCC\n1 2 3 4 __attribute__((warning(\u0026#34;Hello World\u0026#34;))) void f() {} int main() { f(); } 1 warning: call to \u0026#39;f\u0026#39; declared with attribute warning: Hello World [-Wattribute-warning] Тут є цікавий технічний нюанс, на який я натрапив під час перевірки. Цей атрибут спрацьовує, тільки якщо виклик f() доживає до пізніх стадій компіляції. На -O0 усе гаразд, попередження є. А на -O2 компілятор інлайнить порожню f() і викидає виклик ще до того, як атрибут встигне спрацювати, тому попередження зникає. Тобто наявність Hello World залежить від рівня оптимізації.\n#87. __attribute__((error(...)))\n1 2 3 4 __attribute__((error(\u0026#34;Hello World\u0026#34;))) void f(); int main() { f(); } 1 error: call to \u0026#39;f\u0026#39; declared with attribute error: Hello World Аналогічно до warning, але якщо виклик доживає до кодогенерації, то компіляція падає з нашим повідомленням. На відміну від #86, я тут залишив f() без тіла, бо без LTO невизначену функцію неможливо заінлайнити, тому виклик гарантовано доживає, і помилка спрацьовує на будь-якому рівні оптимізації.\n#88. __attribute__((unavailable(\u0026quot;…\u0026quot;)))\n1 2 3 4 __attribute__((unavailable(\u0026#34;Hello World\u0026#34;))) void f(); int main() { f(); } 1 error: \u0026#39;void f()\u0026#39; is unavailable: Hello World unavailable спрацьовує на рівні семантичного аналізу, тобто на будь-яке використання імені, тому не залежить від оптимізації.\n#89. __attribute__((diagnose_if(…))) - лише Clang\n1 2 3 4 __attribute__((diagnose_if(1, \u0026#34;Hello World\u0026#34;, \u0026#34;warning\u0026#34;))) void f() {} int main() { f(); } 1 warning: Hello World [-Wuser-defined-warnings] Clang дозволяє повісити на функцію умовну діагностику з власним текстом. GCC просто ігнорує атрибут (warning: 'diagnose_if' attribute directive ignored).\nАсемблерні директиви #90. asm(\u0026quot;.error …\u0026quot;)\n1 2 asm(\u0026#34;.error \\\u0026#34;Hello World\\\u0026#34;\u0026#34;); int main() {} 1 Error: Hello World Рядок друкує вже не компілятор, а GNU as, коли натрапляє на директиву .error. Clang з інтегрованим асемблером має аналогічну поведінку: error: Hello World.\n#91. asm(\u0026quot;.warning …\u0026quot;)\n1 2 asm(\u0026#34;.warning \\\u0026#34;Hello World\\\u0026#34;\u0026#34;); int main() {} 1 Warning: Hello World Те саме, але рівень warning: об\u0026rsquo;єктний файл усе одно збереться, асемблер лише попередить.\n#92. asm(\u0026quot;.print …\u0026quot;)\n1 2 asm(\u0026#34;.print \\\u0026#34;Hello World\\\u0026#34;\u0026#34;); int main() {} 1 Hello World Асемблер, на відміну від решти цієї секції, друкує рядок у stdout, а не у stderr.\nПідсумок compile-time: 16 способів.\nФінал: усі дороги ведуть до write(2) Загальний підсумок: 92 способи надрукувати \u0026ldquo;Hello World\\n\u0026rdquo; у консоль у C++ на Linux. З них 54 - стандартний C++.\nКатегорія Кількість Стандартний C++26 (рантайм) 46 POSIX (+ glibc unlocked) 18 Розширення (BSD/glibc) 6 Суто Linux 6 Compile-time (стандартний C++) 8 Compile-time (нестандартні) 8 Всього 92 Врешті, майже всі рантайм методи зводяться до одного системного виклику write(2). І лише чотири мають власний системний виклик: writev, sendfile, splice та io_uring.\nСкільки буферів між тобою і ядром Окрема тема, навколо якої багато плутанини: скільки буферів стоїть між викликом і ядром:\nСпосіб Буферизація Коли реально йде write(2) write, writev, dprintf, syscall, asm, /dev/tty немає одразу, на кожен виклик C stdio: printf, fprintf, puts, fputs, fwrite, putchar, putc, fputc, print, println (і _unlocked-двійники) буфер stdout (FILE*) у термінал - на кожен \\n; у файл/pipe - коли буфер повний або при виході iostream: cout \u0026lt;\u0026lt;, .write, .put, sputn, sputc, STL-ітератори у дефолті - той самий буфер stdout; з sync_with_stdio(false) - власний так само, плюс явний flush / endl Тобто прямі способи пишуть у ядро одразу, буферизовані флашаться або на \\n (у термінал), або коли буфер заповниться, або під час нормального виходу з програми (exit флашить всі stdio-буфери й викликає деструктори статичних cout).\nЩе хочеться розказати про нюанс з cerr і clog (#24 і #25). Прийнято вважати, що cerr небуферизований, а clog буферизований.\nstd::cerr має виставлений прапор unitbuf, тому він флашиться після кожної операції виводу. std::clog цього прапора не має. Здавалося б, clog мав би накопичувати output, але по дефолту (sync_with_stdio(true)) обидва потоки пишуть у C-шний stderr, а він сам по собі небуферизований. Тому на POSIX платформах насправді обидва пишуть одразу. Я перевірив через strace (рядок \u0026quot;Hello\u0026quot; \u0026lt;\u0026lt; \u0026quot; \u0026quot; \u0026lt;\u0026lt; \u0026quot;World\u0026quot; \u0026lt;\u0026lt; \u0026quot;\\n\u0026quot; - це 4 операції):\n1 2 strace -e write ./cerr -\u0026gt; 4 separate write(2, …) strace -e write ./clog -\u0026gt; 4 separate write(2, …) Різниця з\u0026rsquo;являється, тільки якщо від\u0026rsquo;єднати iostream від stdio:\n1 2 3 std::ios_base::sync_with_stdio(false); std::clog \u0026lt;\u0026lt; \u0026#34;Hello\u0026#34; \u0026lt;\u0026lt; \u0026#34; \u0026#34; \u0026lt;\u0026lt; \u0026#34;World\u0026#34; \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // now 1 write(2, \u0026#34;Hello World\\n\u0026#34;, 12) std::cerr \u0026lt;\u0026lt; \u0026#34;Hello\u0026#34; \u0026lt;\u0026lt; \u0026#34; \u0026#34; \u0026lt;\u0026lt; \u0026#34;World\u0026#34; \u0026lt;\u0026lt; \u0026#34;\\n\u0026#34;; // still 4 - unitbuf flushes every time Ось тепер clog справді складає все в буфер і флашить одним write наприкінці, а cerr через unitbuf усе одно флашить на кожній операції.\nВрешті, якщо запустити способи, що базуються на write, то strace -e write всюди покаже однаковий результат з точністю до дескриптора:\n1 2 3 4 5 strace -e write ./cout -\u0026gt; write(1, \u0026#34;Hello World\\n\u0026#34;, 12) strace -e write ./printf -\u0026gt; write(1, \u0026#34;Hello World\\n\u0026#34;, 12) strace -e write ./write -\u0026gt; write(1, \u0026#34;Hello World\\n\u0026#34;, 12) strace -e write ./syscall -\u0026gt; write(1, \u0026#34;Hello World\\n\u0026#34;, 12) strace -e write ./cerr -\u0026gt; write(2, \u0026#34;Hello World\\n\u0026#34;, 12) Навіть непійманий throw (#41) врешті просто пише в stderr: (write(2, \u0026quot;terminate called…\u0026quot;, 48), далі write(2, \u0026quot;Hello World\u0026quot;, 11)).\nОтакі справи, малята. Тому я не розумію людей, які кажуть, що C++ роздутий. Все дуже просто і лаконічно.\n","permalink":"https://niksol15.github.io/blog/uk/posts/cpp-hello-world/","summary":"\u003cp\u003eЧи бувало у вас таке, що в неділю ввечері вас починає мучити питання: \u0026ldquo;А скільки ж все-таки способів надрукувати \u003ccode\u003e\u0026quot;Hello World\u0026quot;\u003c/code\u003e у консоль в C++?\u0026rdquo;.\nСподіваюсь, ні, бо це вже як мінімум профдеформація. Але я задався таким питанням, і зрозумів, що немає жодного джерела,\nяке відповідає на нього. Тому я вирішив зробити таке джерело сам.\u003c/p\u003e\n\u003cp\u003eОскільки C++ - це тепер не просто мова програмування, а ще й мова метапрограмування, розглянемо два окремі випадки:\nдрук \u003ccode\u003e\u0026quot;Hello World\u0026quot;\u003c/code\u003e в рантаймі та на етапі компіляції.\u003c/p\u003e","title":"Всі (92) способи надрукувати \"Hello World\" у C++26"},{"content":"Частина 1: Огляд програми | Частина 2: Досвід вступу\nВітаю, товариство! У цьому пості я розкажу про те, як загалом влаштовано навчання, і про свій перший курс - Advanced Operating Systems. Я великий прокрастинатор, тому матеріалу накопичилось достатньо багато. Спершу я хотів у цьому пості розказати про всі предмети, які встиг узяти, але зрозумів, що це занадто за обсягом, тому ще про 3 предмети розповім у наступному дописі.\nОрганізація навчання Онбординг після зарахування Усіх вступників автоматично зараховують на міні-курс, де директор програми Dr. Joyner коротко розповідає що до чого: які очікування від студентів, як проходить навчання, що важливо знати на старті. Тобто, у разі вступу, ви точно розумітимете, що від вас очікується.\nЗарахування на курси і Time Ticket Далі настає в чомусь найскладніша частина - зарахування на курси.\nСтуденти OMSCS використовують ту саму систему, що й усі інші студенти Georgia Tech, але для OMSCS доступні не всі курси. Студент може обрати курс для зарахування тільки після настання часу, записаного в його Time Ticket. Саме зарахування розбите на фази, усього їх 2. Вступники можуть реєструватися тільки в другій фазі, коли більшість місць уже зайнята. Час у Time Ticket доволі зручний у нашому часовому поясі, тому не треба буде вставати посеред ночі, щоб зареєструватися.\nЦі Time Tickets видаються автоматично, а час у них залежить від того, скільки курсів студент уже пройшов. Чим довше навчаєшся - тим більше шансів потрапити на курс, якщо не провтикав настання часу в Time Ticket.\nМісця, waitlist і FFF У більшості курсів місця обмежені, але є кілька курсів з безлімітною кількістю місць, тому ситуація, що ви взагалі залишились без можливості зарахуватись на будь-що, практично неможлива.\nЧастина місць резервується під waitlist. Якщо ви не змогли зареєструватись на курс одразу, у вас є можливість додатись у цей список у порядку черги. Першій людині у списку дають обмежений час скористатись можливістю зарахуватись на курс. Якщо людина не скористалась - можливість передається далі по списку.\nПеред початком навчання є \u0026ldquo;Free For all Friday\u0026rdquo;, яку тепер називають \u0026ldquo;Free For all !Friday\u0026rdquo;, бо проводиться в четвер. Під час FFAF спадають усі резервації, і тоді часто можна \u0026ldquo;залетіти\u0026rdquo; на курс в обхід черги у waitlist.\nЧерез таку схему цілком можливий варіант, що ви не потрапите в першому семестрі на курс, який ви хочете, бо всі місця будуть зайняті. Треба бути до цього готовим і мати план Б.\nОкремо є курс, на який майже нереально потрапити, якщо це не один з ваших останніх семестрів. Це Intro to Graduate Algorithms. Так стається, тому що цей курс обов\u0026rsquo;язковий для випуску, і майже всі місця в ньому зарезервовані для \u0026ldquo;випускників\u0026rdquo;. Хоча я чув історії, як через FFAF туди потрапляли люди у свій перший семестр.\nCanvas, Ed Discussion, лекції і завдання Трохи про організацію навчального процесу.\nВас додають на курс у Canvas і на форум в Ed Discussion. Лекції часто доступні всі одразу і знаходяться в Canvas та/або в Ed. Також завжди є окремий список для читання із рекомендованих статей за темою.\nВ Ed Discussion відбувається основне спілкування викладачів і студентів та колаборація між студентами. Я багато разів бачив, як студенти постили якісь питання, на які отримували пояснення як від Teaching Assistants, так і від інших студентів. Якщо ви любите спілкуватись наживо, щотижня проводяться office hours, на яких можна поспілкуватись з професором і TA. Вони не завжди проводяться в зручний час, але вони записуються, тому їх можна подивитись потім. Якщо бути чесним, то я на це забив і ніколи на них не ходив, а переглядав тільки перший у першому семестрі. Суть у тому, що ситуація, коли ви повністю загубились і немає звідки отримати допомогу, дуже малоймовірна. За бажання та інвестиції часу можна отримати з курсу набагато більше, ніж просто переглянуті лекції.\nНа початку семестру завжди є пост, у якому всі представляються і знайомляться - це гарний спосіб збільшити собі кількість connections з усього світу в LinkedIn. З мого досвіду більшість студентів з англомовних країн, здебільшого США (оце так несподіванка).\nДоступність лаб/завдань залежить від курсу, але зазвичай вони відкриваються поступово протягом семестру.\nКількість екзаменів залежить від курсу, але часто їх двоє: midterm і final.\nДо дедлайнів ставлення суворе: хтось взагалі не приймає завдання після дедлайну, а хтось дає коротке \u0026ldquo;розширення\u0026rdquo; з можливим штрафом по балах.\nДесь трохи раніше середини семестру настає withdrawal deadline. До цього часу можна дропнутись з курсу, і в транскрипті навіть не буде запису, що ви цей курс брали. Після цієї дати у випадку покидання курсу в транскрипті стоятиме W.\nАкадемічна доброчесність Незвичним стало те, що тут ДУЖЕ відповідальне ставлення до академічної доброчесності. Про це постійно наголошують і за цим реально слідкують.\nЯ був свідком ситуацій, коли людей ловили на тому, що вони брали лаби з інтернету або ж генерували їх ллмкою. З LLM проблема не в тому, що було видно, що це згенерований код, а в тому, що LLM генерував код на основі доступного в інтернеті, і ловили саме за плагіат. Санкції виглядають приблизно так:\n1 порушення: 0 за завдання/екзамен, неможливість отримати A за курс і неможливість дропнутись з курсу 2 порушення: відрахування з курсу 3 порушення: відрахування з програми Може здатися дивним: чому неможливість дропнути курс - це взагалі покарання? Логіка в тому, що так студент не може просто вийти з курсу одразу після санкції й \u0026ldquo;обнулити\u0026rdquo; наслідки. Інакше перше порушення майже нічого б не коштувало, максимум довелося б узяти ще один семестр, щоб надолужити.\nТакий підхід має свої плюси і мінуси. Очевидний плюс у тому, що підвищується цінність диплому. Бо більшість студентів дійсно пройшли програму, а не зчітирили собі шлях у ній. А ось мінус у тому, що одне і те ж завдання, яке може бути доволі обмежене і специфіковане, дається великій кількості людей протягом декількох років. Я люблю себе накручувати, а накручування тут в тому, що, як я розумію, завжди існує шанс, що в когось перетнеться з тобою хід думок і стиль вирішення проблеми, тому є шанс false positive потрапляння на плагіат. На щастя, такого зі мною не було, але якщо зайти на сабредіт, можна побачити купу постів, де люди божаться, що їх безпідставно звинуватили в плагіаті.\nНезважаючи на вищесказане, ситуація не настільки погана, бо завжди є процес розгляду, де дають студентам довести свою невинуватість.\nЯк проходять екзамени (прокторинг) Екзамени організовані схожим чином з IELTS.\nЗазвичай на проходження екзамену дається тиждень або, наприклад, п\u0026rsquo;ятниця + вихідні. Тобто можна обрати зручний для себе час. Екзамен треба проходити в тихій кімнаті наодинці.\nУ випадку closed-notes перед екзаменом треба просканувати кімнату, щоб показати, що ти дійсно там один, і що в кімнаті немає нічого, що може допомогти. Також треба просканувати свій паспорт, щоб за вас не могла написати роботу інша людина.\nПід час екзамену записується звук з мікрофона, відео з камери та екран. У прямому ефірі за вами зазвичай ніхто не дивиться, але система відмічає підозрілі моменти, які потім може переглянути хтось із викладачів. Сам екзамен може тривати 2.5 години, що є неймовірним челенджем для любителів коротких смішних відосів в інтернеті.\nСаме навчання Fall 2024 - Advanced Operating Systems (AOS) Мені пощастило зарахуватись на курс, який я хотів. Я обрав Advanced Operating Systems, тому що:\nМені хочеться вірити, що я шарю в ОС, і це легкий спосіб втягнутись у навчання через тему, в якій я вже розбираюсь. Зазвичай багато людей беруть цей курс після Graduate Introduction to Operating Systems, тому відносно мало фрешменів конкурує за місця в AOS. Можна подумати, що цей предмет про ОС, але це тільки частково правда) Я б його назвав Introduction to Distributed Systems. Ось що взагалі викладається в курсі:\nКурс починається з короткого рефрешеру про базові речі - що таке ОС, як працює віртуальна пам\u0026rsquo;ять, кеш, багатопоточність і так далі. Загалом усе, що викладається на ОС під час бакалаврату. Після цього йшла домашня робота, де треба було відповісти на декілька питань, і простенька лаба про багатопоточність на C, щоб люди розуміли, чи взагалі готові вони проходити цей курс. Питання здебільшого базові, типу:\nExplain all the actions from the time a process incurs a page fault to the time it resumes execution. Assume that this is the only runnable process in the entire system.\nале були й цікаві, наприклад:\nExplain page coloring and how it may be used in memory management by an operating system.\nПоняття page coloring було для мене новим. Виявилось, що єдине більш-менш живе ядро, яке його використовує - це FreeBSD.\nДалі йде перша серйозна тема - OS Structures. Тут розглядають три підходи до структури ядра: SPIN, Exokernel і L3 Microkernel. Загалом доволі цікаво, особливо враховуючи, що microkernel архітектура ядра досі жива і використовується в QNX, що є одним з основних виборів за потреби в RTOS. Ще згадувалась дуже повчальна історія про вплив оптимізації на софт: до якогось моменту вважалося, що мікроядра повільні, бо в Mach від Carnegie Mellon border crossing займав десь 900 циклів процесора, але в якийсь момент за справу взявся дядько Лідтке і оптимізував це в своїй ОС L3 до 123 циклів. Хоча здавалося б, Mach писали далеко не дурні люди. Після цього мікроядра повільними вже не вважали)\nНаступна тема - Virtualization. Тут розповідається про CPU, Memory і Device Virtualization. Порівнюються Full Virtualization (VMware) та Paravirtualization (Xen). Цьому модулю відповідає перша лаба на С з використанням libvirt. Вона складається з 2 частин: vCPU scheduler і memory coordinator для guest OSes. Мені лаба дуже сподобалась, бо немає якогось явного кращого детерміністичного рішення. Треба придумати евристику, щоб рішення працювало достатньо добре в декількох різних сценаріях.\nПісля цього Parallel Systems - найбільший модуль курсу. Тут дуже багато чого. Починається з Symmetric Multiprocessor (SMP) і того, як імплементовано memory consistency та cache coherence. Потім про різні можливі способи імплементації спінлоків і бар\u0026rsquo;єрів на UMA і NUMA. Наступною підтемою йде імплементація і оптимізація Remote Procedure Call (RPC), після чого викладаються різні підходи до scheduling. Ця велика тема закінчується обговоренням того, як можна оптимізувати ОС конкретно для Shared Memory Multiprocessor з великою кількістю CPU на прикладі Tornado. Основна ідея в тому, щоб мінімізувати кількість глобальних об\u0026rsquo;єктів у ядрі, захищених м\u0026rsquo;ютексом, замінивши їх локальними репліками.\nДалі йде Distributed Systems. Тут треба сказати про дядька Лампорта. Це людина, яка придумала LaTeX, Paxos, TLA+ і отримала премію Тюрінга в 2013 році за внесок у distributed computing. Його стаття \u0026ldquo;Time, Clocks, and the Ordering of Events in a Distributed System\u0026rdquo; з 1978 року - це основа всього модуля. Головна ідея: у розподіленій системі немає глобального часу, тому потрібен інший спосіб визначити, що сталося раніше, а що пізніше. Звідси happened-before relationship і logical clocks. Це все існує для того, щоб можна було досягти розподіленого консенсусу. Бонусом ще розказується про distributed mutual exclusion. Fun fact: в алгоритмі Лампорта для цього треба мінімум 3(N-1) повідомлень на 1 lock, де N - це кількість процесів. Після цього розповідається про Active Networks, що є концепцією з 90-х, де роутери не просто пересилають пакети, а можуть виконувати код. 90-ті були давно, але Active Networks можна вважати одним з концептуальних попередників SDN, хоча архітектурно вони суттєво відрізняються. Взагалі зараз майже все Software Defined, навіть машини.\nЦим двом темам відповідає друга лаба. У ній треба імплементувати декілька SMP бар\u0026rsquo;єрів на OpenMP і декілька Distributed Memory бар\u0026rsquo;єрів на MPI. Усе це треба додатково заміряти і написати звіт з поясненням результатів. Крутість цієї лаби полягає в тому, що вам дають доступ до PACE - кластера GaTech, де ви через SLURM запускаєте джоби одразу на багатьох машинах усередині кластера.\nПотім трохи археології під назвою Distributed Objects and Middleware: Spring OS (не плутати зі Spring Framework), Java RMI, Enterprise JavaBeans. Не дуже корисний модуль, як на мене, бо це все вже мертве.\nПісля цього Distributed Subsystems - застосування distributed systems. Тут 3 сабмодулі. Перший про Global Memory Systems. Якщо коротко, ідея в тому, щоб робити swap не в локальному накопичувачі, а в RAM іншої машини, з\u0026rsquo;єднаної по LAN. Як можна зрозуміти, це було придумано задовго до SSD. Можна подумати, що це давно мертва ідея, але я загуглив, і ця ідея лягла в основу disaggregated memory, що зараз використовується в HPC кластерах. Наступний сабмодуль називається Distributed Shared Memory, про те, як можна неявно для програми представити фізичну пам\u0026rsquo;ять на різних нодах як один логічний адресний простір, приховуючи явну комунікацію. Плюси, я думаю, очевидні. З мінусів - це те, можна на зверненні до адреси зависнути, чекаючи на пам\u0026rsquo;ять з іншого кінця кластера. Ну і останній сабмодуль про імплементацію distributed filesystem.\nДесь тут треба було зробити третю лабу. У ній треба імплементувати щось на кшталт магазину на gRPC з тредпулом. Загалом лаба цікава і фактично є підготовкою до останньої лаби. Ще з цікавого: на момент, як я її писав, не підтримувались останні версії gRPC, тому асинхронну комунікацію не можна було імплементувати через колбеки, через це довелося використовувати CompletionQueue. І підхід тут дуже цікавий. У кожної операції є void* тег. Так ось у цей тег у більшості випадків треба передавати якийсь динамічно створений через new об\u0026rsquo;єкт, щоб у наступних операціях звернутись до нього по тегу як по вказівнику, бо в ньому зазвичай треба зберігати якусь асоційовану з цим ланцюгом викликів інформацію. Виходить така собі state machine з ручним керуванням пам\u0026rsquo;яттю.\nДалі йде Failures and Recovery: LRVM, RioVista, QuickSilver. Цей модуль про те, як робити транзакції і recovery віртуальної пам\u0026rsquo;яті на рівні ОС з відносно малим оверхедом.\nПісля цього Internet Scale Computing, який розбитий на 3 сабмодулі. Перший загальний про те, як менеджити ресурси великого сервісу і про replication vs partitioning. Другий модуль про легендарний гуглівський MapReduce. Я не знаю, що тут додати, тому якщо не знаєте, що це, то краще погуглити. Третій сабмодуль про Content Delivery Networks (CDN) на Coral DHT. Це теж дуже прикольна і досі актуальна технологія, яку активно використовують, наприклад, стримінгові сервіси.\nЦьому модулю відповідає остання четверта лаба. У ній треба імплементувати спрощену версію MapReduce на основі gRPC. Спрощення полягає в тому, що workers запущені на одному хості і можуть юзати спільну файлову систему. Оригінальна імплементація MapReduce використовує Google File System (GFS), що сама собою є гарним екземпляром програмної інженерії.\nПотім коротке повернення до операційних систем під назвою Real-Time and Multimedia. У цьому модулі розглядається TS-Linux та Persistent Temporal Streams, але фактично більшість теми зводиться до ідей імплементації real-time scheduling.\nІ наостанок трохи Security - Saltzer and Schroeder\u0026rsquo;s design principles і Andrew File System.\nНа цьому з лекціями все. Пару слів про тести. Курс має 3 closed-notes тести: Test 1 (Lessons 1-4), Test 2 (Lessons 5-7), Test 3/Final (Lessons 8-11). На кожен тест відводиться 3 дні (п\u0026rsquo;ятниця + вихідні). Особливість полягає в тому, що в п\u0026rsquo;ятницю викладають 80% питань, і тому за бажання можна гарно підготуватись.\nОкрема особливість курсу - це reading list із ~30 академічних статей. Читати все не обов\u0026rsquo;язково, але за двома статтями треба написати summary. Особисто я прочитав усього 3: MapReduce - бо треба було для лаби, і 2 для summary:\nUsing Processor-Cache Affinity Information in Shared-Memory Multiprocessor Scheduling (1993) - тут назва говорить сама за себе) The Multikernel: A New OS Architecture for Scalable Multicore Systems (2009). Тут уже цікавіше. Тут описується реально існуюча ОС Barrelfish, розроблена в ETH Zürich, ідея якої в тому, щоб мати можливість працювати на гетерогенних ядрах. Уявіть: у вас є кластер, де і x86, і ARM, і RISC-V, а ви зверху накатуєте одну ОС. Що можу сказати про цей курс. Мені було цікаво, а це, я думаю, головне) Особливо цікавими для мене були лаби. Якщо захочете подивитись лекції, то вони є у вільному доступі в інтернеті. З мінусів я можу виділити розгляд неактуальних технологій, але, як кажуть, університет - це не ПТУ. Інколи корисно розглянути щось суто з академічної/теоретичної точки зору, особливо враховуючи, як деякі технології можуть отримувати друге життя - як, наприклад, Global Memory Systems переродилась у disaggregated memory з попитом на навчання LLM.\nЯ дуже хотів отримати A, бо це дає доступ до предмету Systems Design for Cloud Computing (SDCC). SDCC - це логічне продовження AOS, з тим самим професором. Основний топік цього предмету - це імплементувати MapReduce, але вже в повному вигляді. Врешті я отримав A, але SDCC я так поки і не взяв, бо він вимагає відвідування мітингів, які за київським часом проводяться вночі. Це єдиний не повністю асинхронний курс, про який я знаю в OMSCS.\nПідсумки Перший семестр вийшов насиченим. AOS виявився гарним вибором для старту - достатньо challenging, щоб було цікаво, але не настільки, щоб паралельно з full-time роботою це було нереально. На OMSCentral AOS оцінений студентами за складністю в середньому як 4/5.\nЧерез те, що в мене дуже погано з time management, я все відкладав на останній момент. Оскільки дедлайни на тести були десь о 8 ранку в понеділок, я декілька разів складав їх о 1 ночі під кінськими дозами кофеїну. З лабами була приблизно така ж проблема. Я хочу зауважити, що це не через те, що навантаження надзвичайно велике, а суто через моє невміння розподіляти час (були тижні, в які я взагалі нічого не робив із навчання). Тобто якщо ви стабільно декілька разів на тиждень будете щось робити, без довгих перерв - проблем із поєднанням з full-time взагалі не буде.\nЩо далі? У наступній частині розкажу про ще три курси: Software Analysis and Testing, High Performance Computing і High Performance Computer Architecture. Stay tuned! Я сподіваюсь, цього разу перерва між частинами буде менша ніж рік)\n","permalink":"https://niksol15.github.io/blog/uk/posts/omscs-part-3/","summary":"\u003cp\u003e\u003cem\u003e\u003ca href=\"/posts/omscs-part-1-uk/\"\u003eЧастина 1: Огляд програми\u003c/a\u003e |\n\u003ca href=\"/posts/omscs-part-2-uk/\"\u003eЧастина 2: Досвід вступу\u003c/a\u003e\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eВітаю, товариство! У цьому пості я розкажу про те, як загалом влаштовано навчання, і про свій перший курс - Advanced Operating Systems. Я великий прокрастинатор, тому матеріалу накопичилось достатньо багато. Спершу я хотів у цьому пості розказати про всі предмети, які встиг узяти, але зрозумів, що це занадто за обсягом, тому ще про 3 предмети розповім у наступному дописі.\u003c/p\u003e\n\u003ch2 id=\"організація-навчання\"\u003eОрганізація навчання\u003c/h2\u003e\n\u003ch3 id=\"онбординг-після-зарахування\"\u003eОнбординг після зарахування\u003c/h3\u003e\n\u003cp\u003eУсіх вступників автоматично зараховують на міні-курс, де директор програми Dr. Joyner коротко розповідає що до чого: які очікування від студентів, як проходить навчання, що важливо знати на старті. Тобто, у разі вступу, ви точно розумітимете, що від вас очікується.\u003c/p\u003e","title":"OMSCS Частина 3: Перший семестр - організація навчання і AOS"},{"content":"Частина 1: Огляд програм\nЦей допис оригінально опублікований на DOU у січні 2025 року\nЦе друга частина серії про OMSCS. В цьому пості розкажу про свій досвід вступу - від підготовки до IELTS до отримання листа про зарахування.\nКоротко нагадаю, що треба для вступу в OMSCS:\nIELTS Academic або TOEFL iBT на рівень C1 Диплом бакалавра комп\u0026rsquo;ютерних наук із середнім балом 3+/4 (82+/100) 3 рекомендаційних листи Заповнена вступна форма і оплачений application fee Таймлайн Основний момент про який треба знати - це те, що документи треба подавати дуже заздалегідь. Я вступав на осінній семестр 24 року, який починається в кінці серпня. Дедлайн подачі документів - 1 березня того ж року. Якщо ж ви вступаєте на весняний семестр, який починається на початку січня, то дедлайн подачі документів - 15 серпня попереднього року. На літній семестр вступати не можна, тому майте це на увазі.\nIELTS Почнемо, як виявилось, з найцікавішого - з англійської. Вимоги наступні:\nTOEFL Requirements\nInternet-based: 100, with minimum section scores of 19 IELTS Academic Requirements\n7.5 Overall Band score 6.5 Reading 6.5 Listening 6.5 Speaking 5.5 Writing У мене все життя було погано з англійською. В табелі взагалі стоїть 8/12, і на першому курсі університету я був в Pre-Intermediate/Intermediate групі, пари в якій я майже всі пропустив. Після цього були спроби виправити ситуацію, але вони не були системними. Отже, врешті-решт, бакалавріат я закінчив з дуже гарним словниковим запасом, але з майже ніякою граматикою. Я міг нормально говорити, у тому сенсі, що я доносив свою думку, але граматику я обирав інтуїтивно, ледь не рандомно.\nЗнаючи, що у мене до дедлайну майже 9 місяців, я вирішив пару місяців відпочити, а готуватися вже з вересня. Але врешті почав я готуватися в кінці вересня)\nЯ вирішив спочатку підтягнути загальний рівень англійської, і вже потім готуватися специфічно до TOEFL/IELTS. Загалом сенсу в цьому небагато, тому якщо ви в такій ситуації, то починайте готуватись паралельно з підвищенням рівня.\nДля цього я обрав школу First Cambridge Center (не реклама). Мій рівень оцінили десь в B1+/B2-. Я одразу сказав, що мені треба граматика і я хочу персональні заняття. Незважаючи на це, спочатку мені намагались продати групові заняття, бо вони більш вигідні будь-якій онлайн школі. Я все ж таки сходив на одне, бо воно було безкоштовне. Очевидно такий формат мені не підійшов - він більше підходить для людей, яким треба розговоритись, а не швидко підтягнути граматику.\nПісля цього я ходив на індивідуальні заняття. Відгук наступний: викладачі працюють по готовим презентаціям, розробленими школою, через що можливості індивідуалізації підходу обмежені. Проте викладачі привітні та мають хороші знання мови. З підготовкою до IELTS у нас щось не пішло.\nТому в кінці грудня 23 року я почав паралельно готуватися специфічно до IELTS з іншим репетитором, якого мені порекомендувала дівчина. В кінці січня 24 року я припинив заняття в First Cambridge Center і далі готувався вже тільки з репетитором. В цілому це вийшло набагато дешевше, та, що найголовніше, ефективніше. Сам іспит я здавав у кінці лютого 24 року.\nIELTS vs TOEFL Основна різниця - speaking в TOEFL відбувається з комп\u0026rsquo;ютером, а в IELTS - з людиною. Тут вже питання, кому як зручніше, але майте на увазі: якщо в TOEFL ви затупите і не будете знати, що казати, то це фініш. В IELTS speaking проходить у форматі розмови, і у випадку затупу екзаменатор просто задасть вам наступне питання. Звісно, затуп вплине на оцінку, але не так критично.\nЦе не вся різниця між TOEFL і IELTS - можете прочитати про це більше в інтернеті. Але я вважаю, що саме формат speaking є визначальним.\nБільше про IELTS Academic Треба розуміти, що високий рівень володіння англійською мовою не гарантує високий бал на IELTS. Всього є 4 секції:\nListening Reading Writing Speaking Кожна оцінюється від 1 до 9 з кроком 0.5. Listening, Reading та Writing складаються за один раз, Speaking складається окремо, можливо в інший день (я б рекомендував записатись на різні дні).\nListening 40 питань, які розбиті на 4 частини:\nЧастина 1 - повсякденний діалог Частина 2 - повсякденний монолог Частина 3 - діалог на тему education and training Частина 4 - монолог на академічну тему 30 хвилин з невеликими перервами ви слухаєте записи, після цього вам дається 10 хвилин перевірити себе і дописати, що недописано. Основна складність - всі записи ви слухаєте тільки 1 раз, тому відповідати треба на ходу і у вас немає багато часу думати.\nТут дуже важлива практика. Цю частину легко тренувати самому - просто слухаєте і відповідаєте, поки не навчитесь це робити одночасно. Це третя по складності секція для мене.\nReading 40 питань, які розбиті на 3 секції, кожній з яких відповідає текст. Загалом 2150-2750 слів. На це виділяється 60 хвилин.\nЗагалом немає особливо, що розказати. Якщо у вас гарний словниковий запас англійської мови, проблем виникнути не повинно. Проте все одно рекомендую спробувати пройти тест декілька разів перед тим як здавати. Це четверта по складності секція для мене.\nWriting Два завдання на які відводиться 60 хвилин:\nЗавдання 1 - описати візуальну сутність: графік, таблиця, діаграма, навіть карта. Очікується приблизно 20 хвилин. Завдання 2 - написати есе на задану тему. Очікується приблизно 40 хвилин. Я вважаю, що неможливо якісно підготуватись до цієї частини без викладача. Основна складність полягає у тому, що від вас буде очікуватись чітка структура відповіді. Написати в довільному форматі на високий бал не вийде. Саме тому треба, щоб була людина, яка знає, що тут очікується, щоб вона: 1) пояснила це вам 2) могла оцінити ваші відповіді і дати фідбек.\nТут важливою є практика, треба просто набити руку, особливо для першої частини. Як тільки ви правильно описали один графік, далі це майже no brain задача. Це друга по складності секція для мене.\nПро прокторинг: перед тим як складати ці секції, треба на вебку ноутбука повністю зняти кімнату, щоб можна було переконатися, що ви в кімнаті одні. Під час складання іспиту захоплюється екран і відео з вебки. Не можна використовувати навушники, закривати рот рукою і виходити за межі вебки. Під час складання я підпер підборіддя рукою, закриваючи трохи рот, і до мене підключилась людина сказати, щоб я так не робив. Загалом складання тестів в OMSCS, використовуючи Honorlock, відбувається майже ідентично, окрім частини, де до тебе може хтось підключитися.\nSpeaking Три частини, на які виділяється 11-14 хвилин:\nЧастина 1 - ваше представлення і невелике інтерв\u0026rsquo;ю. Питають про життя, дім, роботу. Майже звичайна розмова. 4-5 хвилин. Частина 2 - вам дають карточку з темою, на яку треба говорити 3-4 хвилини. Картка містить поінти, які можете використати. На підготовку дається хвилина. Частина 3 - обговорення відповіді з другої частини. Уточнюючі питання. 4-5 хвилин. Раптом ви думаєте, що якщо у вас гарна розмовна англійська, то ви точно наберете багато балів - ви помиляєтесь. Тут мало того, що треба розмовляти граматично правильно, потрібно ще й використовувати просунуту лексику та граматичні конструкції, які зазвичай в розмовах не зустрінеш.\nПроте це не основна складність. Основна складність - це вижати з себе максимальну кількість інформації. Наприклад, якщо в першій частині вам задають просте питання типу \u0026ldquo;яка ваша улюблена страва\u0026rdquo;, то не можна просто сказати \u0026ldquo;Борщ\u0026rdquo; чи \u0026ldquo;Моя улюблена страва - це борщ\u0026rdquo;. Треба на ходу придумати 2 речення, бажано з вживанням якогось perfect часу. Але майте на увазі - якщо вас понесе кудись не туди, з вас теж знімуть бали.\nДля розуміння: в другій частині вас можуть запитати, яку пораду ви нещодавно отримували, а ви могли не отримувати поради взагалі! Або опишіть пісню, яку ви любите. Як взагалі можна описувати пісню 3 хвилини? Мені пощастило - мені випало щось про забруднення навколишнього середовища, а про це можна багато чого наговорити. В другій частині ваша задача, щоб вас зупинили, а не ви самі зупинились.\nЦе найскладніша частина для мене, бо я в розмовах конкретна людина, і навіть рідною мовою не можу лити воду.\nРеєстрація і результати Де реєструватися? Я використовував British Council. При реєстрації вам додатково дають доступ до платформи де можна тренуватись.\n20 лютого я здавав Speaking, 21 - все інше, 23 я вже мав результати. Здавати раджу заздалегідь, бо IELTS дає можливість перездавати окремі секції за доплату - краще підстрахуватись і здати з запасом, щоб у разі чого мати можливість перездати. Результат IELTS дійсний 2 роки. Ще майте на увазі, що реєструватися треба теж заздалегідь - вільних слотів може не бути на найближчий час.\nРесурси:\nIELTS 17 Academic (є багато частин) IELTS Advantage на YouTube Рандомні відео mock speaking, де проводять speaking частину на камеру Мої результати:\nListening 8.0 Reading 8.5 Writing 6.5 Speaking 6.5 Total 7.5 Як можна побачити, пройшов я \u0026ldquo;на тоненького\u0026rdquo;, але загалом результатом задоволений, особливо знаючи якою була відправна точка.\nРезюмуючи:\nПочинайте готуватися заздалегідь. Час підготовки залежить від вашого рівня, але я б закладав десь пів року. Готуйтеся з репетитором, який знайомий з форматом екзамену і може розробити програму індивідуально під ваш запит. Здавайте і реєструйтеся теж заздалегідь, щоб була можливість перездати. Заповнення заявки Після того як ви здали IELTS або TOEFL, далі все просто. Тут можна ознайомитись з інструкцією як подавати заявку.\nДиплом Треба мати диплом бакалавра комп\u0026rsquo;ютерних наук або суміжної спеціальності із середнім балом 3.0+ з 4, що відповідає ~82+ по 100-бальній шкалі, але це не є строго обов\u0026rsquo;язковим. Обов\u0026rsquo;язкова частина - мати диплом бакалавра, але він може бути майже у будь-якій сфері, або може мати середній бал менше 3.0. Такі випадки розглядаються case-by-case. На Reddit багато історій про те, як люди в такій ситуації успішно закінчували OMSCS.\nКоли я вступав, не було жодної інформації чи взагалі реально туди вступити з українським дипломом. Тому я переживав, що мій диплом вони можуть не прийняти. Але як ви можете побачити, ці переживання були марними)\nТреба завантажити свою копію диплома і додатку до диплома. Після того, як ви оплатили application fee і вас попередньо погодили, треба буде відправити оригінал диплома з додатком і апостильованими перекладами в Georgia Tech. Про те, що вам треба надіслати фізичну копію, вам скажуть окремо. На відправлення диплома дедлайн не такий, як на подачу заявки, тому у вас буде достатньо часу.\nМені пощастило - у мене диплом одразу на двох мовах, тому мені не треба було нічого додатково робити, і я просто його відправив. Після того як ваш диплом перевірять, вам його відправлять назад.\n3 рекомендаційних листи Вам треба попросити 3 людей щоб вони вас порекомендували, і в заявці залишити їх університетську (якщо просите викладачів) або корпоративну (якщо просите ваших direct managers) пошту з описом хто вам є ця людина. Потім цим людям приходить посилання на пошту, за цим посиланням вони заповнюють форму.\nЯ, якщо чесно, не знаю чи треба там прикріпляти сам рекомендаційний лист. Тому, коли я звертався до викладачів, я ще про всяк випадок надсилав їм лист, який вони можуть використати. Мені пощастило - мене проігнорував лише один викладач, якого я просив, тому мені довелося звернутись всього до чотирьох.\nЩе один момент: бажано, щоб це були саме викладачі. Якщо у вас вже немає контакту з викладачами, це можуть бути ваші технічні менеджери або колеги, які можуть щось позитивне розказати про ваші знання Computer Science.\nМайте на увазі, що люди можуть вам довго не відповідати або довго заповнювати рекомендацію, тому не відкладайте це на останній момент.\nДозаповнення заявки Вам ще треба буде коротко відповісти на пару бюрократичних і мотиваційних питань по типу: нащо вам взагалі ця програма і чому ви думаєте, що вам вдасться її закінчити.\nПотім ви маєте заплатити application fee. Коли я подавав заявку, це було 105 доларів. В мене без проблем вийшло заплатити гривневою картою Приватбанку онлайн.\nНа цьому майже все! Далі ви чекаєте поки вас \u0026ldquo;оброблять\u0026rdquo;. Зазвичай рішення відправляють всім приблизно в один період часу, тому можете моніторити Reddit OMSCS - люди почнуть писати що вони були прийняті. Потім вам скажуть, що ви conditionally accepted, і після того вам треба буде підтвердити ваш диплом бакалавра шляхом відправки його в GaTech (я описував це вище).\nУ разі виникнення питань є офіційна пошта, на яку можна звернутись. Вам точно нададуть відповідь, але готуйтесь, що це може зайняти багато часу через кількість звернень. Ще можете задавати питання на Reddit, щоб отримати \u0026ldquo;неофіційні\u0026rdquo; відповіді від студентів і навіть представників університету.\nПіслямова Я хочу, щоб українці айтівці, особливо молоді спеціалісти, розуміли, що зараз є інші шляхи, окрім як по інерції йти на магістра на той факультет, де отримували бакалавра. Сьогодні є можливості отримати освіту світового рівня за адекватні гроші, яка гарно поєднується з full-time роботою, не покидаючи Україну, і вступити на яку може звичайна людина.\nНавіть якщо ви отримали освіту вже давно - корисно знати, що є така можливість перекваліфікуватися або підвищити свою кваліфікацію. Я зустрічав багато випадків, коли на OMSCS йшли люди з досвідом 10+ років в індустрії, щоб відкрити для себе нові можливості або просто дізнатися щось цікаве для себе.\n","permalink":"https://niksol15.github.io/blog/uk/posts/omscs-part-2/","summary":"\u003cp\u003e\u003cem\u003e\u003ca href=\"/blog/uk/posts/omscs-part-1/\"\u003eЧастина 1: Огляд програм\u003c/a\u003e\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003e\u003cem\u003eЦей допис оригінально опублікований на DOU у січні 2025 року\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eЦе друга частина серії про OMSCS. В цьому пості розкажу про свій досвід вступу - від підготовки до IELTS до отримання листа про зарахування.\u003c/p\u003e\n\u003cp\u003eКоротко нагадаю, що \u003ca href=\"https://omscs.gatech.edu/admission-criteria\"\u003eтреба для вступу в OMSCS\u003c/a\u003e:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003eIELTS Academic або TOEFL iBT на рівень C1\u003c/li\u003e\n\u003cli\u003eДиплом бакалавра комп\u0026rsquo;ютерних наук із середнім балом 3+/4 (82+/100)\u003c/li\u003e\n\u003cli\u003e3 рекомендаційних листи\u003c/li\u003e\n\u003cli\u003eЗаповнена вступна форма і оплачений application fee\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 id=\"таймлайн\"\u003eТаймлайн\u003c/h2\u003e\n\u003cp\u003eОсновний момент про який треба знати - це те, що документи треба подавати дуже заздалегідь. Я вступав на осінній семестр 24 року, який починається в кінці серпня. \u003ca href=\"https://omscs.gatech.edu/deadlines-decisions-requirements-and-guidelines\"\u003eДедлайн\u003c/a\u003e подачі документів - 1 березня того ж року. Якщо ж ви вступаєте на весняний семестр, який починається на початку січня, то дедлайн подачі документів - 15 серпня попереднього року. На літній семестр вступати не можна, тому майте це на увазі.\u003c/p\u003e","title":"Магістратура ТОП-10 вишу США за 7k доларів (частина 2)"},{"content":"Цей допис оригінально опублікований на DOU у грудні 2024 року\nУ серії дописів ділюсь своїм досвідом вступу в OMSCS - онлайн Computer Science магістратуру в Georgia Tech - і враженнями від навчання. Тут буде як корисна інформація для тих, хто розглядає онлайн CS магістратури, так і багато власного досвіду з суб\u0026rsquo;єктивними думками і англіцизмами.\nЦей пост - огляд онлайн магістратур і вимоги до вступу.\nЧому OMSCS Ще на 1 курсі універу я прочитав статтю про магістратуру за ціною кави. Там автор порахував, що день навчання на OMSCS коштує як кава в Starbucks, що, погодьтесь, виглядає дуже привабливо для американського диплому у відносно відомому виші.\nУкраїнська державна CS освіта Пропустіть цю секцію, якщо вам не цікаві проблеми вітчизняної вищої освіти.\nБакалавра комп\u0026rsquo;ютерних наук я отримував на кубіку (Факультет Комп\u0026rsquo;ютерних наук і кібернетики КНУ Шевченка). Якщо на молодших курсах я отримував багато задоволення від навчання, то на старших з цим було набагато гірше.\nПо-перше, на 2 курсі я почав працювати на дуже крутому проекті в GlobalLogic, на якому провів 3 з половиною роки і став з трейнішки мемним 21-річним сіньйором (заохочую вас написати комент про те, що не може бути 20-річних сіньйорів, і взагалі зараз інфляція тайтлів в українському айті, а щоб бути сіньйором треба мати мінімум 10 років досвіду). Це означає, що міг витрачати свій час заробляючи гроші, а не оцінки для стипендії в розмірі 1400 гривень.\nІ це розповсюджена ситуація. Активні й шарящі студенти починають працювати ледь не з першого курсу, бо всі хочуть заробляти гроші і набиратись досвіду в індустрії тут і зараз, а не через 4 роки.\nПо-друге, поступово предмети стають дуже специфічними, і у тебе немає змоги від них відмовитись. Тобто у тебе немає вибору, окрім як вчити те, що тобі дають, а дають всім однакове, незалежно від вподобань. Ну як, вибір є, але зазвичай це вибір між двома назвами одного і того ж предмету. У випадку КН на кубіку, єдиний реальний вибір - це вибір між кафедрами на 3 курсі, від якого в семестрі залежить 1-3 предмета. Це вибір між:\nчіловими предметами алгоритмікою, ML, криптографією (я обрав це) займатись веб-розробкою, але на парах в універі Ці дві проблеми працюють у синергії. Тобто коли тобі дають нецікавий для тебе предмет, у тебе немає мотивації себе пересилювати і розбиратись, і ти просто йдеш приділяти час роботі. Таким чином, більшість знань, які надаються універом, проходять повз більшості студентів (включаючи мене).\nХочу зробити ремарку, що я не вважаю кубік поганим факультетом. Я не жалкую, що отримав тут бакалавра і обрав би КН на кубіку ще раз. По-перше, через нетворкінг - я вважаю, що на кубіку вчаться одні з найкрутіших людей. По-друге, незважаючи на ситуацію, яку я описав, періодично з\u0026rsquo;являється можливість вчити дуже цікаві предмети, які викладають дуже круті викладачі. Єдина проблема - вчасно помітити це, а не на екзамені, коли ти закриваєш предмет на 60, бо ти пропустив всі пари.\nОтже, незважаючи на те, що я б обрав кубік для бакалаврату ще раз, я був неготовий обирати його для магістратури, як і будь-який інший факультет у державному виші в Україні. Як показали відгуки від колишніх однокурсників, які продовжили навчання, я зробив правильний вибір. Я почав глибше цікавитись іншими можливими варіантами.\nАльтернативи Почнемо з моїх вимог до магістратури:\nПовністю онлайн і асинхронна, щоб я міг легко поєднувати навчання з full-time роботою Має коштувати адекватних грошей Має бути у відомому респектабельному університеті, щоб посилити моє CV Має бути можливість проходження магістратури перебуваючи в Україні, бо у мене немає бажання виїжджати Має бути широкий вибір предметів Як виявилось, станом на зараз з\u0026rsquo;явилась велика кількість онлайн магістратур, особливо в США. OMSCS була першою, і її успіх дав поштовх великій кількості аналогічних програм.\nКоротко про Master of Science in Computer Science: рейтинг CS програм в різних університетах можна подивитись тут:\ntopuniversities - з усього світу usnews - Graduate програми в США csranking - тільки США, але можна виставити фільтри по конкретній області CS Загалом, майже у кожному випадку треба пройти близько 10 предметів в майже довільному темпі.\nОтже, що ми маємо станом на зараз:\nStanford - тут не виникає питань щодо репутації, але ціна в 70k$ за диплом робить цей варіант не актуальним. Що насправді шкода, бо мені сподобалось проходити MOOCs по криптографії і компіляторам від Стенфорду.\nJohns Hopkins - ситуація аналогічна, але ціна вже 53k$.\nUniversity of Illinois Urbana-Champaign (Coursera), скорочено UIUC MCS. Ціна вже 20k-24k, що вже стає більш реальним. Додатково, є широкий вибір із 22 дисциплін.\nUniversity of Texas at Austin, програма відома як MSCSO. Ціна рівно 10k за 10 предметів, що вже дуже приємно. Вибір більш обмежений, всього 18 предметів. Є схожі програми, але заточені специфічно під Data Science: MSDSO і AI: MSAIO.\nGaTech, програма відома як OMSCS. Ось ми і добрались до справжнього скарбу. Це найстарша, наймасовіша онлайн MSCS програма. Ціна приблизно 7k за весь диплом. Є можливість обрати з майже 70 предметів на будь-який смак!\nЦей список не повний, є багато інших програм. Для пошуку можна скористатися Coursera та EdX. Для вибору рекомендую дивитися відгуки на Reddit і відео на YouTube.\nЩодо вищенаведеного списку, загалом найпопулярніші зараз програми - це OMSCS, MSCSO, UIUC. Враховуючи такі фактори як ціна та різноманітність дисциплін, очевидним вибором для мене є OMSCS, на другому місці - MSCSO.\nХочу відзначити, що якби я цікавився специфічно ML/DS/AI, я б можливо обрав UT at Austin, бо:\nЦе один з найтоповіших універів, якщо розглядати саме DS Різноманітність предметів не так впливає, бо ти й так вчиш те, що тебе цікавить Різниця в 3k$ не так і суттєва для цілої програми Про OMSCS і вимоги для вступу В GaTech, окрім OMSCS, є ще окремі програми по дата аналітиці OMSA і по кібербезпеці OMSC. Але я розкажу специфічно про OMSCS. Наперед рекомендую полазити в інтернеті на тему OMSCS, особливо на Reddit.\nЄ вибір із великої кількості предметів. Треба пройти 10 із них, але пройти рандомні 10 не вийде. По проходженню 10 мають бути виконані вимоги однієї із 6 спеціалізацій. Найпростіший спосіб у цьому розібратись - натикати собі програму в цьому планері.\nПорядок проходження теж важливий. За перший рік треба пройти 2 foundational курси (вони помічені зірочкою в списку) на оцінку не менше ніж B. Якщо у вас не вийде, ви просто будете обмежені у виборі предметів, поки не виконаєте цю умову.\nДодаткове уточнення: навчальний рік складається з 3 семестрів - Spring, Summer, Fall. Не можна пропускати більше 2 семестрів підряд. На проходження програми відводиться 5 років. Всюди рекомендується починати проходити по 1 предмету за семестр. Як показала практика, це правильна порада.\nСподіваюсь мені вдалося розрекламувати програму, тому тепер ми поговоримо про вимоги до вступу. Вимоги описані тут.\nДиплом бакалавра у сфері комп\u0026rsquo;ютерних наук або суміжній із середнім балом 3.0+ з 4.0, що відповідає десь 82+ по 100-бальній шкалі. АЛЕ! Не замучуйтесь, якщо ця вимога не виконана - на OMSCS вчиться багато людей без профільної CS освіти, або з середнім балом меншим за 3.0. Просто під час вступу такі заявки будуть розглядатись case-by-case.\nIELTS або TOEFL на рівень C1. Більш детально тут. На жаль, з цим все строго і цю вимогу ніяк не обійти.\n3 рекомендаційних листи. Бажано, щоб вони були від викладачів, які викладали у вас на бакалавраті. Якщо ж ви дід і отримали бакалавра вже дуже давно, то підійдуть листи від ваших керівників. Єдиний момент - вони мають хвалити ваші CS знання і вміння, а не готовність вночі фіксити поламаний prod.\nРезюме і заповнена форма з питаннями, але це вже дрібниці.\nВсе! Якщо ці вимоги виконані і ви заплатили символічний application fee (десь 70 доларів), то ви майже гарантовано вступили. Тут майже немає відбору, це як масовий MOOC - люди просто відсіюються по ходу програми.\nВажливий факт: по закінченню ви отримаєте диплом, аналогічний до дипломів, які GaTech видає on-site студентам. Також GaTech наголошує, що онлайн програма нічим не відрізняється від on-site, і щоб отримати диплом, треба буде суттєво напрягтись.\n","permalink":"https://niksol15.github.io/blog/uk/posts/omscs-part-1/","summary":"\u003cp\u003e\u003cem\u003eЦей допис оригінально опублікований на DOU у грудні 2024 року\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003eУ серії дописів ділюсь своїм досвідом вступу в OMSCS - онлайн Computer Science магістратуру в Georgia Tech - і враженнями від навчання. Тут буде як корисна інформація для тих, хто розглядає онлайн CS магістратури, так і багато власного досвіду з суб\u0026rsquo;єктивними думками і \u003ca href=\"https://www.youtube.com/watch?v=3W40tBACFbI\"\u003eангліцизмами\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eЦей пост - огляд онлайн магістратур і вимоги до вступу.\u003c/p\u003e\n\u003ch2 id=\"чому-omscs\"\u003eЧому OMSCS\u003c/h2\u003e\n\u003cp\u003eЩе на 1 курсі універу я прочитав статтю про магістратуру за ціною кави. Там автор порахував, що день навчання на OMSCS коштує як кава в Starbucks, що, погодьтесь, виглядає дуже привабливо для американського диплому у відносно відомому виші.\u003c/p\u003e","title":"Магістратура ТОП-10 вишу США за 7k доларів (частина 1)"},{"content":"Привіт, я Нікіта Солонко - C++ розробник із Чернігова.\nЗараз працюю в HFT компанії Atto Trading, де займаюсь розробкою low-latency платформи. До цього три з половиною роки розробляв safety-critical automotive middleware платформу для Volvo/Polestar в GlobalLogic.\nПаралельно здобуваю магістратуру онлайн в Georgia Tech (OMSCS). Бакалавра закінчив на Кібернетиці в КНУ Шевченка, кафедра Математичної Інформатики. Диплом писав про криптографію на еліптичних кривих. Ще маю сертифікати Stanford з компіляторів та криптографії - люблю поглиблювати CS фундамент, коли зʼявляється час.\nПро що цей блог Цей блог - спроба систематизувати власні знання та досвід через створення контенту для української tech-спільноти. Вирішив його почати, тому що в Україні мало контенту про жорсткий С++, системне програмування, комп\u0026rsquo;ютерну архітектуру тощо. Також у нас мало знають про OMSCS, тому це спроба розповісти людям про альтернативу.\nКонтакти GitHub: Niksol15 LinkedIn: nikita-solonko DOU: nikita-solonko/topics Код блогу на GitHub.\n","permalink":"https://niksol15.github.io/blog/uk/about/","summary":"\u003cp\u003eПривіт, я Нікіта Солонко - C++ розробник із Чернігова.\u003c/p\u003e\n\u003cp\u003eЗараз працюю в HFT компанії Atto Trading, де займаюсь розробкою low-latency платформи.\nДо цього три з половиною роки розробляв safety-critical automotive middleware платформу для Volvo/Polestar в GlobalLogic.\u003c/p\u003e\n\u003cp\u003eПаралельно здобуваю магістратуру онлайн в Georgia Tech (OMSCS).\nБакалавра закінчив на Кібернетиці в КНУ Шевченка, кафедра Математичної Інформатики.\nДиплом писав про криптографію на еліптичних кривих.\nЩе маю сертифікати Stanford з компіляторів та криптографії - люблю поглиблювати CS фундамент, коли зʼявляється час.\u003c/p\u003e","title":"Про мене"}]