Let the Holy War begin: Java vs С++



Напередодні Joker 2016 ми накатали пост про Java Performance, який викликав бурю емоцій у читачів. Щоб вкинути палива у вентилятор і спробувати все-таки прийти до якогось єдиного рішення, ми вирішили залучити експертів з різних «таборів»:

  • Дмитро Нестерук. Експерт з .NET, С++ та інструментів розробки, автор курсів з технологій і математики, квант.
  • Андрій Паньгин. Провідний програміст компанії Однокласники, що спеціалізується на високонавантажених бэкендах. Знає JVM як свої п'ять пальців, оскільки раніше протягом декількох років розробляв віртуальну машину HotSpot Sun Microsystems і Oracle. Любить асемблер і низькорівневе системне програмування.
  • Володимир Ситніков. Десять років працює над продуктивністю і масштабованість NetCracker OSS — ПЗ, яке використовується операторами зв'язку для автоматизації процесів управління мережею і мережевим обладнанням. Захоплюється питаннями продуктивності Java і Oracle Database.
  • Олег Краснов. CTO компанії SEMrush і адепт ANSI C.

Андрій Паньгин
— Java та С++, який зараз, по-вашому, мова найбільш затребуваний? Обидва з них вже повнолітні, однак хто більш зрілий і відточений?

— Насамперед, я не вважаю, що між цими мовами існує якась конкуренція. У кожного з них є своя ніша, і вони прекрасно співіснують разом. Традиційно популярність Java декілька вище. Java платформа привертає своїм потужним інструментарієм для налагодження та супроводження програм. Однак і значимість С++ складно переоцінити. Незважаючи на те, що це мова з величезною історією, він і зараз продовжує активно розвиватися: тільки розробники встигли звикнути до C++11, як вийшов стандарт С++14 з безліччю нових цікавих фіч.

— Що мови можуть дати в світі високонавантажених серверів? Має сенс розробляти окремі модулі системи на різних мовах, заточуючи їх під конкретні завдання? Якщо б могли (захотіли або просто була б можливість), стали б використовувати З++ для вирішення завдань або ж робили все тільки на одній мові?

— Під високонавантаженими серверами кожен розуміє щось своє. Для одних це тисячі мережевих запитів в секунду, для інших — паралельні обчислення над великими обсягами даних. Для різних задач краще підходять різні інструменти. У нас (в Однокласниках) є модулі, написані на С++, зокрема, пов'язані з обробкою зображень і відео — там потрібні SIMD обчислення та максимально ефективне використання процесора. Втім, для більшості наших систем продуктивності Java вистачає. Більш того, фрагменти коду, раніше розроблені на C і викликаються через JNI, ми поступово переписали на Java, в результаті чого навіть виграли в продуктивності, оскільки позбулися зайвих копіювань і накладних витрат JNI.

— Використання Unsafe в Java, виправдано чи ні? Чому б тоді не використовувати З++?

— У мене є цілий доповідь на тему того, чому ми використовуємо Unsafe. Існує ряд сценаріїв, де без Unsafe поки не обійтися, зокрема, для роботи з off-heap та взаємодії з нативним кодом.
Якщо б ми захотіли написати програму цілком на C++, нам би довелося реалізовувати заново всі наші спільні фреймворки та протоколи: для збору статистики для моніторингу, для комунікації між серверами і т. д. А так — у нас є лише маленька частина коду з Unsafe, що відповідає за операції низького рівня, зате всю іншу розробку ми ведемо на звичній Java, дотримуючись кращих патернів написання простого і зрозумілого коду. Набагато зручніше, коли вся екосистема розробляється на єдиній платформі.

— Які найбільш часті проблеми продуктивності бувають при розробці Enterprise-систем та можливі способи рішення?

