Как стать автором
Обновить

Комментарии 149

Спасибо, хороший анализ, пару комментариев по статье.


В бенчмарке транзакции не нужны и код написан с учетом того, что транзакций не будет. На практике они случаются.

Я сейчас работаю на проекте, которму скоро 5 лет будет, и за всё это время мы только сейчас начали задумываться что возможно нам нужны транзакции в некоторых новых сценариях. До этого за всё это время они были не нужны. А вот вопросы производительности нас интересуют, поэтому нам такой режим работы как раз-таки очень подошел бы. Я не говорю, что так везде — у меня была куча проектов где без транзакций никак, но требования — разные везде, и отсутствие транзакций — не самая редкая вещь которая может случиться.


Выглядит всё как типичный запуск параллельных процессов. Но, поскольку используется одно соединение с PostgreSQL, запросы к серверу базы данных отправляются последовательно. Один за другим. Как и требуется. Никакого криминала.

Это вообще одна из основных оптимизаций, я когда провожу ревью всегда обращаю внимание на await в цикле и прошу переписать на формирование массива запросов и единоразовое ожидание всех ответов. Это базовая оптимизация, и учитывая что она еще и идеологически правильнее, то не знаю в чем тут может быть претензия. Только так и надо посылать массив запросов. Может понадобиться побить на чанки, чтобы не заддосить шлюз, но принцип остается тем же.


Количество обновляемых записей переменно, подготавливать (PREPARE) запрос заранее нет смысла. Поскольку данные для обновления — числовые, и источнику можно доверять (сам код теста), то риска SQL injection нет, данные просто включаются в тело SQL и всё отправляется по протоколу Simple Query.

Риска SQL injection нет не потому, что тесту можно доверять, а потому что все нормальные ORM умеют эскейпить аргументы чтобы этого не произошло. Поэтому воспользоваться этим чтобы вместо расширенного (и не нужного в большинстве простых сценариев) протокола использовать сокращенный — совершенно логично.


Открывается всего одно соединение с PostgreSQL на поток веб-сервера. В этом соединении используется конвейерный режим, позволяющий эффективно использовать его для параллельной обработки пользовательских запросов.

Это аргумент в пользу того, чтобы научить приложения работать с одним соединением вместо пула, а не ругать актикс что они там считерили с соединенями.


А еще интересно как изменятся результаты тестов после переработки их на async/await.

Если это сильно повлияет на производительность — от этого могут отказаться.

Это аргумент в пользу того, чтобы научить приложения работать с одним соединением вместо пула, а не ругать актикс что они там считерили с соединенями.

При работе с одним соединением долгий запрос к базе разве не будет блокировать все остальные запросы к базе? Хотя тут больше вопросы к «критерии, которым должен удовлетворять код, участвующий в тестах».
Риска SQL injection нет не потому, что тесту можно доверять, а потому что все нормальные ORM умеют эскейпить аргументы чтобы этого не произошло.

Вообще в тестах Techempower значительная часть участников имеет в графе ORM «Raw» то есть встроенной проверки на инъекцию не имеют. Но опять же тут вопросы к авторам тестов. Где запросы по id = «105; DROP TABLE Main --», где кривые запросы отчетов на несколько секунд? Где хотя бы не вовремя запустившийся вакуум?

Для защиты от инъекций не нужен никакой ORM. Нужно просто использовать placeholder-ы которые предоставляет либа БД и не клеить запросы вручную.

Орм не нужен для защиты, это просто еще один плюс от его использования. У меня была пара проектов где было запрещено использовать орм, и писались мегатонны хрупчайшего кода уровня


object[] result = command.ExecuteNonQuery();
int i = 0;
return new Foo {
   Id = (long) result[i++],
   UserName = (String) result[i++],
   Gender = (Gender) (byte) result[i++],
   ...
}

Который ломался чуть ли не каждую неделю потому что где-то либо тип столбца поменялся, либо их порядок немного нарушился.


Так что скажу наоборот: лучше использовать возможности ОРМ и получать бесплатную производительность за счёт использования облегченного протокола запросов.

Для защиты от инъекций не нужен никакой ORM. Нужно просто использовать placeholder-ы которые предоставляет либа БД и не клеить запросы вручную.

От инъекций можно защищаться по разному, тут вопрос в том что эта защита с точки зрения производительности может иметь какой-то оверхед. И в тестах было бы не плохо это отразить. Потому что если этого не требовать, то фреймворки без встроенной защиты будут иметь преимущество в тестах. Хотя в реальных приложениях (где эту защиту допишут) преимущества не будет.
В принципе у TechemPower есть колонка с типом ORM (Raw, Micro и Full) и возможно сравнивать имеет смысл только внутри одного класса.

Мне кажется что эскейпинг контрольных символов в переменных это не самая трудоемкая задача. Общение с БД, сетевое и дисковое лэтенси, вот это всё будет на порядки сильнее влиять на скорость работы.

Таки способно, до тех пор пока представления сервера и клиента об используемой кодировке согласованы.

При работе с одним соединением долгий запрос к базе разве не будет блокировать все остальные запросы к базе? Хотя тут больше вопросы к «критерии, которым должен удовлетворять код, участвующий в тестах».

Ну на картинке видно что запросы по одному соединению продолжают идти пока ответ ответ еще не получен, так что долгий запрос ничего тормозить не должен. Про трейдофы разных режимов открытия соединений думаю лучше посмотреть в документации постгреса, но по крайней мере на нагрузках схожих с бенчем я бы ожидал профита именно от такого формата.


Вообще в тестах Techempower значительная часть участников имеет в графе ORM «Raw» то есть встроенной проверки на инъекцию не имеют. Но опять же тут вопросы к авторам тестов. Где запросы по id = «105; DROP TABLE Main --», где кривые запросы отчетов на несколько секунд? Где хотя бы не вовремя запустившийся вакуум?

Ну, создайте им issue, возможно они учтут ваши пожелания.

Ну на картинке видно что запросы по одному соединению продолжают идти

Идут запросы, но не ответы, которые придут только последовательно. Если я правильно понимаю идею, то здесь борьба за то, чтобы пропихнуть в сокет сразу несколько запросов, не дожидаясь ответа на первый. Это не значит, что база эти запросы все параллельно выполнять начнет. Они их выполнит так же, как если бы слали по одному. Поэтому таки получается, что запросы друг друга блокируют. И если ответ на первый запрос идет долго, то все остальные будут его ждать. Я очень сомневаюсь, что постгре на уровне протокола может в то, что умеет HTTP2, когда в одном TCP соединении несколько логических каналов.

Ну, собственно я и сделал референс на документацию постгре. Но вывод остается прежним: по крайней мере на нагрузках схожих на бенчмарк результаты будут тоже схожи.

Все равно очень странно делать такую хрупкую схему. Это получается, что два параллельных клиента к нашему HTTP серверу, допустим, пойдут через одно PG соединение. И если один из запросов какой-то кривой, и ответ на него долго идет, то повлияет это на оба HTTP запроса. Между клиентами все таки должна быть изоляция хоть какая-то.

Кто бы ещё на стороне СУБД такую изоляцию сделал. Я регулярно наблюдал как сложный запрос останавливает сервер. Хотя это была вина не столько СУБД, сколько админов, разместивших СУБД и сервер приложений вместе.

Если запрос на чтение, то это еще можно как то представть, а вот если запись, то скорее всего запросу в какой то момент нужна будет блокировка, как ни крути.

(и не нужного в большинстве простых сценариев)

Почему ненужного? Я уже давно не следил за развитием баз данных, но много лет назад параметрические запросы использовали не сколько для защиты от иньекций, сколько для производительности.
Когда на сервер приходит запрос, он делает следующее:
1. Смотрит, есть ли такой запрос в кеше запросов, если есть но параметры другие, берет план запроса ис кеша и исполняет под новые параметры. Если нет, то идет дальше:
2. Парсит запрос
3. Составляет план выполнения запроса, основываясь на правилах, на статистике индексов и тд.
4. Выполняет запрос.
5. Помещает его в кеш запроса, а т.к. кеш не резиновый, то оттуда выкидывается что-то, что лежало раньше.

Если мы не используем параметрические запросы, то для сервера все запросы будут всегда разные и он всегда будет делать шаги 1-5. Если использовать параметрические запросы, то на горячей базе чаще будет выполняться только шаг 1.

Ну я не исключаю, что мое понимание устарело и сервера теперь парсят запросы выкидывая из них значения и заменяя их параметрами перед тем как работать с кешем запросов и теперь нет большой разницы: параметрический запрос, или нет, но есть сомнения этому.

Ну, тут я могу ответить классикой — чтобы оценить производительность нужно проводить профилирование. Я уверен, что есть запросы, которые от параметричности только выигрывают. Не спорю.


Но увы, без профилирования те которые выигрывают от тех которые проигрывают отличать не могу.

Тот же EF, насколько я помню, запросы по дефолту делает parameterized. Так, что, видимо, подавляющее большинство запросов выигрывает.
Все верно вы понимаете, simple query подойдет когда у вас запросы динамические. Если же запрос один и тот-же, то потенциал simple query для такой ситуации будет сильно ниже.
По мере развития техники компы становятся быстрее, размеры СУБД — больше, а скорость дисков настолько сильно не растет (кроме скачка с SSD). В итоге чем дальше — тем меньше времени занимает компиляция и составление плана.

Если использовать параметрические запросы, то на горячей базе чаще будет выполняться только шаг 1.
И шаг 4, занимающий 99% времени.
Горячая база потому и горячая, что частоиспользуемые данные уже лежат в кэше в озу, а не на диске.
Спасибо, отличный комментарий!

Это аргумент в пользу того, чтобы научить приложения работать с одним соединением вместо пула, а не ругать актикс что они там считерили с соединенями.

Да!

Вальяжное вступление, возможно, сбивает с толку. Мне НРАВИТСЯ actix, я в восторге от того, насколько уместны, точны и осмысленны те технические решения, которые я увидел в коде.

В бенчмарках actix нет чита. Есть условие проведения бенчмарка — запросы к БД должны выполняться последовательно. В тексте я процитировал код, который может человеком, привыкшим к работе с пулом соединений, восприниматься как параллельный. Это не так.
>Хитростям бледнолицых нет предела.
Вообше-то, один из старейших движков javascript, написанный в Mozilla, и именуемый Rhino, написан именно на Java, и генерирует байткод в том числе. И этому решению сто лет в обед (а точнее, оно 1998 года). И оно широко использовалось, например, на нем пишут в IBM BPM.
Написан он был, вроде, еще нетскейпом, а потом — передан мозилле.
Все-таки, темное было время — перевод всего на джаву… сейчас даже не верится, что когда-то всерьез считали, что однажды джава «захватит мир» и другие языки окажутся практически ненужными…
Любой язык без GC. Серьёзно.

Applе не зря попробовала GC и выкинула.

Потому что GC (настоящий GC, без «счётчика ссылок») непредсказуем.

Rust вообще безумно интересен вот именно тем, что его модель работы с памятью позволяет обходиться без GC — и при этом не требует кучу времени на отладку (кто-то описал вот это вот всё, как «compile-time GC», но, конечно, это не «compile-time GC», это «compile-time ARC»).

