Здравствуйте, ·, Вы писали:
dot>>>Во-вторых, попробуй такой трюк проверни со включенным security manager, запретить const_cast же — невозможно. EP>>Возможно. Даже если опасаешься false positives/negatives grep'а — то легко написать простейший верификатор на базе Clang AST Matchers. dot>Задоблаешься. Скажем, частенько при интеграции с другими библиотеками, особенно C, может вылезти: dot>
dot>Obj *obj2 = (Obj *)obj2;
dot>
dot>И нифига ненаверифицируешь.
Старый cast легко отлавливается компилятором, например опция GCC -Werror-old-style-cast
dot>И вообще, никакая это не верификация, а статический анализ.
Это верификация соответствия требованиям.
dot>>>В-третьих, вот так в C++ писать уже можно? EP>>В Java у volatile семантика другая нежели чем у C++. Для этой же цели используются std/boost::atomic. То есть будет не volatile ImmutableClass *, а atomic<ImmutableClass *>. dot>О, наконец-то стало можно. Если не ошибаюсь, в С++ это появилось c 2011, в Яве же с 2004.
В стандарте C++11 появились std::atomic, memory model, да и вообще понятие потока.
Библиотечные же реализации работают и в C++1998.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
dot>>ООМ везде можно получить. EP>Я описываю конкретный case, который есть при использовании GC, и которого нет при использовании scope-based lifetime.
Понятно, что С++ позволяет сделать больше, чем Java. Но GC это очень хороший алгоритм и даёт много преимуществ в широком классе задач, в т.ч. low latency.
EP>Не надо оправдываться абстрактными "OOM везде можно получить" — я так тоже умею: "везде можно получить расстрел памяти, даже при использовании Java"
Расстрел памяти в Java? Как???
dot>>>>Можно, конечно, такое же соорудить и в C++, но это будет закат солнца вручную. EP>>>Почему вручную-то? Если есть такой развесистый граф владения — то и очищай его по частям, автоматически. dot>>По каким частям? EP>Например очищать фиксированное количество объектов за раз, оставшиеся ставить в очередь.
Фиксированное чем? Как узнать сколько за раз?
dot>>Как эти части определить? EP>Как того требует задача. Если нужно ограничить время — то соответственно засекаешь время/тики, или что там удобнее.
Время чего? Вот у тебя
smartPtr.release(); // упс, заняло 300ms, хотя обычно занимает 3us. Нам не повезло оказаться последним юзером объекта и граф объектов оказался больше обычного.
Куда именно ты поставишь тики?
dot>>В каком порядке очищать? EP>Например в порядке очереди.
Как выстоить в очередь граф объектов?
dot>>Когда ответишь на эти вопросы и многие другие, то переизобретёшь современный gc. EP>Нет, это неверно. Задача GC в первую очередь отличить reachable от unreachable объектов. А уж делать reclaim порциями, или за один присест — это уже отдельное свойство, причём ортогональное наличию/отсутствию GC.
GC в Java это целый комплекс алгоритмов с тучей настроек. Задача — освобождать память от unreachable объектов, а не просто "отличить".
EP>И, кстати, для C++ возможен и runtime'овый GC (прям в стандарте есть специальное API), и библиотечный (я даже как-то делал for fun).
Но как? Precise GC принципиально невозможен. С safepoints тоже проблемы. Дизайн Java позволяет использовать гораздо более мощные алгоритмы GC, чем те, которые доступны в C++ коде.
EP>>>Да и возникают они в Java на порядки чаще чем в C++. Элементарный пример: массив объектов, агрегирующих другие объекты, агрегирующих другие ... — вполне типичная ситуация. На Java будет тот самый развесистый граф, а на C++ может быть просто по сути один вектор освобождающийся за O(1) — так как value semantics. dot>>Сам же написал "GC линеен от количества N живых объектов", т.е. сдохший шмат графа можно освобождать также за примерно O(1). EP>При этом сделав O(N) обход живых объектов, которого нет в случае с C++
В YG это N малО, а OG обходится не часто, и обычно минимально влияя на основные потоки. (Мы рассматриваем low latency, когда память не забита под завязку, конечно).
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, T4r4sB, Вы писали:
TB>·>Т.е. неиндусы — никогда не ошибаются? Даже дело не в том, что ошибаются или нет — проверить никак нельзя. TB>То есть С++ требовательнее к дисциплине программиста. Это его недостаток. Что не так?
Дело не в дисциплине, а в гарантии правильности. Если у тебя есть некий критичный кусочек кода — его можно проанализировать очень внимательно (или даже доказать) и быть уверенным, что именно он так и отработает. В случае С++ — это невозможно принципиально — расстрел памяти ведёт к непредсказуемым результатам любой части кода.
TB>·>запретить const_cast же — невозможно. TB>Это уныло. Ругать язык за то, что в нём можно сделать диверсию — это уныло, потому что диверсию можно сделать в любом языке, что я и показал. И говорить, что якобы в одном языке обнаружить такую откровенную (как конст_каст) диверсию проще, чем в другом — это тоже уныло.
Знаю, что уныло, но факт остаётся фактом. И не диверсия, а просто ошибка.
TB>·>В-третьих, вот так в C++ писать уже можно? TB>А проблема-то в чём? Расскажи, почему в жабе так можно делать (с локами? без них?), а в С++ нельзя? Что за хрень эти иммутабельные классы? Аааа, я понял, в жабе нету типа "указатель на константу", поэтому "иммутальные классы" преподносятся, как откровение.
Укзатель на константу? Это оно?
Здравствуйте, ·, Вы писали:
dot>>>Ролик не смотрел, дома посмотрю... Но могу заметить, что эти все нарезки обычно делаются в довольно ограниченной области кода. И все эти порчи данных довольно изолированы (или как минимум изолируемы). EP>>Да, но мы-то обсуждаем именно такой случай, именно эту самую область кода. И эта ручная error-prone нарезка на структуры, отказ от GC и т.п. — по сути выливается в уровень ниже чем C, и в результате приводит к вполне ожидаемым ошибкам: http://rsdn.ru/forum/philosophy/6201205.1
вместо массива Complex, руками нарезать массив double на Complex, и руками сплавлять обход и перемножение элементов массива в одну операцию. И именно в этом месте у него вылезла ошибка.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
dot>>Мало того, тот кому не повезло освобождать ссылку последним — не придётся тратить ВНЕЗАПНО время на освобождение, EP>Пусть просто поставит в очередь, делов-то
Поставит что? Сразу весь развесистый граф? А если надо только часть освободить? Ставить каждый самый мелкий объект? А очереди не поплохеет? В общем опять, закат gc вручую.
EP>>>На Java кстати какое решение можешь предложить? dot>>На java память ресурс другого типа. "Освобождать память" там нельзя. EP>А я поэтому и сказал конкретно "ресурс", а не просто "объект". Да и память не ресурс только если она не ограниченна — упрёшься в предел, получишь удар по latency, на который постоянно ссылаешься.
В low latency единственный ресурс обычно память. Из LL кода никто в своём уме коннекты к БД не открывает, с SOAP сервисам не обращается.
dot>>А для ресурсов в общем случае — наследники java.lang.ref.Reference. EP>Каким образом они обеспечат prompt finalization?
А надо? А зачем вообще ресурсы обязательно освобождать из финалайзеров? Я не понял какую задачу ты описываешь.
EP>>>На них точно также получаются локи. Чтобы их не было, нужно использовать например lock-free / wait-free схемы. dot>>Ну да... Только, как мне кажется. эти схемы гораздо проще если есть gc. EP>При GC чуть проще реализация lockfree структур данных, за счёт ABA, но при этом сами GC в большинстве своём не lockfree (lockfree GC видел только в статьях) — то есть тут уже большой терминологический вопрос, остаётся ли структура данных lockfree при не non-lockfree GC
Если локи расставляются сами компилятором и лочится на предсказуемое время — почему бы и нет.
EP>Да и такие структуры должны реализовывать профессионалы своего дела, так как это трудная/опасная тема.
В том то и дело. Это уже и реализовали в виде JVM — бери, да используй, а не изобретай велосипед.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ·, Вы писали:
·>Дело не в дисциплине, а в гарантии правильности. Если у тебя есть некий критичный кусочек кода — его можно проанализировать очень внимательно (или даже доказать) и быть уверенным, что именно он так и отработает. В случае С++ — это невозможно принципиально — расстрел памяти ведёт к непредсказуемым результатам любой части кода.
Да-да, я это слышал, это из первой лекции для студентов "недостатки С++". /_-
·>Знаю, что уныло, но факт остаётся фактом. И не диверсия, а просто ошибка.
Случайно сделать 10 ошибок в слове "int" и получить "const_cast"? Или как?
·>Проблему сам найдёшь? Откровение открылось?
Нет, проблемы не вижу, кроме случая, когда в команде бардак и разработчики диверсанты, "случайно" пишущие const_cast, срущие в данные, которые другой поток считает константными, делящие на ноль и невзначай вставляющие в код команду форматирования винчестера.
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Здравствуйте, A13x, Вы писали:
A>Для желающих проверить на практике — предлагаю желающим реализовать простейший интерпретатор нетипизированного лямбда исчисления на С++ (ну то есть REPL который бы понимал что-то вроде такого) и сравнить его быстродейтвие с тем, что можно реализовать на яве (мой вариант меньше 500 строк и по быстродействию уделывает известные мне интерпретаторы scheme). Насколько я себе представляю возможные реализации — на стресс тестах будет большое количество виртуальных вызовов и большое количество выделений памяти под мелкие объекты.
Дык можно запилить свой хитрый аллокатор для мелких объектов просто, а подвох-то в чём?
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Здравствуйте, PM, Вы писали:
PM>>> Короче, сплошной <троллейбус из буханки хлеба.jpeg> PM>·>Собственно сейчас в моде Zing JVM, там задержки не более 10ms, в основном ~100us. PM>·>Писать такое на C++... неее... проще застрелиться. PM>·>А перезапуск нужен не для gc, а чтобы на диск состояние скинуть, ибо использовать классические СУБД — теоретически невозможно. PM>Задержка 10ms может быть неприемлемой в некоторых задачах, в том числе и в биржевых системах.
Я знаю, я говорю, что 10ms это уже проблема — на которую тут же заводится bug report.
PM>Состояние на диск постоянно скидывается в LMAX в отдельном потоке в файл журнала. И разработчики LMAX повоевали и здесь чтобы добиться предсказуемого результата. Ну и для сетевого I/O наверняка использовали какой-нибудь NIO.
В журнал скидываются транзакции (дельты), а раз в день делается снапшот состояния.
Война была, да и до сих пор ведётся не с java, а с архитектурой и железом. 90% тех решений подходит и для С++.
PM>Я почитал ваши сообщения в ветке. Ещё одна иллюстрация, что люди не из мира C++ продолжают верить в мифы 15-20 летней давности. Авторы LMAX скорее всего тоже подумали: "Писать такое на C++... неее... проще застрелиться" и взяли хорошо известный им и широко распространённый инструмент. Хорошо, что при этом они не застрелились
На плюсах есть куски или нативные либы, но как-то постепенно перетекает всё в java. Ибо работает так же, а добавляется куча преимуществ (см ролик http://rsdn.ru/forum/philosophy/6205646.1
)
Как в одно время был процесс переползания с ассемблеров на фортран, на С, на С++, теперь вот java. Конечно, на arduino яву не запустишь, но нишу LL она уверенно отвоёвывает.
PM>Часть кода, кстати, открыта: https://github.com/LMAX-Exchange/disruptor и выглядит на мой взгляд over engineering. Хотя, может для Java мира это нормально
Видимо поэтому этот over engineering стырили https://code.google.com/p/disruptor-cpp/https://github.com/disruptor-net/Disruptor-net Как видишь — дело не в языке, а в архитектуре.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, Ikemefula, Вы писали:
I>>>>>"Как на Си" это в первую очередь вещи, где нужен полный контроль над временем выполнения, не "хрен знает сколько" а скажем "не более 8000 тактов". EP>>>>Это не "как на Си". Контроль над временем вообще ортогонален "как на Си". I>>>Наоборот. EP>>Аргументированно I>Ну и логика I>Вот это аргумент: 'Это не "как на Си"' I>А вот это — 'Это "как на Си"' — уже нет I>Браво !
Врёшь же. Я конкретно сказал что контроль над временем ортогонален "как на Си".
I>>>ибо в low-latency работает в т.ч. и джава. EP>>На Java можно в том числе создавать быстрый код. Затратно, но можно. I>Настолько быстрый, как С++ это невозможно. И тем не менее джава есть в low latence. А следовательно твой
Настолько же быстрый — да, вряд ли. А вот разница в десятки процентов (не считая векторизацию и т.п.) вполне достижима. Огромной ценой, но тем не менее.
EP>>>>На C та часть которая "не только" (то есть опциональная) — она не реализуема. I>>>Реализуема, только иначе. EP>>Расшифруй. I>Это значит, что те же задачи решаются и в Си, за доказательством смотри полноту по тьюрингу
Причём тут полнота по Тьюрингу? В данном месте обсуждаем использование ref-counting там где в этом нет необходимости, то есть для подстраховки.
EP>>А ты представляешь как реализуется ref-counting в C++11? I>А при чем здесь 11 ?
При том что на нём передёргиваний счётчиков на порядки меньше. И соответственно меньше overhead'а привнесённого непосредственно подсчётом ссылок.
I>Есть куча кода написаного в тысячах контор: I>"Зануление счетчика, вызов деструктора, который вызывает зануление другого счетчика, который вызывает деструктор,и тд и тд"
В этом плане ref-counting практически никаких тормозов не добавляет (пара лишних операций перед очисткой) — подобная каскадная очистка есть и без него.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
I>>Вот это аргумент: 'Это не "как на Си"' I>>А вот это — 'Это "как на Си"' — уже нет I>>Браво !
EP>Врёшь же. Я конкретно сказал что контроль над временем ортогонален "как на Си".
Это просто твоё мнение, ничем не аргументированое. Я его услышал, выдал симметричное, только мне понадобилось всего слово для этого.
I>>Настолько быстрый, как С++ это невозможно. И тем не менее джава есть в low latence. А следовательно твой EP>Настолько же быстрый — да, вряд ли. А вот разница в десятки процентов (не считая векторизацию и т.п.) вполне достижима. Огромной ценой, но тем не менее.
То есть, в кратце, незачем выжимать сок из Сишного кода, если код на десятки процентов медленнее справляется с работой. Правильно ?
EP>>>Расшифруй. I>>Это значит, что те же задачи решаются и в Си, за доказательством смотри полноту по тьюрингу EP>Причём тут полнота по Тьюрингу? В данном месте обсуждаем использование ref-counting там где в этом нет необходимости, то есть для подстраховки.
Из этой самой полноты следует "в этом нет необходимости, то есть для подстраховки"
EP>При том что на нём передёргиваний счётчиков на порядки меньше. И соответственно меньше overhead'а привнесённого непосредственно подсчётом ссылок.
А в Киеве дядька.
I>>Есть куча кода написаного в тысячах контор: I>>"Зануление счетчика, вызов деструктора, который вызывает зануление другого счетчика, который вызывает деструктор,и тд и тд"
EP>В этом плане ref-counting практически никаких тормозов не добавляет (пара лишних операций перед очисткой) — подобная каскадная очистка есть и без него.
Твоя "подстраховка", требует ресурсов. Каскадная очистка — часть этой самой подстраховки. Вместо явной логики "вычислить и освободить явно" выбираем неявную "пусть деструкторы срабатывают каскадом по цепочке, авось пронесёт"
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>>>Escape analysis вообще из другой оперы. Это например про аллокации объектов на стэке вместо кучи — в таких случаях никакой unique_ptr не нужен EP>>>Более того аллокация на стэке — это default для C++, и работает даже в случаях escape. dot>>Не обязательно на стеке, ещё есть thread local heap. dot>>Код может быть очень развесистохитрым, а unique_ptr выведется сам, как результат EA. EP>И каким образом у тебя получился тут unique_ptr?
Если VM оптимизатор видит, что объект используется только в одном месте, он переносит ссылку на стек, т.е. по сути выводит unique_ptr.
EP>>>Что? Причём тут теперь weak_ptr? Уже какая-то каша пошла. dot>>shared_ptr у треда-владельца, а "зависимые" — имеют weak_ptr. EP>Как weak_ptr относится к дискуссии?
Как я понял, ты предлагаешь выделить один тред как владелец объекта, т.е. тот, кто держит shared_ptr, а другие треды — просто пользователи объекта, им отдаётся weak_ptr (не голые же указатели передавать?!).
dot>>>>Последствия обращения к неверным указателям — серьёзнее. Не простой краш, а хз что. EP>>>А например OOM — тоже вполне серьёзно dot>>ООМ происходит в яве ровно так же как и в плюсах. Всё то же самое. EP>OOM он конечно везде OOM, но причины наступления бывают разные
OOM не кидается в случае если gc есть что освободить.
dot>>Отказываться от классов? Это как? В смысле раскладывать данные по массивам? Массивы не отказ от классов, а от полей классов. Эта техника не специфична для Java, в С++ тоже так делают: dot>>http://programmers.stackexchange.com/questions/246474/data-oriented-design-impractical-with-more-than-1-2-structure-members EP>Нет, именно от классов. То что ты привёл это иногда называется Structure of Arrays, в C++ кстати я такую трансформацию реализовывал в библиотечном виде, то есть раскидывание происходит автоматически. EP>Я же говорю про обычные структуры, в Java нет даже их. То есть положить в один массив плотно друг к другу данные вида: EP>
напрямую не получится, и это существенный источник тормозов. В зависимости от ситуации медленнее может быть на несколько порядков. На ровном месте вырастают целые деревья индерекций. EP>Обходится например через огород low-level эмуляции на чём-то типа ByteBuffer.
Делаешь класс (зачем от классов то отказываться?!)
class FooArray
{
ByteBuffer data;
getA();setA();
getB();setB();
getC();setC();
getD();setD();
}
Да, выглядит некрасиво, но обычно составляет <0.1% кода и сложностей никаких не доставляет. Почему-то я уверен, что "трансформацию реализовывал в библиотечном виде" будет выглядеть гораздо хуже и иметь довольно нетривиальный код.
EP>>>Это фантазии, ничем не обоснованные. EP>>>При использовании Java в таких случаях отказываются и от GC и от классов, и нарезают вручную массивы байт на структуры. Получить порчу данных в таком случае на порядки проще чем на высокоуровневом C++. EP>>>Вот конкретный пример (первые минут двадцать) dot>>Посмотрел начало ролика. В общем там неплохо сказано — быстрое ядро относительно маленькое, зато обвязка — на порядок больше. Поэтому в целом и получается этот порядок. EP>Субъектива он там нагнал предостаточно (ЕМНИП было даже что-то типа "для нормального C++ кода, нужны те кто ковыряется внутри кода GCC"). EP>Давай обсуждать объективные вещи.
Так вполне объективно: LL ядро — один сервис, обвязка вокруг него — ещё пол сотни.
dot>>И, кстати, я заметил, что java-specific там практически ничего не было. Основной упор именно на то, что надо понимать как работает железо, а какой язык — не важно, техники отличаются деталями реализации, принцип же тот же. EP>Понимать как работает железо обязательно нужно. Также нужно понимать как отображаются конструкции языка в железо — и вот с этим у Java проблемы, для быстрого кода приходится отказываться даже от элементарных абстракций и городить low-level код, который даже ниже уровень чем то что есть в языке C.
Да нет каких-то сверх-проблем. Да, нужно писать код определённым образом, реализовывать определённые решения, но это верно для любого оптимизируемого кода на любом языке. Java в этом плане ничуть ни лучше, ничуть не хуже, чем C++.
dot>>А. Да. Не так посмотрел, там оказывается специализации под разные стратегии. dot>>В общем да. Да, на плюсах можно делать то же, что и в Яве. Ведь Ява на плюсах написана. Но из-за арифметики указателей и прочего нормальный gc сделать всё равно не выйдет, только всякие ref counting и прочее. EP>Я делал библиотечный копирующий GC for fun — простой вариант с одним поколением уместился в несколько сотен строк. Естественно есть ограничения, но тем не менее. EP>AFAIR, в Mozzila используют библиотечный GC в местах interop'а с JS.
Простые варианты и делаются просто. А когда ты начинаешь делать не for fun, а серьёзно — становится всё плохо.
Mozilla не LL, а JS вообще однопоточный. В общем, всё на порядки проще.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, Ikemefula, Вы писали:
I>>>·>gc прощает гораздо больше ошибок. И самое страшное что может случиться — тормоза. Без него — всё что угодно, и тормоза, и порча памяти, и утечки. I>>>Самое страшное — твою софтину для биржи на джаве никто не купит. Отсюда ясно, что часть софта в принципе никто даже не станет пытаться писать на джаве. I>·>Расскажи это как миниимум упомянутым dxFeed и LMAX. I>Есть куча проектов, которые загнулись из за того, что так и не смогли сбороть этот GC.
А конкретнее? Ни одного С++ проекта не загнулась из-за сложности кода?
I> Потому для LMAX это вполне реальный риск и именно по этому они лезут из кожи вон, что бы обеспечить нужное время отклика.
Ещё раз. GC составляет O(1) проблем LL, самая жуть это железо и ОС. Глючный сетевой кабель — и 300мс задержка только в путь, такое GC и не снилось.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>>>Возможно. Даже если опасаешься false positives/negatives grep'а — то легко написать простейший верификатор на базе Clang AST Matchers. dot>>Задоблаешься. Скажем, частенько при интеграции с другими библиотеками, особенно C, может вылезти: dot>>
dot>>Obj *obj2 = (Obj *)obj2;
dot>>
dot>>И нифига ненаверифицируешь. EP>Старый cast легко отлавливается компилятором, например опция GCC -Werror-old-style-cast
Ну пусть будет reinterpret_cast<Obj *>(obj) какая разница-то?
dot>>И вообще, никакая это не верификация, а статический анализ. EP>Это верификация соответствия требованиям.
Под верификацией обычно понимается проверка формальными методами. А так — анализ, тестирование.
EP>>>В Java у volatile семантика другая нежели чем у C++. Для этой же цели используются std/boost::atomic. То есть будет не volatile ImmutableClass *, а atomic<ImmutableClass *>. dot>>О, наконец-то стало можно. Если не ошибаюсь, в С++ это появилось c 2011, в Яве же с 2004. EP>В стандарте C++11 появились std::atomic, memory model, да и вообще понятие потока. EP>Библиотечные же реализации работают и в C++1998.
Ну так нечестно. А то я скажу, что "gc можно отключить в java" если использовать библиотеки с off-heap.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ·, Вы писали:
EP>>Не надо оправдываться абстрактными "OOM везде можно получить" — я так тоже умею: "везде можно получить расстрел памяти, даже при использовании Java" dot>Расстрел памяти в Java? Как???
Элементарно. Её может расстрелять сторонняя native библиотека, причём даже не из-за бага в самой библиотеке, а потому что Java код нарушил предусловия. Или например может расстрелять свой же Runtime — гарантии нет
Это аргумент примерно того же порядка что и "OOM везде можно получить" в ответ на описание конкретного use-case'а.
dot>>>Как эти части определить? EP>>Как того требует задача. Если нужно ограничить время — то соответственно засекаешь время/тики, или что там удобнее. dot>Время чего? Вот у тебя dot>
dot>smartPtr.release(); // упс, заняло 300ms, хотя обычно занимает 3us. Нам не повезло оказаться последним юзером объекта и граф объектов оказался больше обычного.
dot>
Не обязательно удалять весь граф сразу, можно по одному узлу, граф-остаток ставить в очередь.
dot>>>В каком порядке очищать? EP>>Например в порядке очереди. dot>Как выстоить в очередь граф объектов?
Например в деструкторе smart-pointer'а не сразу удалять, а ставить в очередь.
dot>>>Когда ответишь на эти вопросы и многие другие, то переизобретёшь современный gc. EP>>Нет, это неверно. Задача GC в первую очередь отличить reachable от unreachable объектов. А уж делать reclaim порциями, или за один присест — это уже отдельное свойство, причём ортогональное наличию/отсутствию GC. dot>GC в Java это целый комплекс алгоритмов с тучей настроек. Задача — освобождать память от unreachable объектов, а не просто "отличить".
А ещё он внутри делает сложение целых чисел. Из этого не следует что сложение целых чисел это "переизобретание современного GC".
EP>>И, кстати, для C++ возможен и runtime'овый GC (прям в стандарте есть специальное API), и библиотечный (я даже как-то делал for fun). dot>Но как? Precise GC принципиально невозможен.
Возможен. Заводится отдельная GC Heap, все указатели на элементы внутри этой кучи заворачиваются в gc_ptr<T>.
Отличать root от не root можно разными способами. Например если сам объект-указатель типа gc_ptr находится не в этой куче — то это root.
AFAIR, нечто подобное используется в SpiderMonkey.
EP>>>>Да и возникают они в Java на порядки чаще чем в C++. Элементарный пример: массив объектов, агрегирующих другие объекты, агрегирующих другие ... — вполне типичная ситуация. На Java будет тот самый развесистый граф, а на C++ может быть просто по сути один вектор освобождающийся за O(1) — так как value semantics. dot>>>Сам же написал "GC линеен от количества N живых объектов", т.е. сдохший шмат графа можно освобождать также за примерно O(1). EP>>При этом сделав O(N) обход живых объектов, которого нет в случае с C++ dot>В YG это N малО, а OG обходится не часто,
Чем больше N — тем чаще. В любом случае это не O(1).
dot>и обычно минимально влияя на основные потоки.
Memory throughput не бесконечная, более того — часто является bottleneck'ом. GC во время обхода интенсивно использует память, сужая этот bottleneck.
Здравствуйте, T4r4sB, Вы писали:
TB>·>Дело не в дисциплине, а в гарантии правильности. Если у тебя есть некий критичный кусочек кода — его можно проанализировать очень внимательно (или даже доказать) и быть уверенным, что именно он так и отработает. В случае С++ — это невозможно принципиально — расстрел памяти ведёт к непредсказуемым результатам любой части кода. TB>Да-да, я это слышал, это из первой лекции для студентов "недостатки С++". /_-
И с тех пор ты никогда в жизни не расстрелял память или не видел, когда вполне дисциплинированый дев расстреливал?
TB>·>Знаю, что уныло, но факт остаётся фактом. И не диверсия, а просто ошибка. TB>Случайно сделать 10 ошибок в слове "int" и получить "const_cast"? Или как?
const_cast не единственный способ потерять константность.
TB>·>Проблему сам найдёшь? Откровение открылось? TB>Нет, проблемы не вижу, кроме случая, когда в команде бардак и разработчики диверсанты, "случайно" пишущие const_cast, срущие в данные, которые другой поток считает константными, делящие на ноль и невзначай вставляющие в код команду форматирования винчестера.
В ядре линуха такие баги находили (а там, видимо бардак и диверсанты).
Вот поэтому и не нужно считать константным, а тупо использовать иммутабельный тип (что при наличии деструкторов в языке сделать невозможно).
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ·, Вы писали:
dot>>>Мало того, тот кому не повезло освобождать ссылку последним — не придётся тратить ВНЕЗАПНО время на освобождение, EP>>Пусть просто поставит в очередь, делов-то dot>Поставит что? Сразу весь развесистый граф? А если надо только часть освободить? Ставить каждый самый мелкий объект? А очереди не поплохеет?
Не каждый объект, а те которые не успеваем освободить.
EP>>>>На Java кстати какое решение можешь предложить? dot>>>На java память ресурс другого типа. "Освобождать память" там нельзя. EP>>А я поэтому и сказал конкретно "ресурс", а не просто "объект". Да и память не ресурс только если она не ограниченна — упрёшься в предел, получишь удар по latency, на который постоянно ссылаешься. dot>В low latency единственный ресурс обычно память.
Сильное утверждение. Да и это проблема ко всему относится, а не только к low latency.
dot>Из LL кода никто в своём уме коннекты к БД не открывает, с SOAP сервисам не обращается.
Файлы не открывают? Соединения не устанавливают?
dot>>>А для ресурсов в общем случае — наследники java.lang.ref.Reference. EP>>Каким образом они обеспечат prompt finalization? dot>А надо?
Надо, это задача.
dot>А зачем вообще ресурсы обязательно освобождать из финалайзеров?
Освобождай откуда угодно, но покажи как это реализуется в Java.
dot>Я не понял какую задачу ты описываешь.
Ту же самую, с потоками, при завершении последнего нужно освободить ресурс.
EP>>>>На них точно также получаются локи. Чтобы их не было, нужно использовать например lock-free / wait-free схемы. dot>>>Ну да... Только, как мне кажется. эти схемы гораздо проще если есть gc. EP>>При GC чуть проще реализация lockfree структур данных, за счёт ABA, но при этом сами GC в большинстве своём не lockfree (lockfree GC видел только в статьях) — то есть тут уже большой терминологический вопрос, остаётся ли структура данных lockfree при не non-lockfree GC dot>Если локи расставляются сами компилятором и лочится на предсказуемое время — почему бы и нет.
Потому что lock'и это не lockfree, принципиально. Смотри определение. Lockfree это прежде всего некоторые гарантии, а не например скорость — скорость вообще может быть меньше.
Да и кто сказал что время предсказуемо? Например GC заснул захватив lock, остальные ждут. Тут желательно хотя бы obstruction free
EP>>Да и такие структуры должны реализовывать профессионалы своего дела, так как это трудная/опасная тема. dot>В том то и дело. Это уже и реализовали в виде JVM — бери, да используй, а не изобретай велосипед.
А я предлагал изобретать? Я сказал что ABA проще решается при использовании GC, решать её при этом самому не обязательно.
Здравствуйте, Ikemefula, Вы писали:
EP>>Врёшь же. Я конкретно сказал что контроль над временем ортогонален "как на Си". I>Это просто твоё мнение, ничем не аргументированое.
Это аргумент. Ты можешь быть согласен с ним или нет.
I>Я его услышал,
Ты если не согласен с аргументом, или не понял, то так и скажи, и попроси разъяснения.
I>выдал симметричное, только мне понадобилось всего слово для этого.
Ты вообще не выдал никакого аргумента.
EP>>>>Расшифруй. I>>>Это значит, что те же задачи решаются и в Си, за доказательством смотри полноту по тьюрингу EP>>Причём тут полнота по Тьюрингу? В данном месте обсуждаем использование ref-counting там где в этом нет необходимости, то есть для подстраховки. I>Из этой самой полноты следует "в этом нет необходимости, то есть для подстраховки"
Это
Задача была сделать подстраховку, я сказал что такая подстраховка не реализуема на C, ты сказал что "реализуема, только иначе", а теперь говоришь суть реализации в том что в этом нет необходимости
EP>>При том что на нём передёргиваний счётчиков на порядки меньше. И соответственно меньше overhead'а привнесённого непосредственно подсчётом ссылок. I>А в Киеве дядька.
То есть ты не понимаешь в чём тут отличие у C++11
I>>>Есть куча кода написаного в тысячах контор: I>>>"Зануление счетчика, вызов деструктора, который вызывает зануление другого счетчика, который вызывает деструктор,и тд и тд" EP>>В этом плане ref-counting практически никаких тормозов не добавляет (пара лишних операций перед очисткой) — подобная каскадная очистка есть и без него. I>Твоя "подстраховка", требует ресурсов. Каскадная очистка — часть этой самой подстраховки. Вместо явной логики "вычислить и освободить явно" выбираем неявную "пусть деструкторы срабатывают каскадом по цепочке, авось пронесёт"
Ещё раз, эта каскадная очистка была бы и без ref-counting. Understand?
Здравствуйте, ·, Вы писали:
EP>>>>Escape analysis вообще из другой оперы. Это например про аллокации объектов на стэке вместо кучи — в таких случаях никакой unique_ptr не нужен EP>>>>Более того аллокация на стэке — это default для C++, и работает даже в случаях escape. dot>>>Не обязательно на стеке, ещё есть thread local heap. dot>>>Код может быть очень развесистохитрым, а unique_ptr выведется сам, как результат EA. EP>>И каким образом у тебя получился тут unique_ptr? dot>Если VM оптимизатор видит, что объект используется только в одном месте, он переносит ссылку на стек, т.е. по сути выводит unique_ptr.
И не в покер, а в преферанс. И не выиграл, а проиграл.
И не ссылку, а сам объект. И не unique_ptr, а просто стэковый объект.
EP>>>>Что? Причём тут теперь weak_ptr? Уже какая-то каша пошла. dot>>>shared_ptr у треда-владельца, а "зависимые" — имеют weak_ptr. EP>>Как weak_ptr относится к дискуссии? dot>Как я понял, ты предлагаешь выделить один тред как владелец объекта, т.е. тот, кто держит shared_ptr, а другие треды — просто пользователи объекта, им отдаётся weak_ptr (не голые же указатели передавать?!).
Именно голые не владеющие указатели — владелец то переживёт всех. Вполне обычная/нормальная/стандартная практика
Smart-pointer'ы прежде всего помогают избавиться от голых владеющих указателей, а не от того что ты подумал.
dot>>>>>Последствия обращения к неверным указателям — серьёзнее. Не простой краш, а хз что. EP>>>>А например OOM — тоже вполне серьёзно dot>>>ООМ происходит в яве ровно так же как и в плюсах. Всё то же самое. EP>>OOM он конечно везде OOM, но причины наступления бывают разные dot>OOM не кидается в случае если gc есть что освободить.
Упёрлись в потолок, GC начинает очень долго освобождать за O(N), отбирая ресурсы у других потоков, общая пропускная способность снижается (забиты каналы памяти, и аллокации происходят медленней), извне приходят всё новые задачи для которых в свою очередь нужна всё новая память, в конце концов либо OOM либо DoS.
dot>>>Отказываться от классов? Это как? В смысле раскладывать данные по массивам? Массивы не отказ от классов, а от полей классов. Эта техника не специфична для Java, в С++ тоже так делают: dot>>>http://programmers.stackexchange.com/questions/246474/data-oriented-design-impractical-with-more-than-1-2-structure-members EP>>Нет, именно от классов. То что ты привёл это иногда называется Structure of Arrays, в C++ кстати я такую трансформацию реализовывал в библиотечном виде, то есть раскидывание происходит автоматически. EP>>Я же говорю про обычные структуры, в Java нет даже их. То есть положить в один массив плотно друг к другу данные вида: EP>>
напрямую не получится, и это существенный источник тормозов. В зависимости от ситуации медленнее может быть на несколько порядков. На ровном месте вырастают целые деревья индерекций. EP>>Обходится например через огород low-level эмуляции на чём-то типа ByteBuffer. dot>Делаешь класс (зачем от классов то отказываться?!)
Такой Array и другие контейнеры нужно делать для каждого типа данных. У тебя будут FooArray, BarArray, FooPriorityQueue, BarPriorityQueue и т.д.
Либо как вариант добавлять лишней динамичности, но от неё будут свои тормоза, причём как размеры заранее не известны.
dot>Да, выглядит некрасиво, но обычно составляет <0.1% кода и сложностей никаких не доставляет.
Это одна из главных причин тормозов в языках с превалирующей pointer semantics. Положил класс с несколькими полями-объектами в массив — уже на ровном месте выросло целое дерево индерекций и аллокаций.
dot>Почему-то я уверен, что "трансформацию реализовывал в библиотечном виде" будет выглядеть гораздо хуже и иметь довольно нетривиальный код.
Нет, не хуже, также как и обычный вариант. В реализации — да, код не самый простой, но она пишется один раз.
Да и это же Array of Structures, нужно не так часто. Речь же про то, что в Java же нет самых обычных структур — для быстрого кода это супер-критично, именно поэтому и нарезают вручную.
dot>>>И, кстати, я заметил, что java-specific там практически ничего не было. Основной упор именно на то, что надо понимать как работает железо, а какой язык — не важно, техники отличаются деталями реализации, принцип же тот же. EP>>Понимать как работает железо обязательно нужно. Также нужно понимать как отображаются конструкции языка в железо — и вот с этим у Java проблемы, для быстрого кода приходится отказываться даже от элементарных абстракций и городить low-level код, который даже ниже уровень чем то что есть в языке C. dot>Да нет каких-то сверх-проблем. Да, нужно писать код определённым образом, реализовывать определённые решения, но это верно для любого оптимизируемого кода на любом языке. Java в этом плане ничуть ни лучше, ничуть не хуже, чем C++.
Хуже, намного. Разве пример со структурами тебя не убедил? — это реально уровень ниже чем C, на ровном месте. Так это только один аспект.
Быстрый код на C++ можно писать на высоком уровне абстракции — не нужно отказываться ни от замыканий, ни от классов, ни от ФВП и полиморфизма — и при этом он будет давать точно такое же (либо практически такое же) быстродействие как и такой же код написанный на низком уровне вручную — в этом одна из главных фишек C++.
Здравствуйте, Nuzhny, Вы писали:
N>P.S. Я уже молчу про центральный пассаж, что bubble sort — это типичный код. Скажите мне пожалуйста, кто из плюсовиков или шарпистов вообще писал хоть какую-нибудь сортировку? А buble sort?
Меня больше позабавило, что сортировка на unsafe указателях — это типичный код на C#
Здравствуйте, ·, Вы писали:
EP>>>>Возможно. Даже если опасаешься false positives/negatives grep'а — то легко написать простейший верификатор на базе Clang AST Matchers. dot>>>Задоблаешься. Скажем, частенько при интеграции с другими библиотеками, особенно C, может вылезти: dot>>>
dot>>>Obj *obj2 = (Obj *)obj2;
dot>>>
dot>>>И нифига ненаверифицируешь. EP>>Старый cast легко отлавливается компилятором, например опция GCC -Werror-old-style-cast dot>Ну пусть будет reinterpret_cast<Obj *>(obj) какая разница-то?
1. reinterpret_cast не снимает константность, мы же о ней говорим?
2. ловится тем же grep'ом
dot>>>И вообще, никакая это не верификация, а статический анализ. EP>>Это верификация соответствия требованиям. dot>Под верификацией обычно понимается проверка формальными методами. А так — анализ, тестирование.
Это не тестирование точно. Под определение верификации попадает, причём и на рус/eng. Ну да ладно, это уже чистая терминология.
EP>>>>В Java у volatile семантика другая нежели чем у C++. Для этой же цели используются std/boost::atomic. То есть будет не volatile ImmutableClass *, а atomic<ImmutableClass *>. dot>>>О, наконец-то стало можно. Если не ошибаюсь, в С++ это появилось c 2011, в Яве же с 2004. EP>>В стандарте C++11 появились std::atomic, memory model, да и вообще понятие потока. EP>>Библиотечные же реализации работают и в C++1998. dot>Ну так нечестно.
Почему не честно-то? Например де-юре потоков в языке C++ не было, де-факто были на каждой платформе их поддерживающей, плюс были и есть кроссплатформенные библиотеки типа Intel Threading Building Blocks, Boost.Thread.
dot>А то я скажу, что "gc можно отключить в java" если использовать библиотеки с off-heap.
Так ведь так и делают, работают против языка, убегают от GC, потому что он доставляет вполне конкретные проблемы.
В случае же с boost::thread и т.п. — наоборот работа вместе с языком, а не вопреки ему — std::thread вошёл в стандарт практически в таком же виде, каком он и так был де-факто в библиотеках C++98.