— Рідко, коли ми впираємося в продуктивність самої платформи Java. Зазвичай проблеми можна вирішити або заміною алгоритму, або масштабуванням, тобто, нарощуванням заліза. Найчастіше вузьким місцем виявляється пропускна здатність мережі або дисковий введення-виведення. Але якщо говорити про JVM, то головну або навіть єдину проблему продуктивності нам іноді доставляє збирач сміття, бо як паузи довше 500мс в наших випадках часто критичні. Тому ми намагаємося не робити Heap надмірно великим: максимум 50 гігабайт, але частіше і того менше: від 4 до 8 гігабайт на додаток. Великі обсяги намагаємося виносити за межі хіпа: зробили навіть фреймворк для створення великих високонавантажених кешей. Додаткова перевага такого кеша порівняно з хиповым — персистування, тобто, можливість перезапуску програми без втрати даних. Це досягається за рахунок використання поділюваної пам'яті: відразу після запуску додаток відображає об'єкт спільної пам'яті в адресний простір процесу, і кеш з усіма даними моментально стає доступний.

— Що ви можете сказати з приводу швидкого виходу JDK 9 і його головною фічі — модульності?

— Про модульність було багато розмов, дата релізу навіть кілька разів переносилася, щоб цю модульність, нарешті, доробити. Але при цьому серед моїх знайомих я не знаю жодного Java розробника, кому ця фіча була б, дійсно, потрібна. Я вважаю, краще б раніше випустили JDK 9 — розробники були б вдячні. Нам, наприклад, модульність, швидше нашкодить, ніж допоможе: адже один з побічних ефектів, що Unsafe тепер буде захований глибоко всередині, і без спеціальних ключиків недоступний. Зате в JDK 9 очікуються куди більш приємні нововведення, заради яких нову версію варто хоча б спробувати: поліпшення в G1, Compact Strings, VarHandles та ін

— Якщо дуже грубо подивитися, то різниця між C++ і Java — в прошарку runtime, яка, крім іншого, виконує всілякі оптимізації. Що краще: використання архітектурних особливостей машини вручну (С++), або краще покластися на динамічні оптимізації JVM? Якщо говорити зовсім про конкретні речі, то краще автоматична збірка сміття або ручне управління?

— Адаптивна компіляція і автоматичне керування пам'яттю — якраз сильні сторони Java. У цьому віртуальна машина досягла й перевищила статичні компілятори. Але головне навіть не це. Ми вибираємо JVM за ті гарантії безпеки, що вона нам дає. В першу чергу — захист від фатальних помилок із-за неправильної роботи з пам'яттю. Шукати проблеми, пов'язані з покажчиками або виходом за межі масиви, в некерованому коді на порядок складніше. І вартість виправлення таких помилок з лишком перекриває вигоду від того невеликого переваги в швидкості, що дає прямий доступ до пам'яті. Як говорилося вище, ми іноді використовуємо Unsafe, і в цих випадках ми автоматично піддаємо себе тим же ризикам, що і в C++. Так, нам іноді доводиться розбиратися в крашдампах JVM, і це заняття не з приємних. Саме тому ми все ж вважаємо за краще чисту Java, а некерований код використовуємо лише у випадках крайньої необхідності.

Також у мене буде доповідь на Joker як раз на тему: «Міфи та факти про продуктивність Java».

Дмитро Нестерук
— Java та С++, який зараз по-вашому мову найбільш затребуваний? Обидва з них вже повнолітні, однак хто більш зрілий і відточений?

— Якщо говорити про популярність, то тут все очевидно: Java, звичайно, більш затребувана, ніж інші мови. C++ займає свою нішу в трьох основних дисциплінах (game dev, фінанси і embedded), ну і плюс є основною мовою для HPC та scientific computing. Тому якщо дотримуватися шкурних інтересів, то Java звичайно безпечніше як навик, якщо тільки ви не йдете цілеспрямовано в одну з цих областей.

Що стосується зрілості, то тут все складно і треба спочатку розбивати на фічі мови, здатності компілятора і особливості стандартних бібліотек.

Почнемо з першого — з мов. І там і там є проблеми. З Java проблема в тому, що мова не розвивається так стрімко, як його найближчий конкурент, в результаті чого фічі приходять дуже повільно і не так, як хочеться. Тут примітно, що C# молодше, але в ньому першому з'явилися лямбды, також технологія LINQ (Language Integrated Query — це такі зручні механізми обходу вибірки наборів даних), так і початкові рішення на основі C# (тобто підтримка властивостей і делегатів) теж були виконані грамотно і вдало.