GC вполне предсказуем если ты понимаешь как он работает и какие паттерны усложняют его работу.


На том же Go можно при желании писать код, который практически не будет вызывать аллокаций в рантайме и всё равно это будет проще чем раст или плюсы. Со сравнимой производительностью.

GC вполне предсказуем если ты понимаешь как он работает и какие паттерны усложняют его работу.

Угу, все возможные GC, с которыми приложение может работать. И со всеми возможными настройками.


На том же Go можно при желании писать код, который практически не будет вызывать аллокаций в рантайме и всё равно это будет проще чем раст или плюсы. Со сравнимой производительностью.

А можно привести пример? Обычно Go без аллокаций выглядит как мешанина из unsafe.Pointer, в которой за деревьями сложно разглядеть лес.

Угу, все возможные GC, с которыми приложение может работать. И со всеми возможными настройками.

Это в зоне ответственности разработчика\архитектора — задавать требования к среде выполнения. Чем больше вариантов — тем больше неопределенностей.


Я бы и сам рад писать на языке без GC с удобным синтаксисом и прочими прелестями Go. Но я таких не знаю.


А можно привести пример? Обычно Go без аллокаций выглядит как мешанина из unsafe.Pointer, в которой за деревьями сложно разглядеть лес.

Эээ, пример чего? Любой программы, которая не аллоцирует? :)
Ну, по сабжу текущей статьи — https://github.com/valyala/fasthttp, он не делает аллокаций на входящие соединения в отличии от net/http.


Из общих паттернов — активно юзать sync.Pool и статические буферы. Иногда необходимо прибегать к unsafe, да. К примеру, самое банальное и больное место — zero-allocation преобразование []byte в string:


func unsafeString(b []byte) string {
    return *(*string)(unsafe.Pointer(&b))
}

Криво и косо, я согласен. Но оно необходимо, судя по всему, потому что спецификация языка обещает что string — immutable.


На моей памяти это единственный unsafe, который я использовал.

Я бы и сам рад писать на языке без GC с удобным синтаксисом и прочими прелестями Go. Но я таких не знаю.
Python. Внезапно.

Да, там есть GC — но он там используется как «подушка безопасности», когда ARC не срабывает. Не доводите до этого — и будет вам «щастя».

Не смешно :) Я на нем тоже много пишу, но когда не нужна производительность, а важно удобство и понимание кода менее опытными товарищами.


Из недостатков для меня:
1) Динамическая типизация — ад с рефакторингом. Для этого придумали всякие Type Hints которые пытаются превратить его в "статический" язык. Линтеры немного помогают, но только немного.
2) Нет нативных конкурентных примитивов вроде горутин\каналов в Go
3) Геморрой с деплоем и зависимостями. Если в Go я собираю статичный бинарь и деплою его с systemd-юнитом рядом, то в Питоне… такой зоопарк. Ну, недавно статья тут была на эту тему. Немного помогает fpm, которым я собираю в rpm/deb PIP-зависимости автоматом в Makefile.
4) Собственно скорость работы. Тут речь не о GC, а вообще. Всякие PyPy не щупал, но стандартный питон сильно проигрывает.

Python. Внезапно.

Питон это ад с его сломанной concurrency, ужасающей производительностью, рантайм ошибками и прочими прелестями.

Не доводите до этого — и будет вам «щастя».

Точно так же можно сказать про любой GC. Аллоцируйте меньше в куче и будет вам счастье. Кстати, это актуально даже для не GC языков. Аллокации в отсутствии GC очень дорогая штука.
Аллоцируйте меньше в куче и будет вам счастье.
К сожелению в Java-подобных языках это почти невозможно. Какой-нибудь банальный printf будет создавать сотни объектов в куче — хотите вы того или нет.

Аллокации в отсутствии GC очень дорогая штука.
И именно поэтому printf в C обходится без них.

Аллоцируйте меньше в куче и будет вам счастье.
К сожалению счастье это — неизмеримо. В буквальном смысле: вы можете что-то сказать про конкретную реализацию, но никаких гарантия язык вам не даёт.

Вы можете изучить ваш конкретный рантайм и именно его и поставлять с вашей программой, но… вам мало Electron? Вы реально хотите к каждой утилитке на 100 килобайт прикручивать 20-мегабайтный рантайм?

Языки с GC — это огромная ошибка индустрии…

Питон это ад с его сломанной concurrency, ужасающей производительностью, рантайм ошибками и прочими прелестями.
У него куча проблем, но вот конкрено работа с память у него сделана правильно.
но никаких гарантия язык вам не даёт.

Может давайте без голословных утверждений? C# и Go дают. У них есть value типы и выделение памяти на стеке. GC это никак не затрагивает.

Вы можете изучить ваш конкретный рантайм и именно его и поставлять с вашей программой, но… вам мало Electron? Вы реально хотите к каждой утилитке на 100 килобайт прикручивать 20-мегабайтный рантайм?

А при чем тут электрон? Я в основном пишу на Go сейчас. Рантайм там конечно место занимает, плюс статическая линковка, но я готов платить за это и получать взамен все то, что дает этот язык. Вот тащить за собой Java или C# рантайм, который обычно дохера весит, я уже меньше хочу. Но это и не те языки, которые задумывались для написания утилит и микросервисов, которые сотнями запускаются в каком-нить кубернетисе как у меня сейчас с моими Go сервисами. Базу поставить какую-нить нить на джаве почему бы и нет, их полно отличных.

У него куча проблем, но вот конкрено работа с память у него сделана правильно.

Нет, работа с памятью у него так же очень медленная и любой код, который сильно трогает память и критичен до скорости, выносят в С/С++ библиотеки. Только так питон может что-то быстро делать.
Может давайте без голословных утверждений?
Голословные утверждения — это у вас.

C# и Go дают. У них есть value типы и выделение памяти на стеке. GC это никак не затрагивает.
GC, если он есть в процессе, затрагивает всё и всегда. Потому что компактифицирующие GC и GC с несколькими поколениями могут в любой момент, по желанию левой пятки этого GC остановить ваш код, чтобы что-то, порождённое в другом треде почистить.

В Go, насколько я знаю, эта проблема не стоит очень остро — но это особенность текущей реализации. Язык, сам по себе, ничего не гарантирует. Более того — я даже в описании текущей реализации никаких подобных гарантий не наблюдал.

Если если вы возьмёте и запретите прерывания посреди вашей деятельности — вы сможете гарантиповать, что вся ваше система не зависнет? И я не видел никого, кто реально работал бы с рантаймом Go или .NET, кто бы утверждал обратное, но, возможно, вы уникум и проникли в тайны языка, о которых даже его разработчики не подозревают…
GC, если он есть в процессе, затрагивает всё и всегда. Потому что компактифицирующие GC и GC с несколькими поколениями могут в любой момент, по желанию левой пятки этого GC остановить ваш код, чтобы что-то, порождённое в другом треде почистить.

К чему это балабольство? Value типы не затрагивают GC, поэтому он может даже никогда не выполняться. В том же Go запуск цикла сборка происходит по превышению размера кучи. Если кучу не трогать сильно, то и GC работать не будет. GC никогда не запускается по «желанию левой пятки». Есть четкие критерии, почему и зачем он запустился. Этот миф распространяют только люди, которые не понимают их работу.

В Go, насколько я знаю, эта проблема не стоит очень остро — но это особенность текущей реализации. Язык, сам по себе, ничего не гарантирует. Более того — я даже в описании текущей реализации никаких подобных гарантий не наблюдал.

Текущая реализация такая, потому что это позволяет язык. Java невозможно реализовать без выделения в куче, в нем все кроме примитивных типов является ссылкой. Поэтому сейчас есть инициатива, чтобы добавить это в язык. В Go и C# есть структуры, которые передают по значению. Не имеет никакого значения, что и где не написано. Оно так работает и работает прекрасно.

Если если вы возьмёте и запретите прерывания посреди вашей деятельности — вы сможете гарантиповать, что вся ваше система не зависнет?

Какие прерывания? Чего? Процессора что ли?

Я вам советую одно. Перестать нести ерунду и прочитать про то, как работают GC. У вас в голове сплошная каша и полное непонимание темы разговора. Вам не нравится GC и то, чем он требует жертвовать — ради бога. Хотите обсудить, что и как работает — почитайте матчасть.
Не имеет никакого значения, что и где не написано.
Вот, собственно, основная разница в вашем с вами взгляде на мир. Вы исповедуете подход «ну вроде работет, сегодня, а если завтра реактор рванёт — ну починим как-нибудь, дело-то житейское».

Я, со своей стороны, хочу гарантий и предсказуемости.

В принципе оба подхода имеют право на жизнь, но в сегодняшнем миру уж очень много людей полагаются на авось и «даст бог — пронесёт».

Боюсь, что это вам нас всем ещё боком выйдет.

Какие прерывания? Чего? Процессора что ли?
Не обязательно процессора. Можно и sigblock вызвать — уже хорошо получается. Но процессора — гораздо лучше. Deadlock почти на 100% гарантирован.
GC, если он есть в процессе, затрагивает всё и всегда. Потому что компактифицирующие GC и GC с несколькими поколениями могут в любой момент, по желанию левой пятки этого GC остановить ваш код, чтобы что-то, порождённое в другом треде почистить.

Но ведь если у вас не ОС реального времени, то ваш процесс, написанный хоть на Rust, хоть на С/С++, система периодически останавливает, чтобы заниматься другими делами.
Неточно настолько, что становится неверным.
Потому что компактифицирующие GC и GC с несколькими поколениями могут в любой момент, по желанию левой пятки этого GC остановить ваш код
Не в любой момент, а при вызове аллокации. Нет аллокаций — нет остановок на работу GC.
Если если вы возьмёте и запретите прерывания посреди вашей деятельности — вы сможете гарантиповать, что вся ваше система не зависнет?
Не зависнет конечно, если хватит памяти. С чего вдруг ей виснуть?
Не в любой момент, а при вызове аллокации. Нет аллокаций — нет остановок на работу GC.
Вот только эти аллокации могут быть вообще в другом потоке, который занят совсем другой задачей.

Основная беда GC — это нелокальность. Когда несвязанные друг с другом вещи начинают влиять друг на друга.

Конечно malloc/free тоже этим страдают — но «масштаб бедствия» гораздо меньше: если ваш, конкретный, thread память не выделяет — то его никто не будет трогать.

Конечно современные GC тоже стараются так не делать… но помогает это только до некоторого предела.

Не зависнет конечно, если хватит памяти. С чего вдруг ей виснуть?
Вы так уверенно говорите, похоже, потому что никогда с подобным не сталкивались. Я — сталкивался. Регулярно. Не в случае с Go (он как-то на Android не прижился, несмотря даже на то что там система сборки на Go), но Mono — весьма популярен (особенно в Unity). И вот когда оно начинает собирать память, а один из потоков, по той или иной причине, занят своими делами и не отвечает — то пользователи наблюдают замершуюю программу и через какое-то время Android предлагает её «пристрелить, чтобы не мучилась».

И никакие «value types» этому эффекту не препятствие…

Mono и так далеко не самая лучшая среда выполнения, так в Unity ещё и безнадежно устаревшую версию форкнули. Ничего удивительного, что там сборка мусора лагает...

Уточнение хорошее, но сильно корректирует предыдущие утверждениями про «зависнет» и «запретить прерывание» =)

ОК, GC может приостановить все треды, где использовался GC. Но как выше замечали, остановка даже на 300мс это серверные объемы чистящейся памяти, типично меньше.
Так что ваш случай «не отвечает» надо бы расследовать сначала.

Я кстати под запрещением прерывания в данном контексте понимаю GC.disable() и аналоги
Я кстати под запрещением прерывания в данном контексте понимаю GC.disable() и аналоги
Это как раз строго противопложное действие. Работа с GC через «парадный вход».

Самое противное свойство GC — это как раз то, что он взаимодействует со всем подряд. Даже с потоками, которые с ним никогда в жизни не общались.

А уж два GC в одном процессе — это радость какая! И не говорите что так не бывает — тот же Unity на Android это прекрасный пример.

В общем я всё ещё придерживаюсь мнения, что когда «архитектурные астронавты» продвинули IT-индустрию на путь использования языков с GC — они сделали большую ошибку.

Rust — это шанс её, наконец, исправить. Небольшой… но какой уж есть.
Это как раз строго противопложное действие. Работа с GC через «парадный вход».
Остальные запрещения прерываний действуют одинаково на все программы, что с GC, что без. А через жопу черный ход работать с GC я плохо себе даже представляю как =)
Самое противное свойство GC — это как раз то, что он взаимодействует со всем подряд. Даже с потоками, которые с ним никогда в жизни не общались.
Это проблемы частной реализации, не более. Зачем?
А уж два GC в одном процессе — это радость какая! И не говорите что так не бывает — тот же Unity на Android это прекрасный пример.
Так делать нельзя, это ничем не лучше запрещенной тематики использования в проге и в dll двух менеджеров памяти, а точнее частный случай такого.
Остальные запрещения прерываний действуют одинаково на все программы, что с GC, что без.
Нет. Вот ни разу не так. Обычному аллокатору или ARC не интересно и не нужно знать — что там происходит в других потоках. Вся работа с памятью — локальная (tcmalloc, скажем, выделает пулы как раз под потоки и большинство аллокаций оттуду обслуживает).

Более того — если у вас будут части, написанные на разных языках — они могут использовать разные схемы работы с памятью… и это не вызовет никаких проблем.

Для GC это, в некоторых случаях необходимо. Соотвественно GC начинает влиять на потоки, где его деятельность не нужна и неинтересна.

Это проблемы частной реализации, не более. Зачем?
Потому что если он этого делать не будет, то возможна, например, ситуация, когда объект Java удерживает объект C#, а тот, в свою очередь, держит другой объект Java. Если GC не сможет эту цепочку отследить — то это добром не кончится.

Так делать нельзя, это ничем не лучше запрещенной тематики использования в проге и в dll двух менеджеров памяти, а точнее частный случай такого.
Я даже не знаю — смеяться тут или плакать. Вы вообще так, в принципе, в курсе, что если вы статически линкуете MSVCRT в вашу DLL'ку (а это вполне себе не редкость), то у вас в программе будет 2, 3, 10 менеджеров памяти? Что куча народу так делала 10, 20 и более лет назад… и сегодня делает.

Ну Ok, ладно, допустим мы приняли парадигму, что два GC в одном процессе у нас жить не должны. Годится.

Теперь мы хотим портировать программу на C# (ну или Go — неважно) под Android, где в каждом процессе уже есть GC от ART. Который ни про C#, ни про Go не знает или знать не будет.

Ваши действия?

А что этот GC от ART будет в программе на C# собирать?

Java-объекты, однако. Нет, понятно, что если у вас программы на C# никак с пользователем не общается, выглядит как чёрный квадрат Малевича и только батарейку потихоньку жрёт — то проблем не будет.

Но пользователи вряд ли оценят.

А вот если вас с самим Android'ом придётся общаться — то тут вы уже начнёте создавать разного рода Java-объекты (этот API в Android только в виде Java-API существует). И вам нужно будет как-то сделать так, чтобы эти объекты не исчезали пока они нужны C# программе.

Обычно тут без довольно «толстой» прослойки на C++ (или, сегодя, можно уже на Rust) не обходится.
Не вижу связи между прерываниями (какими, кстати?) и потоками.
Потому что если он этого делать не будет, то возможна, например, ситуация, когда объект Java удерживает объект C#, а тот, в свою очередь, держит другой объект Java. Если GC не сможет эту цепочку отследить — то это добром не кончится.
Мне не хватает силы эротических фантазий такое представить. Посмотреть бы на код.
Да и какое дело GC одного языка до объектов другого?
Теперь мы хотим портировать программу на C# (ну или Go — неважно) под Android, где в каждом процессе уже есть GC от ART. Который ни про C#, ни про Go не знает или знать не будет.

Ваши действия?
Наверное, посмотреть, на андроид реализацию GC в Cs и Go для Андроида соответственно.
В принципе, наверное я неправ, и вложенные GC вполне себе работоспособны, хотя напрашивается решение просто транслировать вызовы системному GC.
Да и какое дело GC одного языка до объектов другого?
Ну дык программа-то у вас одна! И если объект α (на Java — скажем «главная программа») имеет ссылку на объект β (на C# — скажем «главное окно»), который имеет ссылку на объект γ (снова на Java — скажем notificaion message)… то вам же нужно добиться как-то, чтобы ART Runtime не удалил «ненужный» ему более объект B. А на него ведь в «Java-мире» ссылок-то, идущих от корня, уже и нетути…

Мне не хватает силы эротических фантазий такое представить. Посмотреть бы на код.
Ну попробуйте глянуть в исходники Xamarin.Android.

хотя напрашивается решение просто транслировать вызовы системному GC.
О, это прекрасная идея, да. Начнём с нескольких постулатов.
  1. Все объекты в памяти, управляемой GC ART соотвествуют модели объектов ART
  2. Модель объектов ART нигде не зафиксирована и, более того, меняется от версии к версии Android
  3. Никаких value-объектов (о которых нам тут много хорошего рассказали и которые весьма важны для программ на C# и Go) в этой модели не предусмотрено

Как будем «транспилировать»?

В принципе, наверное я неправ, и вложенные GC вполне себе работоспособны,
Ну… истина посередине, на самом деле. Вложенные схемы ARC или просто malloc/free — более, чем работоспособны. Вот просто вообще никаких проблем. Они никак друг от друга не зависят и «глобальных знаний» о программе не требуют.

Два же GC можно, в принципе, подружить между собой — введя простслойку с malloc/free и разработав соотвествующий API. Это работает, но регулярно приводит ко всем проблемам, которые вы наблюдаете в системах с ARC (утечки памяти), malloc/free (memory corruption, deadlocks и прочее) и, разумеется GC (фризы и прочее) — и всё это одновременно.

Прекрасный, прекрасный, великолепный результат! Просто, что называется, «всё жизнь мечтали».
Никаких value-объектов (о которых нам тут много хорошего рассказали и которые весьма важны для программ на C# и Go) в этой модели не предусмотрено

Вот это это как раз совсем не проблема, просто поля value-объекта включаются в объект, его содержащий (ну или попадают на стек в качестве вроде как локальных переменных).

ну или попадают на стек в качестве вроде как локальных переменных
Вроде как локальные объекты в Java очень ограничены. Вы не можете просто так взять — и положить массив на стек.

Ну как бы уже тот факт, что IKVM существует, а ничего подобного в обраную сторону никто никогда не делал — говорит о много.

Впрочем и IKVM тоже уже Legacy, официальное решение — устраивать прослойки между двумя GC. Со всеми вытекающими.
Вроде как локальные объекты в Java очень ограничены. Вы не можете просто так взять — и положить массив на стек.

Но у нас и не Java.


Ну как бы уже тот факт, что IKVM существует, а ничего подобного в обраную сторону никто никогда не делал — говорит о много.

В обратную сторону там проблема с дженериками вылезает.


Но GC на дженерики пофиг. Сформировать нужную структуру в памяти, в предположении что спецификация появилась, гораздо проще чем придумать способ динамической генерации байт-кода в момент инстанцирования дженерика.

Сформировать нужную структуру в памяти, в предположении что спецификация появилась
Ну дык это предположение ведь неверное. Откуда, вдруг, внезапно, появится спецификация?

гораздо проще чем придумать способ динамической генерации байт-кода в момент инстанцирования дженерика.
А уж если вспомнить про то, что ART компилирует байткод во время установки приложения, а не после его загрузки… так и совсем «весело».

Но у нас и не Java.
Да, у нас ART. Который принимает Dalvik-байткод и компилирует его в нативное приложение во время установки.

Ну как? Полегчало? Стало вам легче вот в это вот транслировать CLR байткод?

А никакого друого интерфейса к GC у вас нету. Никаких API, позволяющих отделить его от ARTа нет и не будет — Гуглу это нафиг не надо.
ART будет работать с дотНЕТ приложением точно также, как с каким-нибудь нативным c++ приложением — через Java Native Interface без каких-либо попыток рулить тем, что там происходит внутри своим GC. Вся работа из C# с Java объектами точно также возможна только через unsafe с ручным управлением памяти, т.к. c# не знает о том, что там внутри того кода и какие там менеджеры памяти. Если вы на каких-то андроид проектах встечались с суперфризами, так что приложение приходилось вырубать, очевидно на тех проектах что-то было сделано совсем криво, ибо стопицод юнити игрулек в маркете как-то работающих без многосекундных фризов говорят о том, что это возможно.
Как все пессимистично.
Как будем «транспилировать»?
В доке в принципе написано.

Еще перед глазами у меня есть RemObjects, где вообще сделали прослойку и могут смешивать языки с разными моделями и для разных ОС.
Они кстати, уже и Go добавили — нет еще на титульной.
И, что характерно, ни один из этих проектов ничего под ART не транслирует.

Да, сделать виртуальную машину, поверх которой можно запустить и C++ и Go и Java и вообще всё, что угодно — как бы можно…

Вот только это делает проблемы с GC ещё более неприятными. Так как теперь, когда вы пишите программ на Go — вы должны учитывать не только особенности оригинальной реализации, но ещё и gccgo и RemObjects и прочего, прочего, прочего.

Да, можно вставть в позу и объяснять, что это у всех «неправильный» C#, а «правильный» — только под Windows (или, шире, там где .NET Core портировали)… но с C++ таких вещей — гораздо меньше.

Разные компиляторы могут геренить разный код, кто-то лучще, кто-то хуже, но разница там не «1ns vs 1s»…
Да, сделать виртуальную машину, поверх которой можно запустить и C++ и Go и Java и вообще всё, что угодно — как бы можно…
Я думаю, что сила вашей экстраполяции штучных имеющихся проблем на всю отрасль, не стоит продолжения разговора. RemObjects определенно не ВМ.

А Rust чем вам не подошел? Предвижу, что скажете — плохой синтаксис. Но чем именно он плох?

1) Да, большой фактор это многословный инопланетный синтаксис. Его, конечно, можно выучить, нужно лишь понять зачем. Я сейчас больше занимаюсь сетевым программированием, а не системным...


Я пытался трогать Rust пару лет назад, но не нашел для себя причин веских углубляться.


2) Опять встроенные в Go примитивы для конкурентной работы очень развращают и после горутин\каналов не хочется более сложных абстракций


3) Rust до сих пор очень активно развивается, более революционно чем эволюционно. Go же, наоборот, как мне кажется очень сильно пытается быть совместимым с 1.0 и новые фичи добавляют очень аккуратно.