Що стосується C++, то тут основна проблема — це 100% сумісність З++ з мовою, що автоматично означає величезний багаж нікому не потрібних мовних функцій. З іншого боку, стагнація С++ в 2000-ті роки популярності мови не додала, т. к. розробників потрібно постійно підживлювати новими фічами. Зараз ситуація краща — в С++ є і лямбды (до речі, більш експресивні, ніж в C#/Java), висновок типів змінних і навіть повертаються з функції значень, загалом мова як-то еволюціонує.

Це що стосується мов. Тепер щодо компіляторів. Тут, по-перше, порівняння не зовсім коректно, оскільки JVM — це JIT, тобто ідея про те, що можна взяти байт-код і перетворити його в ідеальний таке подання для поточного процесора, з усіма оптимизациями, які застосовні. Це звучить добре в теорії — я не знаю як це на Java, але .NET світі такий підхід, порівняно з оптимизациями С++ компілятор, звичайно, він не робить практично нічого. Якщо ви робите математику або, я так скажу: якщо ви купуєте математичну .NET бібліотеки в мережі, припустимо, то це буде всього лише обгортка навколо С++.

Так, і щодо С++ компілятор: я для обчислювальних задач використовую Intel C++, тобто компілятор, який постачає, власне, виробник процесорів. В цьому є величезна кількість мінусів: мовних особливостей менше, ніж у MSVC, купа безглуздих помилок за яким доводиться звертатися в саппорт, але їмо ми цей кактус з однієї простої причини: оптимізації. Intel'івський компілятор генерує найбільш ефективний код. Звичайно, не єдиним кодом: тут у нас вся міць Intel Parallel Studio йде в хід, це і Threading Building Blocks для паралеллизации (до речі, аналог Microsoft Parallel Patterns Library), і Intel Math Kernel Library, яку навіть, якщо ти не використовуєш її безпосередньо, ти використовуєш її опосередковано через MATLAB та інші. Тут потрібно пояснити, що бібліотека начебто MKL вона вже оптимізована хлопцями з Intel: тут і векторизація, і паралелізація, і навіть кластерна паралелізація через MPI (наприклад для FFT) зроблена «з коробки» — тобто, бери і користуйся. Ну і звичайно варто згадати про засоби профиляции, які теж частина IPS. Це дуже потужний інструментарій, він, по суті, ставить собі за мету допомогти розробникові оптимізувати код в плані продуктивності, ну і коректності теж — там профиляция пам'яті теж є, так що витоку і все подібне легко знайти.

Ну і нарешті про «либы» — тут все просто, Java виграє, в С++ все погано. Я не буду навіть говорити про те, що сам інтерфейс C++ Standard Library трошки божевільний, але проблема мені бачиться не тільки в тому, що всі «легасі», а в тому, що просто дуже мало фіч! У нас ось тільки-тільки з'явилися такі речі як підтримка файлової системи і худо-бідно підтримка потоків якась. І те, ось є у мене рядок, хочу її побити на підрядки по прогалині — цього в стандартній бібліотеці немає, тобто я повинен брати сторонню бібліотеку (ну добре, що є така штука як Boost, там багато корисного). Але розвиток дійсно гальмує. Багато компаній, наприклад Electronic Arts, вони пишуть свої реалізації STL тому що їх не влаштовує стандартна. Ну і, в кулуарах, багато зізнаються що нам по суті потрібна нова, з нуля, бібліотека, якийсь STL2, хоча коректніше було б назвати її Standard Library 2 або якось так.

Є ще маса проблем, наприклад, відсутність основного пакетного менеджера, а також, навіть якщо б він був, як бібліотеки-то шарити? В Java або .NET можна просто бінарники роздавати, а в С++ по суті треба шарити сорцы. Ось цю проблему поки що ніхто толком не вирішив, і це теж гальмує розвиток т. к. іноді береш чуже либу, а потім витрачаєш півгодини просто, щоб вона у тебе заробила.

— Як у цілому відчувають себе мови в Enterprise, наприклад, у банківській сфері? Наприклад, в HFT (High-Frequency-Trading) світі великі навантаження і високі вимоги до надійності. Також фінансова індустрія досить консервативна. Як це позначається на виборі тієї чи іншої технології?

— Enterprise — це одна така гребінка велика, під яку зараз йде будь-яка корпоративна розробка. Глобально — це, звичайно, C# і Java, і інші мови десь на периферії. От стосовно банківської сфери — тут все трохи більш цікаво, і особливо цікаво, що місцями з'являється З++, ну і є деякі контори начебто Bloomberg'а, які взагалі на С++ повністю, але це мені здається аномалія. Взагалі, якщо зараз отримувати MFE, тобто магістра за фінансової інженерії, то там в основному З++ і використовується, хоча зараз популярні такі мови як Python і R, ну і MATLAB теж залишається актуальним.

Що стосується HFT, це теж така спірна тема, але так, вона, в основному, тяжіє до С++, а навіть до C, використання всяких FPGA, де і системний С присутній, або люди на всяких HDL мовах пишуть. Коли важлива швидкість, продуктивність, то тут native code як-то ближче, хоча аргумент, що, мовляв, «Java гальмує», мені здається вже неактуальний. Тут просто ще ручне управління пам'яттю іноді людям потрібно, всі бояться великої злісного GC, який прийде і зупинить всі потоки саме в той момент, коли вам потрібно буде зробити якусь операцію.

У quant finance «плюси» залишилися швидше з якихось консервативних міркувань, тому що финмат, на відміну від звичайної розробки софта, він розглядає програмування як навик схоже на знання англійської мови, а не як щось системоутворююче. Відповідно, люди просто вчать С++ і не мучаться, хоча зараз для аналізу Python і R якось більш популярні, навіть. Але «плюсів» в інвестбанках вагон.

— Для розробки ПЗ для вбудованих пристроїв, яку мову краще підходить на ваш погляд. Наскільки, на Ваш погляд, ці мови дозволяють писати портативний код?

— Взагалі, тема embedded занадто широка. Для багатьох embedded — це всякі Rasperry Pi або Arduino, для мене — це FPGA, для кого-то ще щось. Але якщо узагальнювати, то embedded це, звичайно, в основному З або С++, якщо говорити про прикладному рівні. Звичайно, для FPGA розробки я використовую або VHDL безпосередньо або пишу MATLAB, який після конверсії VHDL викидає — суть залишається та ж.

Конкретно про FPGA, оскільки це єдина тема, в якій я хоч щось розумію, можу сказати що мови, та й сам підхід розробки — це хороша ілюстрація того, як взагалі вся технологія може застрягти набагато гірше ніж С++ десь у старих моделях, мови і взагалі. З цією технологією дуже складно працювати, і ти починаєш по суті або використовувати всякі генератори начебто MATLAB або писати щось своє. Тобто, для людей, які працюють суто на системному рівні, перекладання бітів в ручну — це нормальне явище, але мені як людині, яка хоче, наприклад, моделювати набір бізнес-правил в «залізі», зовсім не подобається такий підхід, і мови не вистачає, щоб на високому рівні пояснити, що ж мені треба.

А говорити про Java і embedded я просто не кваліфікований.

— Якщо дуже грубо подивитися, то різниця між C++ і Java — в прошарку runtime, яка, крім іншого, виконує всілякі оптимізації. Що краще: використання архітектурних особливостей машини вручну (С++), або краще покластися на динамічні оптимізації JVM?

— Ну я, здається, вже це питання підняв, але тут все на любителя. Ось взяти мене, я на практиці використовую всі рівні паралелізації, тобто SIMD, OpenMP, MPI і це не кажучи про всяку специфіку начебто апаратних прискорювачів. SIMD оптимізації є якісь в Java, зараз он та .NET повільно підтягується, але по суті З++ все ще рулить в плані автоматичних оптимізацій, і не будемо забувати, що в С++ можна вручну забивати асемблерні блоки. Я розумію, що зараз асемблер ніхто не знає і багато-то і З++ в очі не бачили, але суть в тому, що коли справа стосується чисто обчислень, тобто математики, і хочеться швидше, то чому б і ні?

Я не особливо вірю в динамічні оптимізації ось чому: якщо у вас простий цикл, в ньому припустимо сумується масив — це так, це можна розпізнати, розпаралелити там. Проблема в тому, якщо ви, наприклад, втягли в цикл якусь залежність ззовні, що тоді робити? В OpenMP у нас відповідна розмітка, а динамічний оптимізатор такі завдання не зможе вирішувати, ніколи. Тому хтось, наприклад, подивиться на CUDA і скаже, що це модель абсолютно нереальна, навіщо мені всі алгоритми переписувати, та ще вчитися чогось? А як по мені, то це неминуче, оскільки оптимізатори працюють дуже добре зрозумілих, простих речах, роблять всякі инлайнинги, але все що performance-critical можна написати і руками, написати в native code і не мучитися.

— Наскільки динамічна екосистема Java і C++? Наскільки часто виходять оновлення, релізи, стандарти? Наскільки живі мови (наскільки багато мовних особливостей з'являється)?

— Ну, я думаю можна сказати, що «плюси» безсмертні, на відміну від Java як мови, де з'явилося багато нові мови з цікавими фічами — це і Scala і Kotlin та інші. Інша справа, що мова і платформа це різні речі. Java як мову багатьох не влаштовує, власне тому і нові мови. Але як платформа — там все прекрасно, судячи з усього, знову ж таки, є переваги навіть у порівнянні з найближчим конкурентом (наприклад в плані GC). Але як мову — достатньо підстав для претензій.

Про С++ я тут повинен висловитися, напевно. Звичайно, після того, як ком'юніті років так 13 в свій час взагалі нічого не робило, нові стандарти та нові фічі бібліотеки — це, звичайно, добре, чудово, я б навіть сказав, порівняно з повною апатією. В С++11 відбулося багато чого, дуже багато реально корисних зрушень, я тепер пишу зовсім інший З++ ніж раніше. В С++14 все ще трохи доулучшали, але от в С++17 знову розчарували весь світ — тобто, ті фічі які всіх чекали, їх не буде. Основна фіча, яку всі хотіли і хочуть загалом-то, це модулі. Просто зараз С++ компілюється дуже повільно, точніше первинна компіляція, т. к. инкрементальность-то у нього, якщо судити по MSVC, просто супер, але збірка наприклад либы «з нуля» — це задоволення нижче середнього. Ну і ось модулі повинні вирішити цю проблему, але ніхто не знає коли.

Знову ж таки, в С++ така проблема, що найбільш базових речей немає в стандартній бібліотеці. І це новачка, якому потрібно перевести рядок, припустимо, в нижній реєстру або побити по токенам, просто призведе в ступор. Сторонніх бібліотек, звичайно, багато, але сама юзабельность бібліотек це теж питання. Мови, у яких є метадані — ти бачиш там функцію і знаєш, як її використовувати, навіть документація з'явиться в комплишне. А в С++ в тебе може бути шаблонний аргумент типу Func, тобто функція, а сигнатуру функції ти можеш не зрозуміти, навіть якщо залізеш в сорцы. І незрозуміло, що з цим, власне, робити.

Загалом, якщо резюмувати, я б сказав, що обидві мови як би живі, і все залежить від того, що вас, власне, треба. В цілому, можна продуктивно писати і на тому і на тому. Щодо ліб, тут плюси програють, це всім ясно, причому дивно, оскільки мова начебто старіше, набагато, а бібліотеки ну, навіть якщо вони є, то з світу і не особливо юзабельны, або їх просто немає, і потрібно шукати десь ззовні, завантажувати, компілювати, і тільки потім використовувати.

Олег Краснов
— Чому?

— Коли я прийшов у компанію SEMrush, значних напрацювань внутрішньої логіки на інших мовах не було. На той момент я переважно програмував на З і вирішив розробляти продукт саме на цій мові. Я вірив у свої сили. =)
Для мене — це простий і зручний мову. При достатньому умінні і знанні бібліотек, він чудово підходить для прототипування розробок на рівні скриптової мови.
У нас, в компанії SEMrush, серед мов програмування серверної частини розподіл приблизно наступне: 1/3 — це С і С++, 1/3 — скриптові, 1/3 — Java.

— Java і С, який зараз по-вашому мову найбільш затребуваний? Обидва з них вже повнолітні, однак хто більш зрілий і відточений?

— Мій досвід говорить про те, що на мові С варто розробляти речі, які належать до продуктивних завдань. Наприклад, це робота з сокетами, мультиплексування даних, високонавантажених багатопотокові програми, де можна і потрібно максимально повно управляти ресурсами комп'ютера.
У компанії SEMrush немає явного поділу мов програмування по зонах їх застосування. Якщо потрібно почати новий продукт, то вибір тієї чи іншої мови залежить від професіоналізму людини, яка починає розробку архітектури та програмування. А також від того, наскільки він комунікабельний і здатний донести до колег ідеї, які хоче втілити.
Досить часті завдання для нас — це збір і обробка даних. Серед причин, чому, наприклад, ми не використовуємо Java на всіх продуктах, так це те, що у нас в плані сутностей немає дикого спадкування. Глибина його, в силу специфіки нашої роботи, у вертикальній площині менше, ніж у горизонтальній. Тобто велика кількість даних скоріше буде передаватися між незалежними сутностями, ніж між батьками і нащадками.

— чи Має сенс розробляти окремі модулі системи на різних мовах, заточуючи їх під конкретні завдання?

— Я вважаю, що має. У цьому плані у нас дуже добре все влаштовано. Розробку ведуть невеликі групи по 5-6 осіб, кожна з яких працює над своїм продуктом, а взаємодія між ними здійснюється через API. І користувальницький інтерфейс, і сервіси між собою повинні добре взаємодіяти. Це здійснюється, наприклад, за допомогою таких форматів даних, як JSON і Binary-JSON. Тому так, можна використовувати різні мови для написання цілої системи.

У нас є наша власна БД, написана на С, і великих проблем з її роботою не спостерігалося. Коли я займався розробкою архітектури цієї бази, не було готових адекватних засобів, які б підійшли. Дана БД працює на звичайних файлах і, з урахуванням цього, досить надійна. Якщо виключити з розгляду всі заходи щодо забезпечення її безперебійної роботи (резервне копіювання, кластеризація), то навіть «відвалився» диск (неполадки в обладнанні або непередбачені технічні причини) не завдасть великої шкоди, буде втрачено не більше 8.5% даних. Тобто ймовірність того, що користувачі постраждають, додатково знижена на рівні бізнес-логіки. Але в цілому система побудована таким чином, що дані не втрачаються. Все дуже надійно.
Ми проводили тести з максимальної утилізації продуктивності «заліза» з 12 дисками. Якщо використовувався RAID5, то швидкість читання/запису була приблизно 2.5 х швидкості одного вінчестера. Але наша система використовує 12 дисків роздільно на рівні бізнес-логіки, це дозволяє нам досягти 11-кратного зростання швидкості за рахунок того, що кожний потік працює зі своїм диском.

— Як у цілому відчувають себе мови в Enterprise?

— У нас є великі продукти. Наприклад, один з них обходить мережу Інтернет і створює базу посилаються один на одного сторінок сайтів. Ядро написано на С, що дозволяє утилізувати «залізо», практично на всі 100%. У цьому продукті задіяно понад 150 серверів, але такий підхід дозволяє бути впевненим в тому, що ми не переплачуємо за серверний парк, а нас, як ви розумієте, турбує фінансова ефективність. Окремо зазначу, що завдяки agile процесів розробки, у нас є час на доставку нових можливостей користувачеві, так і на покращення продуктивності кожного продукту.

— Які найбільш часті проблеми продуктивності бувають при розробці Enterprise-систем та можливі способи рішення?

— Якщо чесно, особливих проблем не можу навіть пригадати. Якщо раптом не вистачає потужності, то використання асемблера, спеціальних бібліотек і зусиль висококласних програмістів дозволяє дуже швидко вирішити проблему. Але в 99% випадків у нас з продуктивними рішеннями проблем немає.
У мене немає упереджень щодо Java, але вона більш вимоглива до ресурсів. Так, з її допомогою дуже зручно вирішувати завдання зі складною багаторівневою бізнес-логікою, будувати взаємопов'язані системи. Однак такого роду завдання, як мережеві взаємодії, багатопоточне програмування або великі двійкові дані, на мій погляд, більше підходять для С. На Java це теж можна зробити, однак я б вибрав С.

— Яка по-вашому повинна бути «ідеальна» БД? Варто морочитися з продуктивністю додатків, якщо вся міць системи нівелюється транзакціями до БД і як це можна виправити?

— Ідеальна БД не універсальна. Вона робиться для певної програми і повинна добре відповідати його бізнес-логікою. Так, її складніше спроектувати і простіше скористатися готовими рішеннями, але такий підхід дозволяє «драматично» збільшити продуктивність.

— Зараз дуже модно працювати з великими даними, для задач обробки великих масивів, що краще підходить — Java або С++?

— Великі дані — це Святий Грааль для програмістів, і для кожного вони виглядають по-різному. Для когось це петабайты, а для когось- экзабайты. На початковому етапі обробки таких даних існують великі вимоги до рівня заліза. Тому найбільш ефективно для їх первинної обробки використовувати мову С.

— Наскільки динамічна екосистема Java і C++? Наскільки часто виходять оновлення, релізи, стандарти? Наскільки живі мови (наскільки багато мовних особливостей з'являється)?

— У нас використовується стандарт С99, і з тих пір нічого нового, швидше поліпшення компілятора більш важливі. Ускладнення мови не завжди добре, на мій погляд. У останніх стандартів З++ — С++14 С++17 (draft) спостерігається така кількість нових фіч, що не ясно, чи має сенс використовувати більшість з них. Я розумію, що програмістам подобається розвиток мови. Однак особливості та зручності потрібно правильно використовувати. Гонка за фічами як самоціль неефективна і часто шкодить основної ідеї продукту.
Періодично я спостерігаю використання С++ як З класами або навіть як З без класів з деякими контейнерами З++. Це нераціонально. Виходить каша. Якщо планується використання С++, слід уважно вивчити його особливості і використовувати їх найбільш повноцінно.

Володимир Ситніков
— Що мови можуть дати в світі високонавантажених серверів? Має сенс розробляти окремі модулі системи на різних мовах, заточуючи їх під конкретні завдання? Якщо б могли (захотіли або просто була б можливість), стали б використовувати З++ для вирішення завдань або ж робили все тільки на одній мові?

— У світі ентерпрайз додатків подібне розділення зустрінеш не часто. Цікавий фактор — це підтримка програми. Якщо в програмі використовуються 10 різних мов і, по суті, 10 різних екосистем, то підтримувати таке дуже складно. У підсумку, якщо програму пише одна компанія, а розгортає, використовує і підтримує інша, то вкрай важливо зберігати універсальність і простоту супроводу. Тут навіть використання двох різних мов вже може сильно підвищувати поріг входження для підтримки. Другий важливий фактор — зручність аналізу проблем. Наприклад, якщо java програма вирішила прочитати і зберегти многогигабайтный файл в пам'яті, то без проблем можна дізнатися який файл, чому і т. п. У випадку C++ подібний аналіз зробити набагато складніше. У підсумку, для ентерпрайз розробки java виступає не тільки зручною платформою для розробки, але й у багатьох випадках забезпечує прийнятну продуктивність.

— Використання Unsafe в Java, виправдано чи ні? Чому б тоді не використовувати З++?

— Наївно вважати, що Unsafe в java потрібен (використовується) для того, щоб виділяти і звільняти пам'ять в обхід збирача сміття. Від хорошого життя unsafe коді java існує багато об'єктів. Наприклад, в java OpenJDK.util.HashMap використовує проміжні Map.Entry об'єкти, які не зберігають самі дані, а зберігають лише посилання на ключ і значення. Така реалізація не тільки збільшує накладні витрати на зберігання, але і уповільнює роботу, оскільки звернення до пам'яті у випадковому порядку завжди складніше і повільніше, ніж послідовне звернення. В силу семантики мови, у багатьох випадках javac і JIT-компілятор змушені залишати цю купу дрібних об'єктів.

Якщо ж подивитися на C++, то там хеш-таблиці реалізовані без зайвих entry об'єктів. Дані зберігаються в пам'яті компактніше, і значення зберігається поруч з ключем. С++ дозволяє бути ближче до заліза.

Що ж робити java програмістів? Переходити на темну сторону Unsafe, off-heap і іже з ним? В першу чергу, потрібно провести вимірювання, і переконатися в тому, що проблема для вашого випадку реально існує. Більшість типових java додатків цілком стерпно працюють з простими HashMap під капотом.

Якщо ви «професіонал на закритому треку», то можете спробувати таке (Unsafe, VarHandlers, memory mapped files тощо). Якщо для вас проблема дійсно важлива, то варто брати участь в обговореннях JEP 169: Value Objects — там ведеться дискусія про те, як згладити проблему зайвих об'єктів в Java без шкоди надійності коду і швидкості розробки.

— Які найбільш часті проблеми продуктивності бувають при розробці Enterprise-систем та можливі способи рішення?

— Типові проблеми продуктивності як правило викликані не самими мовою, а то як різні програми взаємодіють між собою. Наприклад, це повільні SQL-запити. Тобто неефективна робота з БД. Якщо система виконує тисячі запитів, то продуктивність залежить не від мови, а від використовуваних алгоритмів. «Правильне» ТЗ і повнота нефункціональних вимог дозволяють вирішити багато потенційні проблеми. Ця проблеми виникає не тільки при використанні БД, але вона не зважується сама собою при переході до микросервисам. І при не правильній гранулярности API цих микросервисов у нас на рівному місці може виникнути 1000 звернень і швидкість роботи визначатиметься швидкістю мов, а кількістю і якістю цих звернень.

— Що ви можете сказати з приводу швидкого виходу JDK 9 і його головною фічі модульності? Це спроба вирішити певні складнощі, з якими стикаються розробники на Java?

— При використанні Java однієї і незручних ситуацій є довгий запуск програми. Наприклад, при запуску серверного додатка контейнера може знадобитися 30 і більше секунд, залежить від заліза і кількості завантажених бібліотек.

А чому Java-машина кожен раз при запуску заново збирає всі класи програми? Чому вона не може переиспользовать вже нещодавно згенерований машинний код? Склад .class файлів може оновитися і JVM не має жодних гарантій щодо складу класів програми. Модульність в JDK9, на перший погляд, програмісту нічого не дає, а на другий погляд виявляється, що вона дає дуже багато самій java-машині. Java-машина може використовувати більш сміливі припущення про код, який може виконуватися, і таким чином модульність відкриває можливість прискорення часу запуску.

Більш прозаїчна користь від модульності – зменшення розміру дистрибутива java-машин. Навряд чи розробники стрункими рядами підуть створювати свої збірки JVM заради економії декількох мегабайт, але в ряді випадків можливість буде вельми до речі.

— Якщо дуже грубо подивитися, то різниця між C++ і Java — в прошарку runtime, яка, крім іншого, виконує всілякі оптимізації?

— Одна з ключових можливостей Java — можливість роздільної компіляції коду без втрати продуктивності для кінцевого програми. Наприклад, java-бібліотека може працювати з інтерфейсом Iterator. Так, при компіляції бібліотеки у байткод там будуть «віртуальні» виклики (invokeinterface). Але це не заважає отримувати хорошу продуктивність, якщо за фактом використовується одна і та ж реалізація Iterator в конкретному місці нашої програми. JIT-компілятор бачить, які за фактом об'єкти використовуються, і він генерує машинний код без зайвих пошуків «а де у переданого об'єкта реалізований метод hasNext». В результаті програміст пише зручний код на інтерфейсах, а при виконанні весь ітератор перетворюється в один-єдиний регістр процесора, зберігає «поточну позицію».

Якщо ж говорити про C++, то, якщо бібліотеку скомпілювали і бібліотека виконує віртуальний виклик, то все, у вас немає шансів зробити виклик не віртуальним.

В цілому, один з найдієвіших способів оптимізації — виключати зайві дії. Для того щоб C++ бібліотеки «бачили» особливості їх використання, програмістам C++ доводиться робити різноманітні присідання (clang LLVM, завжди компілювати всі бібліотеки разом з кодом програми тощо).



На чиєму ви боці?
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.