В общем, пока у меня нет необходимости биться за каждый такт процессора — смысла усложнять и замедлять разработку я не вижу.


Ну и даже в случае системного программирования возможно лучше взять C++, хотя тут у меня нет четкого мнения — багаж легаси в плюсах огромен...

Я просто хотел уточнить, а в синтаксисе ли дело? Ну то есть вам не нравится синтаксис паттерн-матчинга в Rust (допустим) или сам паттерн-матчинг вам не знаком и непонятно зачем нужен? Просто Rust и Go по своим общеязыковым возможностям сильно в разных категориях ИМХО.

Я согласен что в разных. И возможностей в Rust гораздо больше судя по моему его поверхностному изучению.


Но я подхожу к выбору языка с довольно утилитарной точки зрения: мне нужен инструмент чтобы решить определенную задачу достаточно эффективно с точки зрения времязатрат и производительности.


Так что синтаксис тут не основная причина конечно же. На Go удобно писать сетевые сервисы — пишу на нем. Что-то удобно написать на Python? Пишу на нем. И так далее.


Насчет паттерн-матчинга: довольно интересная штука, на мой взгляд. Эдакий switch/case обросший кучей возможностей. Насколько полезны эти возможности на практике — сложно сказать, но, думаю, что применения найдутся. Также как и мелочь в виде type-matching в switch-ах Go часто бывает полезна.

Угу, все возможные GC, с которыми приложение может работать. И со всеми возможными настройками.

Поэтому в том же Go одна крутилка для настройки GC, который идеально подходит для целевого применения языка — backend.

А можно привести пример?

Go имеет value типы, поэтому писать без аллокаций там проще простого.
Go имеет value типы, поэтому писать без аллокаций там проще простого.
Хотя это на первый взгляд так, и можно в C#/D/Go аллоцировать на стеке или вызывать системный аллокатор, но…
когда пишешь что то побольше пруфофконцепта, то невольно становишься заложником фреймворков и библиотек, которые ес-но будут интенсивно дергать GC =(

Выход тут только иметь (или написать) набор библиотек, noGC — ready. Что недешево. Или обходиться вызовами libc/ os_api
В Go нет особо и вообще не принято пользоваться фреймворками, где черт ногу сломит что внутри творится. Зато можно взять HTTP сервер без аллокаций (fasthttp), логирование (zerolog). Язык дает инструменты, чтобы писать быстрый код, которому GC будет только рад. C# тоже value типы имеет, за что ему разработчики благодарны. Теперь ждут, когда Java научится.
Ну и это все при том, что надо постараться, чтобы заставить GC причинять тебе неудобства.
Ну и это все при том, что надо постараться, чтобы заставить GC причинять тебе неудобства.
GC причиняет боль и страдания когды вы пишите UI. Потому что психология.

Что это:
Нажали на кнопку — через 100мс результат.
Нажали на кнопку — через 99мс результат.
Нажали на кнопку — через 101мс результат.
Правильно — отличный, отзывчивый, интерфейс. Во всяком случае он таким ощущается.

Что это:
Нажали на кнопку — через 60мс результат.
Нажали на кнопку — через 120мс результат.
Нажали на кнопку — через 40мс результат.
Правильно — «глюки» и «тормоза». Во всяком случае они так ощущаются.

Даже если объективно вторая версия вроде как и лучше…

И вот с GC почти всегда получается второй вариант…

Ну:
1) На Go никто в своем уме UI не пишет :) Точнее, конечно, пишут, но это не его целевое применение скажем так
2) Паузы stop-the-world GC там сейчас уже меньше 0.5мс, что никто не заметит


Из других языков с GC UI, наверное, умудряются писать только на Яве и .Net, но у меня с ними мало опыта.

И вот с GC почти всегда получается второй вариант…

В нормальном low-latency GC пауза будет ниже 1мс. Go таковым обладает, Java два экспериментальных имеет.
Go имеет value типы, поэтому писать без аллокаций там проще простого.

Гм. А как мне быть, если мне нужно работать с некой последовательностью данных, длина которых неизвестна в рантайме? Передавать ссылку указатель на массив не получится, а слайс всегда выделяет память.

Эпл его выкинула, потому что видимо их реализация была говно. Современные GC очень далеко продвинулись с тех времен. Паузы невидимы, только процессорное время некоторое отбирает. Собственно, их новенький ARC этого процессорного времени отбирает еще больше.
Rust вообще безумно интересен вот именно тем, что его модель работы с памятью позволяет обходиться без GC
Ну, у Rust и Java несколько разные области применения. Для системной разработки GC обычно мешает, а для обычных бизнес-приложений практически идеален (даже без подсчета ссылок).
Для системной разработки GC обычно мешает, а для обычных бизнес-приложений практически идеален (даже без подсчета ссылок).
Бизнес-приложения можно вообще на чём угодно писать. Когда люди, платящие деньги, и люди, испольщующие программу — это разные люди, то можно творить вообще всё, что угодно, главное, чтобы специалисты-продажники были хорошие. Captive market — то такое.

Какой бы убогой ни была программа для оформления заказов в IKEA (или McDonald's) — а ей будут пользоваться если цены на мебель (или бургеры) будут ниже, чем у конкурентов.
Бизнес-приложения можно вообще на чём угодно писать
Обычно заказчики уже просят соблюдать и определенный стек разработки. Коммодизация стека так сказать. И не зря ЯП с GC там присутствуют в первую очередь.
И не зря ЯП с GC там присутствуют в первую очередь.
ЯП с GC там присутствуют ровно потому, что люди, с этим работающие — это не те люди, которые за разработку деньги платят.

Главное что позволяют сделать языки с GC — выпустить дерьмо… много дерьма… но дёшево.

Для бизнес-приложений, где деньги экономит бизнес, а мучаются рядовые исполнители — то, что надо.
Главное, что позволяет достичь GC — это корректность. Для бизнеса не так важно, что приложение работает чуть медленнее и кушает чуть больше памяти. А вот если у приложения течёт память или ручное управление привело к сегфолду, или, не дай бог, к повреждению данных?
Главное, что позволяет достичь GC — это корректность
Никогда не наблюдали XSS уязвимости? Сыплюшие во все стороны NPE? И прочее добро? На самых суперпупербезопасных языках?

От кривых рук нет защиты на уровне компилятлора, увы.

А вот если у приложения течёт память или ручное управление привело к сегфолду, или, не дай бог, к повреждению данных?
Вот почему-то у команд, пишущих на Java — память как раз постоянно течёт и с этим идёт непрерывная борьба. У С++, впрочем, чаще падения в SIGSEGV. Но рассказывать о том, что GC защищает как-то утечек памяти не стоит — практикой это не подтверждается…

Я думал не стоит пояснять, что я говорю о корректности в рамках управления памятью. Конечно, это никак не защитит от нпе и прочих ошибок, не очень понимаю к чему Вы это написали.


То, что gc защищает от утечек, в моей практике очень даже подтверждается. Java программы довольно редко текут по памяти, хотя, конечно, такое случается. Но это не повод полностью отказываться от защиты.
Сегфолд это ещё хороший сценарий, гораздо опаснее когда повреждают существующие данные.

Конечно, это никак не защитит от нпе и прочих ошибок, не очень понимаю к чему Вы это написали.
К тому, что не требуется большой дисциплины для того, чтобы не косячить при работе с памятью. У нас в модуле на C++ за последние несколько лет было найдена куча ошибок — но ни одна из них не было связана с ошибками выделения памяти.

Потому что управление памятью — это гораздо проще, чем работа с сигналами, внешними ресурсами и прочим.

Сегфолд это ещё хороший сценарий, гораздо опаснее когда повреждают существующие данные.
И, тем не менее, почти все web-сервера, базы данных, файловые системы и вот это вот всё — написаны на C++. Никто языки с GC туда тащить даже не пытается.

GC защищает только от простых ошибок, делает это плохо и тратит на это кучу ресурсов — если бы было иначе, то мы все давным-давно перешли бы на HotJava.

Соответственно ниша, где GC реально востребован — низкокомпетентные разработчики, которые не знают что творят… со всеми вытекающими.

Вы не правы. GC защищает от массы довольно сложнообнаружимых ошибок, и позволяет разработчику освободится от постоянного ручного управления.
Конечно, есть техники, позволяющие значительно упростить и обезопасить язык и без гц, но пока гц самый простой и всеобъемлющий способ.
При этом оверхед не слишком большой для большинства случаев.


Ниша, где GC востребован — огромна. Это и простые скрипты, и сложная бизнес логика, которая за вменяемые деньги поддерживается и развивается разными разработчиками на протяжении десятилетий.

сложная бизнес логика, которая за вменяемые деньги поддерживается и развивается разными разработчиками на протяжении десятилетий.
Сложная бизнес-логика и на COBOL прекрасно «поддерживается и развивается разными разработчиками на протяжении десятилетий» — без вского GC.

Не развивается, а только поддерживается. Не даром Java вытеснила его отовсюду, оставив только недобитый легаси.
Java не идеальный инструмент, но GC хорошо себя показывает, облегчая и удешевляя разработку и поддержку.

Java не идеальный инструмент, но GC хорошо себя показывает, облегчая и удешевляя разработку и поддержку.
Я в этом вот ни разу не уверен. Java позволяет нанимать условных «дешёвых индусов» и оставлять топ-менеджерам себе больше денег на бонусы… но вот в том, что она что-то там облегчает и удешевляет… я не уверен.

Специалист Java обычно как минимум на дешевле специалиста С++. И бизнес готов переплачивать в замен на (относительную)надежность и скорость разработки и поддержки.
Будьте уверены, когда у вас килотонна взаимодействующих компонентов разного качества, и вам нужно срочно добавить/изменить функционал, последнее с чем Вы захотите столкнуться — это ub, повреждение данных, segfault, утечки и прочие радости, которые неизбежно приносит С++. А что в замен? Скорость? Низкое потребление оперативки?
В типичной для бизнеса модели взаимодействия, скорость практически всегда упирается в бд, а Java и так довольно быстрая. Стоимость оперативной памяти несравнимо меньше стоимости работы, тут даже говорить не о чем.

В типичной для бизнеса модели взаимодействия, скорость практически всегда упирается в бд, а Java и так довольно быстрая.
А вы никогда не редактировали Wikipedia? Или хотя бы просматривали? И не сравнимали это с тем, как работает, скажем, программа по заказу товара в IKEA? Думаете покуптели IKEA делают больше заказов в секунду, чем посетители Wikipedia — правок в базе Wikipedia?

И бизнес готов переплачивать в замен на (относительную)надежность и скорость разработки и поддержки.
Бизнес вначале нанимает условных «дешёвых индусов», которые порождают ад в коде… а потом уже поздно.

Тут уместнее всего примерно такой диалог (сказанный в совсем другое время по совсем другому поводу, но суть передающий на 100%):
— Теперь еще один технический вопрос — имейте в виду, товарищ Клямер, технический, а не припнципиальный, — скажите, коротко только, почему вам нужно не сорок воспитателей, а пятнадцать, и почему вы против оклада в сорок рублей?
Я подумал и ответил:
— Видите ли, если коротко говорить: сорок сорокарублевых педагогов могут привести к полному разложению не только коллектив беспризорных, но и какой угодно коллектив.
Бритый вдруг откинулся на спинку кресла в открытом закатистом смехе и, показывая пальцем, спросил сквозь слезы:
— И даже коллектив, состоящий из Клямеров?
— Неизбежно, — ответил я серьезно.
Вот и условные «индусы» пытающиеся решать задачу методом добавления случайных мутаций в код с последующим прогоном тестов — могут привести код на любом языке в невменяемое состояние. Просто код с GC (и, особенно, на Java) дольше сопротивляется, вот и всё.

Будьте уверены, когда у вас килотонна взаимодействующих компонентов разного качества, и вам нужно срочно добавить/изменить функционал, последнее с чем Вы захотите столкнуться — это ub, повреждение данных, segfault, утечки и прочие радости, которые неизбежно приносит С++.
Не привносит. Всё, что нужно делать — не включать в вашу программу код написанный «индусами». Запускать их в другом процессе или вообще общаться по сети.
А вы никогда не редактировали Wikipedia? Или хотя бы просматривали? И не сравнимали это с тем, как работает, скажем, программа по заказу товара в IKEA? Думаете покуптели IKEA делают больше заказов в секунду, чем посетители Wikipedia — правок в базе Wikipedia?
Редактировал, вроде всё хорошо. Икеей не пользовался, что это должно мне показать?
Бизнес вначале нанимает условных «дешёвых индусов», которые порождают ад в коде… а потом уже поздно.

Такое бывает и часто. В чём Ваша мысль? Что не нужно нанимать индусов и всё будет хорошо? Это и так понятно, но в любом случае без ГЦ придётся потратить больше времени как на разработку, так и на поддержку. Без значимых преимуществ.
Вот и условные «индусы» пытающиеся решать задачу методом добавления случайных мутаций в код с последующим прогоном тестов — могут привести код на любом языке в невменяемое состояние. Просто код с GC (и, особенно, на Java) дольше сопротивляется, вот и всё.

Это правда, программы с GC «дольше сопротивляются». Однако, это не все его заслуги. Даже «идеальный код» с ГЦ проще писать и поддерживать по сравнению с «идеальным кодом» без него.
Не привносит. Всё, что нужно делать — не включать в вашу программу код написанный «индусами». Запускать их в другом процессе или вообще общаться по сети.
Мы сейчас не говорим о Вашем софте. Все уже поняли, что у Вас в принципе нет ошибок, и ГЦ лично Вам не нужен. Но в _любом_ другом софте на С++, написанным профессионалом любого уровня, неизбежно есть полный набор этих ошибок. Более того — чуть ли не половина уязвимостей ПО с ними связана.
Редактировал, вроде всё хорошо. Икеей не пользовался, что это должно мне показать?
Посмотрите если будете там. У них программа как раз на вот этих вот «бизнес-языках», где любая операция занимает секунд по 5-10. Притом, что данных в базе у них явно на порядок меньше, чем в Wikipedia.

И не нужно мне говорить про то, что «ведь IKEA нужна супернадёжность» — это нифига не так. Единственное, что должно происходить надёжно — это финальная «фиксация» заказа, когда он становится готовым к оплате… да даже и там это, на самом деле, не требуется: вы же можете заказать со стелажа товар, который оттуда кто-то уже забрал!

То есть фактически только учёт на кассе должен быть 100% надёжным… ну и тогда с какого перепуго оно всё тормозит нещадно?

Это и так понятно, но в любом случае без ГЦ придётся потратить больше времени как на разработку, так и на поддержку.
Совершенно неочевидно. ARC (и его аналоги в C++ и Rust) вполне достаточен для разруливания 99% проблем. А последний 1% и с GC всё равно проблемный.

Без значимых преимуществ.
Значимое преимущество — предсказуемое время работы и потребление памяти. Для GUI — это важно.

Даже «идеальный код» с ГЦ проще писать и поддерживать по сравнению с «идеальным кодом» без него.
Нет. Как я уже сказал: ARC, «умные указатели» и «арены» (в C++), borrow checker (в Rust) решают подавляющее большинство проблем.

А патологические случаи могут быть как в пользу GC, так и наоборот.

Но в _любом_ другом софте на С++, написанным профессионалом любого уровня, неизбежно есть полный набор этих ошибок.
А в Java их нет? Хотите сказать что никогда не получали документов, где фигурировали устаревшие данные или вообще данные от других людей?

Просто то, что в C++ приведёт, скорее всего, к падению — в Java приведёт к неверным результатам. Скажем если у вас где-то в памяти табличка с какими-нибудь курсами валют, но при обновлении ссылку на неё забывают обновить. В C++ будет «висячая ссылка» и программа упадёт, а в Java — она даст невероные результаты. И тут уж я не знаю что лучше.

Да, что выпадания «в кору» у C++ случается часто — все знают. А вот про альтернативу в Java (а это неверные рассчитаные результаты) — почему-то забывают…
То есть фактически только учёт на кассе должен быть 100% надёжным… ну и тогда с какого перепуго оно всё тормозит нещадно?
Не знаю, что у них там тормозит. В конце концов java сама программу не напишет — кто знает какой у них там код?
На Android всё нормально работает, идея тоже.
Значимое преимущество — предсказуемое время работы и потребление памяти. Для GUI — это важно.
Только enterprise это в основном не про GUI.
Нет. Как я уже сказал: ARC, «умные указатели» и «арены» (в C++), borrow checker (в Rust) решают подавляющее большинство проблем.
ну да, умные указатели, borrow checker. В каком-нибудь swift может быть. В С++ это не работает — т.к. нет единого интерфейса для взаимодействия библиотек.
А в Java их нет? Хотите сказать что никогда не получали документов, где фигурировали устаревшие данные или вообще данные от других людей?
Насколько я понимаю, это вообще не имеет отношение к перечисленным проблемам.

Просто то, что в C++ приведёт, скорее всего, к падению — в Java приведёт к неверным результатам. Скажем если у вас где-то в памяти табличка с какими-нибудь курсами валют, но при обновлении ссылку на неё забывают обновить. В C++ будет «висячая ссылка» и программа упадёт, а в Java — она даст невероные результаты. И тут уж я не знаю что лучше.
Что-то не очень понял. Если данные обновили(таблица мутабельная), то зачем обновлять ссылку? Если таблица иммутабельная, то что значит «обновили»? И да — С++ вообще всё что угодно может вернуть. И сегфолд это не то же самое что exception. Неожиданное завершение Java программы — недопустимо(речь о enterprise и серверах).
На Android всё нормально работает, идея тоже.
Запустите на процессоре где-нибудь в 200-300MHz, потом говорите. Аккуратно написанный C++ код на таком «летает».

Конечно если писать всё через задний проход, то тормоза можно устроить на любом языке.

В С++ это не работает — т.к. нет единого интерфейса для взаимодействия библиотек.
Это как?

Насколько я понимаю, это вообще не имеет отношение к перечисленным проблемам.
А к чему это имеет отношение? Вы вообще когда-либо видели хоть от кого-нибудь задание «сделать так, чтобы программы выдала чёрт-знает-что, не важно что, главное чтобы не упала»?

Нет же. Задачи пишутся для решения кокретных задач. И то, что в случае с C++ задача очевидным образом не решена (программа падает), а в случае с Java он неочевидным образом не решена (программа возвращает чушь)… так для бизнеса первый вариант предпочтительнее даже.

Неожиданное завершение Java программы — недопустимо(речь о enterprise и серверах).
Если у вас «enterprise» — это «нам нужно не дорого, а очень дорого», тогда да.

А если вы хотите дёшево, тогда проще разрешить программам падать и обеспечить надёжность с помощью дублирования. Ну и, конечно, каждая программа должна выполнять одну задачу.

То, что в enterprise до сих пор, зачастую, делается не так — это просто доведение принципа «работает — не трогай» до предела, до маразма, до апофигея. Ситуации, когда у вас, действительно, требуется, чтобы всё крутилось на одной машине редки, как единороги. В большинстве случаев всё упиратся в подход «так отцы и деды делали, так и мы будем делать».

Забавно только, что как раз «деды» отлично без GC обходились, а их «внукам» — он просто вот позарез необходим.
Запустите на процессоре где-нибудь в 200-300MHz, потом говорите. Аккуратно написанный C++ код на таком «летает».
А зачем? У меня есть замечательный телефон, на котором прекрасно работают приложения.
А к чему это имеет отношение? Вы вообще когда-либо видели хоть от кого-нибудь задание «сделать так, чтобы программы выдала чёрт-знает-что, не важно что, главное чтобы не упала»?
Вы, похоже, сами с собой спорите. Объясните нормально задачу. Из того что я понял: есть таблица, в ней поменяли данные. Почему должна была поменяться ссылка мне непонятно. Возможно, таблица иммутабельная, тогда что значит «обновление», если ссылка не изменилась?
В любом случае — здесь Java максимум вернёт устаревшие данные. С++ же сокорее вернёт устаревшие, может вернуть вообще рандом. Да и сегфолда тут не будет.
Если у вас «enterprise» — это «нам нужно не дорого, а очень дорого», тогда да.
Что это значит? Такое поведение по дефолту на Java сервере.
Т.е. Вы считаете что невозможность корректно обработать ошибку — преимущество С++?
А зачем? У меня есть замечательный телефон, на котором прекрасно работают приложения.
А у меня есть телефон, где они работают отвратительно, когда батарейка садится и скорость процессора снижается.

Объясните нормально задачу.
Это не задача, а решение. Задача простая: наш сервис перегружает сервер, раздающий, скажем, курс валют из-за того, что мы туда посылаем миллион QPS. Нас попросили это исправить. Так как курс валют меняется редко, то напрашивающееся решения — кеширование.

Дальше — беда: как известно в программировании есть только две сложные вещи: инвалидация кэша и наименование сущностей. Мы с первой проблемой не справились и продолжаем где-то держать ссылку на курсы валют, которые были загружены при первом образщении. А сервис обновления мастер-таблицу периодически в памяти обновляет и старые данные стирает.

Так вот в C++ в этом месте образуется «висячая ссылка», программа упадёт и мы её починим. А в Java — мы будет пересчитывать данные по курсу годичной давности, пока это аудит не заметит и нам штраф не выпишут…

В любом случае — здесь Java максимум вернёт устаревшие данные. С++ же сокорее вернёт устаревшие, может вернуть вообще рандом. Да и сегфолда тут не будет.
Сегофолт будет, когда fortify обнаружит «битые» данные.

Т.е. Вы считаете что невозможность корректно обработать ошибку — преимущество С++?
Как раз невозможность корректно обработать ошибку наблюдается в вашей «ынтырпрайз системе» на Java. Вот тут:
Неожиданное завершение Java программы — недопустимо(речь о enterprise и серверах).

Если ваша система не готова к том, что у вас вдруг, внезапно, отрубят целиком датацентр — то она ненадёжна. Потому что такое иногда случается. А если вы готовы к тому, что у вас, вдруг, исчезают из системы отдельные датацентры, сервера, и процессы — то падение процесса — не есть что-то страшное. Вот и всё.

К сожалению по историческим причинам (когда-то компьютеры миллионы долларов за штуку стоили, покупать в количестве 10x было невозможно) очень много «ынтырпрайза» построено именно как ненадёжные системы — с одним центральным уязвимым компьютером. Да, его стараются сделать понадёжнее, там могут даже иногда стоять несколько процессоров с горячей заменой и прочие чудеса — но всё равно это паллиатив. Потому что от пожара или, не знаю, Боинга в небоскрёб — это всё равно не защитит.

Что самое смешное — сделать надёжную систему будет ещё и дешевле, так как вместо IBM Z с космическим ценником вы можете использовать куда как более дешёвое железо. И да — оно будет дешевле даже с учётом многократного дублирования.
Дальше — беда: как известно в программировании есть только две сложные вещи: инвалидация кэша и наименование сущностей. Мы с первой проблемой не справились и продолжаем где-то держать ссылку на курсы валют, которые были загружены при первом образщении. А сервис обновления мастер-таблицу периодически в памяти обновляет и старые данные стирает.

Зачем держать множество ссылок, если можно ссылаться на один объект, и его модифицировать(или менять через прокси)?
Так вот в C++ в этом месте образуется «висячая ссылка», программа упадёт и мы её починим. А в Java — мы будет пересчитывать данные по курсу годичной давности, пока это аудит не заметит и нам штраф не выпишут…
С++ позволит Вам вычислить по прошлогоднему курсу, позапрошлогоднему, вообще любому курсу. Стало намного лучше?
Сегофолт будет, когда fortify обнаружит «битые» данные.
Мы читаем, какие битые данные?
Как раз невозможность корректно обработать ошибку наблюдается в вашей «ынтырпрайз системе» на Java. Вот тут:
Неожиданное завершение Java программы — недопустимо(речь о enterprise и серверах).
Никакой С++ вас от падения цода не спасёт.
Другое дело когда Ваша супер надёжая прога крешится и все в панике с дебагером в зубах начинают искать ошибку, а на Java в кибане появляется красноречивый стектрейс. И пока Вы ищите, бизнес теряет деньги.

Дальнейшие рассуждения(про железо) пропущу что-бы не разводить полемику(по моему мнению никакого отношения к реальности они не имеют).
Зачем держать множество ссылок, если можно ссылаться на один объект, и его модифицировать(или менять через прокси)?
Обычное RCU. Если вам не требуется, чтобы данные были верны именно на текущую миллисекунду, то гораздо проще разрешить продолжать использовать старые данные для тех запросов, которые начали исполняться во время получения обновления… а уже новые — будут идти с обновлёнными данными.

С++ позволит Вам вычислить по прошлогоднему курсу, позапрошлогоднему, вообще любому курсу. Стало намного лучше?
Да. Если, вдруг, компьютер выдаст кому-то, как у Хайнлайна, «чек на 10 000 000 000 000 185,15 долларов-купонов» — то, во-первых, по этому чеку никто никому ничего не выдаст, а во-вторых — это будет немедленно поводом начать разбираться в том, что у нас тут в системе происходит.

Мы читаем, какие битые данные?
Не влазящие в буфер, например.

Никакой С++ вас от падения цода не спасёт.
Это почему вы так в этом уверены? Вы когда-нибудь читали о том, что где-то данные Gmail стали недоступны из-за того, что ему кабель в Орегоне перерезали? Нет? А почему нет? Подумайте над этим.

И если ваш ответ: «этого не произошло потому что их датацентры никто никогда не отключал»… то ответ неверный. Мы точно знаем, что это случалось — просто потому что это тоже тестируется.

Другое дело когда Ваша супер надёжая прога крешится и все в панике с дебагером в зубах начинают искать ошибку, а на Java в кибане появляется красноречивый стектрейс.
Во-первых с чего вы решили, что когда программа закрешится — там не будет стектрейса. Во-вторых — какая паника? Запрос пошлют три раза, после чего тому, кто его инициировал, покажут сообщение об ошибке и всё. Три упавших сервера как легли, так и поднимутся.

И пока Вы ищите, бизнес теряет деньги.
Это извините, у вас он будет терять деньги если вы всё завязали на один instance одного сервера и он, несмотря на все костыли, упадёт. У нас — об инциденте никто и не узнает, кроме тех, кому нужно будет код чинить.

Дальнейшие рассуждения(про железо) пропущу что-бы не разводить полемику(по моему мнению никакого отношения к реальности они не имеют).
Ну да, ведь так же легко засунуть голову в песок и сделать вид, что проблемы не существует.
Да. Если, вдруг, компьютер выдаст кому-то, как у Хайнлайна, «чек на 10 000 000 000 000 185,15 долларов-купонов» — то, во-первых, по этому чеку никто никому ничего не выдаст, а во-вторых — это будет немедленно поводом начать разбираться в том, что у нас тут в системе происходит.
Ваш тезис звучит как: лучше врезаться с разгону в столб, чем приехать не туда. Что-ж — бейтесь, Вам никто не мешает.
Во-первых с чего вы решили, что когда программа закрешится — там не будет стектрейса. Во-вторых — какая паника? Запрос пошлют три раза, после чего тому, кто его инициировал, покажут сообщение об ошибке и всё. Три упавших сервера как легли, так и поднимутся.
Т.е. у вас один пользователь только что 3 сервера положил, и неизвестно сколько данных повредил. А что если ошибка начала возникать с какой-то периодичностью у всех пользователей? Вот Вам и ответ — почему Java.

Это извините, у вас он будет терять деньги если вы всё завязали на один instance одного сервера и он, несмотря на все костыли, упадёт. У нас — об инциденте никто и не узнает, кроме тех, кому нужно будет код чинить.
Вы сами себе это придумали. У меня не один инстанс, и даже если бы был один я бы(бизнес, который меня нанял) деньги бы не терял.
Это почему вы так в этом уверены? Вы когда-нибудь читали о том, что где-то данные Gmail стали недоступны из-за того, что ему кабель в Орегоне перерезали? Нет? А почему нет? Подумайте над этим.

И если ваш ответ: «этого не произошло потому что их датацентры никто никогда не отключал»… то ответ неверный. Мы точно знаем, что это случалось — просто потому что это тоже тестируется.
У Вас какое-то странное представление о бизнесе. Конечно, первое что предпримет средней руки организация — начнёт скупать ЦОДы по всему миру и нанимать С++-богов, которые наклепают им профессиональный софт, который крешится и повреждает данные на каждый чих.

И да — Java не только красноречивый стектрейс выдаст, а всю последовательность ошибок(caused by). Плюс можно отладочной инфы приложить — id сессии, активности итд.
А как у Вас это решается? Просто интересно стало, не для спора и не как аргумент.

Есть отладочные переменные, которые показывают что и где происходит. Они тоже попадают в лог (лог собирает другой процесс, так что ошибки в основном его данные повредить не могут).
Интересно. А это самописное решение, или есть какой-то специфический софт которым все пользуются?
Самописное и довольно старое. Я даже не знаю — пытались ли его открыть.
А что если ошибка начала возникать с какой-то периодичностью у всех пользователей?
Ну вот тогда уже начнут зажигаться красные лампочки и слаться SMSки.

Вы сами себе это придумали. У меня не один инстанс
Тогда откуда взялись ваши же слова про недопустимость падения?

и даже если бы был один я бы(бизнес, который меня нанял) деньги бы не терял
Опять двадцать пять. Если инстранс один, то их может, внезапно, стать и ноль — по совершенно независящим от вас причинам. И либо для вас это допустимо, либо нет. Если это недопустимо — то вам нужно дублирование. И проблем с C++ нет. Если допустимо… то допустимо — и проблем нет изначально.

У Вас какое-то странное представление о бизнесе. Конечно, первое что предпримет средней руки организация — начнёт скупать ЦОДы по всему миру и нанимать С++-богов, которые наклепают им профессиональный софт, который крешится и повреждает данные на каждый чих.
Знаете — эту сказку про белого бычка пора кончать. Про это Джоел уже тоже писал. Если вы — «средней руки бизнес» и не можете себе позволить качественный софт, то вы берёте условного «дешёвого индуса», Java и получаете… что-то. Оно кривое, косое, неудобное и глючит — но оно дёшево стоило, а это для вас главное.

Если вы можете себе это позволить и вам нужно качество — то вы нанимаете нормальных программистов и даёте им в руки C++ или MISRA C++ или вообще Аду.

Если бы все эти ужасы про C++ и мнимые преимущества GC и Java имели какое-то отношение к реальности — то мы бы все сейчас пользовались браузерами на Java (или C#) и подобными же Web-серверами. Потому что сегодня это — самые очевидные точки для атака на вашу безопасность на любом предприятии, независимо от масштабов.

Однако же реальность, в нашем мире — совсем другая. И выше отрицание очевидного ничего тут не изменит.
Если бы все эти ужасы про C++ и мнимые преимущества GC и Java имели какое-то отношение к реальности — то мы бы все сейчас пользовались браузерами на Java (или C#) и подобными же Web-серверами. Потому что сегодня это — самые очевидные точки для атака на вашу безопасность на любом предприятии, независимо от масштабов.

Однако же реальность, в нашем мире — совсем другая
Не знаю, в каком ты мире, но в моем почти все веб сервера — а именно логика — на GC — ява, пхп, питон, шарп…

Понятно, что системный слой, как и все в мире, написано на С. Не о том речь
Понятно, что системный слой, как и все в мире, написано на С. Не о том речь
А о чём, собственно? Вот мы сейчас тут очень долго обсуждаем из чего нужно делать надёжные вещи — из стали или «говна и палок», условно говоря. И вы говорите: ну, конечно, из «говна и палок»!

Ведь даже в танке «мешок с дерьмом» сидит — пусть даже броня у него из стали.

Так вот: во-первых это не говорит не о надёжности этого «мешка с дерьмом». Можно говорить о гибкости или там ещё о чём-то, но надёжности в нём никакой — рулькой попади и всё. А во-вторых над его «изведением» — работают.

Не знаю, в каком ты мире, но в моем почти все веб сервера — а именно логика — на GC — ява, пхп, питон, шарп…
Вот только в большинстве случаев никто даже не рискует их выставить без защиты в интернет. Максимум — веб-сервер на C/C++ где где-внутри, под надёжной защитой, крутится модуль на другом языке. Потому что «надёжные» такие, наверное.

Кстати Actix, который мы тут, вроде как, обсуждаем, похоже можно выставлять в интернет… Ему защита не нужна… и в нём — таки есть ARC и нет GC.
Тогда откуда взялись ваши же слова про недопустимость падения?
Недопустимость означает лишь то, что такая ситуация — совсем не норма. И если ошибку можно обработать в штатном режиме — то лучше так и сделать.
Знаете — эту сказку про белого бычка пора кончать. Про это Джоел уже тоже писал. Если вы — «средней руки бизнес» и не можете себе позволить качественный софт, то вы берёте условного «дешёвого индуса», Java и получаете… что-то. Оно кривое, косое, неудобное и глючит — но оно дёшево стоило, а это для вас главное.
Ну во первых — у бизнеса просто может не быть денег и времени.
А если бизнес действительно может позволить себе качество — то он и нанимает хороших программистов. Даёт им хороший инструмент(Java), и спит спокойно, не думая о том что у него завтра будет глобальный ДОС из-за того, что С++ бог где-то обратился по битой ссылке(как бонус — потенциальная уязвимость к новым классам атак). А софт можно поддерживать и быстро менять под требования бизнеса.
И реальность в нашем мире — именно такая. Рыночек уже порешал, это и есть «очевидное». Веб серверами на java бизнес пользуется давно и повсеместно.

На самом деле меня во многом Java не устраивает, но это не относится к vm, gc, производительности и возможностям отладки.
На самом деле меня во многом Java не устраивает, но это не относится к vm, gc, производительности и возможностям отладки.
Вообще интересно, когда я делаю бенчи на коленке — C# core оказывается быстрее чем Java, смотришь techempower — а в нем C# вообще довольно низко, и это странно.
PS
Я так понимаю, оба формата байт-кода не позволяют проводить глубокую оптимизацию, может поэтому и пилят альтернативу в виде WASI, где в компайл-тайм возможны чудеса.
В techempower и java с java в разы отличаются. Всё потому что разные модели многопоточности используются, разные алгоритмы. Думаю в среднем .NET core должен быть побыстрее.
Насколько я понимаю, байткод jvm(clr не знаю), позволяет делать много разных оптимизаций, но всё это делается в рантайме(в т.ч. динамический инлайнинг, размещение объектов на стеке, «обход» ненужных точек синхронизации).
WASI особо не изучал, думаю там больше расчёт на compile-time. Но в compile time нельзя понять, например, насколько часто используются методы что-бы их заинлайнить или какие синхронизированные методы используются только из одного потока, что-бы «выключить» синхронизацию.
или какие синхронизированные методы используются только из одного потока, что-бы «выключить» синхронизацию.
Пожалуй, раст такое сможет в компайлтайме. Интересна не битва «GC против не-GC», а JIT против AOT. Собственно, зачем этот высокоуровневый байт-код, который нужно джитить, если есть низкоуровневый байт-код (WASM) или вообще натив? Мне кажется что идея виртуальных машин устаревает, и Java и .Net скоро сольют тому же WASI, потому что в нем нет предустановленного GC, система команд, как я понял, довольно близка нативу, а значит компиляторы могут малыми усилиями добавить себе еще один target.
PS
Например, TypeScript, компилируемый в WASM, красота жеж, жаль что нету.
Если говорить о производительности — у подхода WASM преимущество для UI. Меньше время запуска, не надо ждать пока «прогреется» вм, оптимизации доступны сразу итд. Но вот вопрос какие отпимизации в итоге качественнее — в теории, именно рантайм оптимизации. Т.к. у среды исполнения больше информации чем у компилятора. Так что, думаю, на серверах java будет жить как обычно. Для остального может развиваться в сторону AOT-компиляции.
А вообще мне близка идея компиляции всего что только возможно, высокоуровневые языки с сильной системой типов. И wasm для такого выглядит неплохо.
Т.к. у среды исполнения больше информации чем у компилятора.
Ресурсов тоже сильно меньше. Если у нас компилятор требудет 64GB… ну поставим на билд-сервер памяти побольше. А если столько JIT сожрёт?

Ещё хуже ситуация с L1 — он мечется между 16K, 32K, и 64K. Причём Zen2 — опять 32K. То есть рассчитывать на то, что L1 будет расти, со совремнем, нельзя. Совсем нельзя.

В результате всё время приходится бороться за то, чтобы работа JIT'а не мешала основной деятельности — а это, в пределе, вырождается в PGO. AOT.

Хотя сделать исскуственный пример, где JIT бы «рулил» — раз плюнуть. Но на практике это не нужно.

А какой компилятор, кроме плюсовых требует такого огромного количества ресурсов?
Какой-нибудь ghc довольно много жрет, но там много уходит на статические проверки системы типов. Это можно разрулить во время компиляции в промежуточный код.
Плюс jvm далеко не всё компилирует.
Ваши рассуждения о кэше чем-нибудь подкреплены?

А какой компилятор, кроме плюсовых требует такого огромного количества ресурсов?
Любой, если вы будете делать анализ порядка выполнения на большом модуле делать. Чудовищные объёмы памяти (в десятки гигабайт) возникают не просто от C++ — они от LTO над целыми программами возникают… то есть там, где можно больше всего выиграть.

Ваши рассуждения о кэше чем-нибудь подкреплены?
Ничем, кроме здравого смысла. Но вообще у разработчиков JIT'а эта дилемма «сгенерировать хорогий код» vs «не слишком сильно замедлить собственно выполнение программы» — проходит красной нитью во всём, что они делают.

Ну и да, некоторые из них мечтают о том, что когда-нибудь, возможно, если повезёт, JIT будет делать AOT PGO компиляциюю. Но это всё так… «в светлом будущем, может быть в XXII веке».

Приктически я ещё не видел никого, кто AOT PGO «делает» — и многие даже не могут похвастаться чем-то быстрее AOT без PGO.
Любой, если вы будете делать анализ порядка выполнения на большом модуле делать. Чудовищные объёмы памяти (в десятки гигабайт) возникают не просто от C++ — они от LTO над целыми программами возникают… то есть там, где можно больше всего выиграть.
О, у Вас есть опыт сборки программ(не С/С++), когда компиляторы заполняли бы десятки гигабайт оперативной памяти? Расскажите. Сколько в таком случае длится компиляция?
Для jvm это конечно смысла не имеет, т.к. там как раз локальный анализ и локальная компиляция.
Ничем, кроме здравого смысла. Но вообще у разработчиков JIT'а эта дилемма «сгенерировать хорогий код» vs «не слишком сильно замедлить собственно выполнение программы» — проходит красной нитью во всём, что они делают.
Просто рассуждения о кэше процессора _как правило_ ничем не подкреплены, не вижу смысла об этом говорить.
Поэтому jvm и не компилирует всё, а только нагруженные участки.
AOT PGO уже есть в GraalVM. Только, думаю, в реальных случаях это не особо быстрее JIT(для серверов). Плюс JVM и так достаточно быстрая для большинства кейсов(да и выбирают Java не столько за скорость), а упирается часто всё в БД.
Плюс JVM и так достаточно быстрая для большинства кейсов(да и выбирают Java не столько за скорость), а упирается часто всё в БД.
Ну это уже сто раз обсуждали. Та же самая причина, по которой Google тратит десятки миллионов на «допил» llvm, но не пользует ICC: даже существенная экономия на серерах недостаточна, чтобы «особый» toolchain поддерживать.

О, у Вас есть опыт сборки программ(не С/С++), когда компиляторы заполняли бы десятки гигабайт оперативной памяти?
Десятки гигабайт я видел только на C/C++, но простая логика говорит о том, что если кто-то понапишет столько кода, что результирующий бинарник, как у моих знакомых, не уместится в 2GB (есть у clang/gcc такое ограничение), то промежуточные данные легко могут десятки гигабайт занять. Собственно в момент LTO ведь уже все страшные конструкции языка обработаны, там уже только биткод остаётся к этому моменту…
Пожалуйста поправьте меня, если я ошибаюсь, ибо уже несколько лет не занимался llvm, но на сколько я помню там сначала фронтэнд, например, clang, транслирует все в IR, затем LLVM прогоняет все эти оптимизационные проходы по байт-коду IR, более 200 оптимизационных проходов при компиляции с оптимизацией, когда я последний раз с ним занимался, а уже затем, оптимизированный IR транслируется в нативный код бакендом и там уже нет таких сильных оптимизаций, только платформозависимые.
Что-то поменялось за последние годы? Если нет, то почему .NET и Java не могут делать абсолютно то-же самое, что и LLVM: основные оптимизации во время компиляции в свой байт-код?
но на сколько я помню там сначала фронтэнд, например, clang, транслирует все в IR
И уже на этом этапе всё завязано на архитектуру, под которую вы всё это компилируете.

Ну вот смотрите — рассмотрите простейший случай, когда структура, вдруг, внезапно, режется на части — кто это будет делать? И когда? Ответ: это будет делать clang на этапе генерации IR. Со всеми вытекающими.

Что-то поменялось за последние годы?
Ничего не поменялось. IR в LLVM всегда зависел как от версии LLVM, так и от архитектуры, под которую вы компилируете программу. Странно, что вы этого не знаете.

Если нет, то почему .NET и Java не могут делать абсолютно то-же самое, что и LLVM: основные оптимизации во время компиляции в свой байт-код?
Потому что «основные оптимизации» начинаются с соглашения о вызовах и тому подобной низкоуровневой мути. И чтобы им можно было сделать вам нужно по всей дороге от исходника до бинарника о ней знать.

А в .NET и Java ведь байткод в первую очередь для переносимости существует…
Ну это уже сто раз обсуждали. Та же самая причина, по которой Google тратит десятки миллионов на «допил» llvm, но не пользует ICC: даже существенная экономия на серерах недостаточна, чтобы «особый» toolchain поддерживать.
Не очень понимаю, как это связано с Java. Не все компании — гугл. Да и на практике всё в основном упирается в алгоритмы, а не в оптимизации. Посмотрите хотя-бы techempower. Java там быстрее C++.
Десятки гигабайт я видел только на C/C++, но простая логика говорит о том, что если кто-то понапишет столько кода, что результирующий бинарник, как у моих знакомых, не уместится в 2GB (есть у clang/gcc такое ограничение), то промежуточные данные легко могут десятки гигабайт занять. Собственно в момент LTO ведь уже все страшные конструкции языка обработаны, там уже только биткод остаётся к этому моменту…
Не представляю себе бинарник такого размера. Подозреваю что дело в кодогенерации(шаблоны)? Тогда это специфичный для плюсов случай.
У нас в модуле на C++ за последние несколько лет было найдена куча ошибок — но ни одна из них не было связана с ошибками выделения памяти.

Зато у всех остальных подавляющее большинство дыр в безопасности это ошибки работы с памятью. С памятью работать вручную и без ошибок не умеет никто, поэтому нам нужны GC и языки вроде Rust. Особенно в многопоточном коде.

Никто языки с GC туда тащить даже не пытается.

Еще как пытается. На джава достаточно много big data баз написано. На Go вон кубернетис, etcd и прочие штуки важные нынче.
С памятью работать вручную и без ошибок не умеет никто, поэтому нам нужны GC и языки вроде Rust.
На Rust, как я уже говорил много раз — я поглядываю. Пока в прод не пускал, но что там делается мне нравится. А GC — в топку. Это типичное лекарство, которое хуже болезни.

На Go вон кубернетис, etcd и прочие штуки важные нынче.
Это-то и печалит. Ну хорошо хоть вместо пургена (Java) начали хоть кислотой ширяться (Go). Всё-таки в Go хотя бы чуть-чуть о том, как это всё должно работать в реальном мире, думали до релиза, а не после.
Сыплюшие во все стороны NPE?

Вместо сегфолтов или даже скрытого повреждения памяти?

Смотря какие приложения. Что-то я все время сталкиваюсь с такими, для которых скорость работы и безопасность весьма важны. Ну и плюс язык с хорошей системой типов всегда предпочтительнее языка с плохой. Так что думаю, что Rust вполне конкурент для Java.

Так что думаю, что Rust вполне конкурент для Java.
Пока нет, экосистемы несравнимы… но в будушем — возможно.
>Чем меньше активных подключений — тем быстрее отвечает PostgreSQL.
В общем случае это не так. Скажем, у нашего проекта много подключений к разным системам, многие из которых оракл. И у всех разные настройки и разное железо, иногда различающиеся на несколько порядков. И когда DBA спрашиваешь, как лучше с ними работать — иногда выдают совершенно разные рекомендации. Где-то — задать хинт в запросе, а где-то и открыть несколько соединений, и выполнить несколько запросов одновременно.

Вообще для меня странно, что бенчмарк http сервера включает в себя какие-то запросы к базе. Тем более — к конкретной базе. Это почти автоматически означает, что мы меряем непонятно что.
ИМХО таки да, можно было и коллекцией какой нибудь в памяти ограничиться а не писать в БД. Ну или тесты ORM тогда уж отдельно. Котлет от мух как говорится.
Ну тут уже получается полный бред. Кому и для чего может потребоваться работа в памяти? Сколько вообще таких сайтов в мире?

То, что разобраны не все типичные сценарии — это плохо. Уж долгие-то транзакции изредка вместе с запросами можно было предусмотреть, это часто встревающаяся задача: всё-таки большинство сайтов довольно-то таки часто что-то там в базе обновляют… ну пусть хоть один запрос из 100.

Но тестировать то, что в реальном мире не встречается? Это уже соревнования дрегстеров получатся. Красиво, невероятно зрелищно… и абсолютно бессмысленно.
Ну, в таком виде оно не выглядит сильно менее бессмысленно. Если мы хотим померять быстродействие web — мы должны мерять его. Да, реальная нагрузка бывает разная, от статики до запросов к базам (и не только к ним).

Чтобы измерение было осмысленным, на мой взгляд эту нагрузку нужно эмулировать, т.е. грубо говоря, тест веба должен вызывать некоторые тестовые «сервисы», которые выжирают память, процессор, и вносят задержки в обработку исходного http-запроса. По определенному профилю нагрузки, возможно даже случайному.

И возвращают некие тестовые данные, в виде html/json/etc разного размера, для отдачи клиенту. Тоже видимо случайные, но с известными статистическими показателями.

А реальную СУБД из теста все же следовало бы исключить.
А реальную СУБД из теста все же следовало бы исключить.
Тогда вы исключите ровно вот этим самые эффекты типа «пул соединений» vs «пур запросов». А это — весьма принципиальный момент и в реальных задачах тоже.
Эффект, который актуален для постгре, а для других баз меньше или вообще не актуален. Тот же мускл не настолько чувствителен, а мария и того меньше.
Мы вместо них добавим контроллируемые задержки и потребление ресурсов. И для тестов веба должно быть все равно, куда ушли ресурсы в том приложении, которое генерировало html — на ожидание результатов из базы, или на sleep, на вычисление чего-то, или на цикл очистки памяти. Главное — тот факт, что запрос обработался за время T, сожрал N памяти, и M циклов процессора.

Остальное-то зачем в этом тесте мерять? Пул соединений вполне можно отдельно протестировать. В моем мире их кстати много разных, почему это вообще часть веб сервера вдруг, если оно не в вебе вполне имеет место?
но что такое постгресс?

статистически в корпоративе — оракл и мсскл, на сайтиках май…

очень неудачный и нетипичный выбор. еще и без транзакций

хотя есть же и другие тесты на темб
Вообще для меня странно, что бенчмарк http сервера включает в себя какие-то запросы к базе. Тем более — к конкретной базе. Это почти автоматически означает, что мы меряем непонятно что.

Бенчмарки TechEmpower проводятся по семи сценариям. Есть и бенчмарк http сервера (plaintext). Он упоминается в статье, туда легче всего попасть. Фреймворки ссылаются именно на него. Там замеры делаются очень даже понятно чего, причем самой длинной линейкой.


Опишу ситуацию, в которой остальные, синтетические, бенчмарки полезны.


Скажем есть возможность, даже необходимость, сменить стек (фреймворк, язык программирования, всё, что душа пожелает). Собирать его по частям — ну очень утомительно. Вот 5 фреймворков, вот 3 клиента postgres, вот так (обещают) будет работать быстро, а вот так — идиоматичненько. А может использовать MySQL? Вот еще 2 клиента на выбор. И так по каждому пункту. И это если отбросить вообще неведомые вещи, которые хороший парень ну очень рекомендует.


Такая непрерывная фрустрация, когда информации много, тонешь. Рыться можно часами и сутками, а решение в итоге принимается с ощущением выбора наугад.


Синтетические бенчмарки дают готовый, подобранный другими людьми набор с четкими результатами (ну очень удобно, если есть требования по нагрузке). Помогает сузить стартовый набор вариантов, одновременно предлагая несколько работающих примеров использования.


А полигон для исследований так совсем отличный.

Открывается всего одно соединение с PostgreSQL на поток веб-сервера. В этом соединении используется конвейерный режим, позволяющий эффективно использовать его для параллельной обработки пользовательских запросов.

В нормальных библиотеках с пулом соединений, есть параметр максимальное число соединений. Сделано это на случай dos, или не неожиданной нагрузке.

Это немного разные вещи. Представьте, что к нам пришли два HTTP-запроса, A и B, которым нужно выполнить SQL-запросы A1, A2, A3 и B1, B2 соответственно. Если у нас есть пул из одного коннекта, то запросы будут выполняться либо в последовательности A1, A2, A3, B1, B2, либо в последовательности B1, B2, A1, A2, A3. В бенчмарке используется подход, в котором запросы могут выполниться в порядке A1, B1, A2, A3, B2 (и аналогичных). Можно, конечно, и в первом случае класть коннекшн обратно в пул после каждого запроса и брать перед очередным, но я не видел, чтобы так делали.
Можно, конечно, и в первом случае класть коннекшн обратно в пул после каждого запроса и брать перед очередным, но я не видел, чтобы так делали.

Стандартная библиотека в Go так и делает. Есть нюансы, но в общем случае это так.

Да, но при этом каждое из N открытых соединений обрабатывает только одну команду за раз. То есть если Latency БД допусим 5мс то каждый коннект из пула сможет делать не более 200 запросов в секунду. В случае пула pipelined коннектов такой проблемы бы не было, но ломается транзакционная модель. В том же Go при открытии транзакции коннект из пула лочится за этой транзакцией и другими потоками уже переиспользоваться не может пока транзакция не закроется.

У автора есть небольшая логическая ошибка в статье. Он ссылается на деградацию производительности БД от числа соединений. И в выводе указывает:


  • "Открывается всего одно соединение с PostgreSQL на поток веб-сервера."
  • "Чем меньше активных подключений — тем быстрее отвечает PostgreSQL."
    Соответственно залочив количество соединений в пуле, то эти рекомендации применительно к этим тестам не подходит, а весь вывод сокращается до одной строчки: используй pipeline.

В общем случае рекомендация 1 соединение на 1 поток зависит от железа и типа нагрузки CPU bound или I/O bound.

Прочитав заголовок, был уверен, что речь пойдёт об unsafe, в свете последних событий.
А тут вон он как. Интеренсо. Спасибо.

Там вообще например место из за которого драма то началась вообще не аффектило перфоманс, а unsafe был.
А все потому, что вот такие места с unsafe нигде в коде не подтверждены бенчмарками.
Все выводы видимо делались исходя из медитации над кодом или ручного профилирования.
Так, что бенчмарки иногда очень полезны бывают, что отстоять точку зрения.

Я так понимаю, что мейнтенить проект теперь будет другой человек, который не против выпилить unsafe. Будем надеяться, что вы правы и проект станет безопаснее при этом не потеряв в производительности.

Скорее всего будет небольшая просадка. Если оно после этого перестанет падать в вырожденных случаях — это будет нормальным результатом.

Да, довольно интересно. Автор actix-web был реально ориентирован на производительность.

Видно, что при увеличении количества открытых соединений производительность сервера PostgreSQL стремительно падает.

Зачем же так статью коверкать? Все совсем не так тривиально. Малое число соединений работает, когда клиентов мало, что как бы очевидно. Когда клиентов становится много, то пулы соединений уже оказываются нужны и показывают значительно большую производительность.
Именно. Причем «много или мало» — зависит от параметров и настройки сервера.
Кстати, конвейерный режим (Pipelining) передачи запросов для БД Redis рекомендуетсься непосредственно в документации.
В Rust реализуется принцип CDD:
Compiler Driven Developmen.
Разработка, управляемая компилятором.

При всей моей любви к Rust: это не так. Сообщения от компилятора, конечно, сильно помогают, но писать код всё же надо самому. Вот где действительно то, что можно назвать compiler-driven development — так это в Idris

Спасибо AnthonyMikh.
За ссылку на Idris.
Друзья куда торопимся. Даже текст не проверили на ошибки Вспрокрастинулось.
Очень хорошо.
А теперь найдите ошибку здесь:

use std::threads;

fn main() {
    let greeting = "Здравствуйте raiszakharova";
    let mut thread = Vec::new();

    for num in 0..5 {
        thread.push(threads::spawn(move || {
            println!("{} из потока номер {}", greeting, num);
        }));
    }
    for threads in thread {
        let res = threads.join();
        if let Err(_) = res {
            println!("Произошла ошибка!");
        }
    }
}
Вроде работает после исправления опечаток. А что должно быть не так?
Лог плейграунда
Standard Error
Compiling playground v0.0.1 (/playground)
Finished dev [unoptimized + debuginfo] target(s) in 0.96s
Running `target/debug/playground`
Standard Output
Здравствуйте raiszakharova из потока номер 0
Здравствуйте raiszakharova из потока номер 2
Здравствуйте raiszakharova из потока номер 1
Здравствуйте raiszakharova из потока номер 3
Здравствуйте raiszakharova из потока номер 4

Вот так всегда.
Только начинаешь с девушкой знакомиться и тут…
image
Вспрокрастинулось.

«Вспрокрастинулось» — вообще шедевр же, за одно это слово можно 20 плюсов поставить.

Вот и всё.
: Холмс
: А как вы вычислили гениального подлеца Мориарти
: Этика мой друг
: Этика
image
текст комментария дан в режиме субтитров.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории