Здравствуйте, ·, Вы писали:
I>>>>>>Разве не ты говорил про off-heap приседания ? I>>>>dot>Off-heap не особо нужен в подавляющем большинстве случаев, да и на архитектуру никак не влияет. I>>>>Off-heap и есть архитектура. Все что ты выделяешь теперь, надо делать через этот off-heap dot>>>Да какая архитектура блин. Вместо "MyObj obj = new MyObj()" пишешь "MyObj obj = newMyObj()", в общем-то и вся разница по факту. EP>>А вместо например MyObj[] что? dot>ну например List<MyObj>
1. На GC всё равно будет висеть граф множества объектов, пусть и меньшего размера.
2. Теперь на этих ссылках будут висеть финализаторы, так? Для того чтобы знать когда очищать off-heap. То есть теперь ко всему прочему ещё и освобождение будет линейным.
3. Лишние индерекции внутри List никуда не делись.
EP>>И кто и как создаст эти newMyObj? dot>Программист напишет. Притом можно описывать, скажем, интерфейсы, а их имплементации генерить при запуске.
"в общем-то и вся разница по факту." — да?
Придётся расписывать/генерировать не только пользовательские классы, но и воплощения всех контейнеров/алгоритмов в которых они участвую — иначе тормоза.
Покажи какую-нибудь библиотеку/утилиту которая берёт всё это на себя.
Здравствуйте, alex_public, Вы писали:
_>Здравствуйте, Serginio1, Вы писали:
_>>>Ох, ты похоже не понимаешь... Если переписать тот же 1C или скажем Питон с C++ на C#, то получившийся скриптовой язык станет только медленнее. Так понятно? ) S>> А зачем переписывать? Если можно сразу писать на C#? Мало того можно сравнить Питон на C++ и IronPython на C#
_>Затем, что все тормоза 1C следуют из этого. Но и основные преимущества (DSL) тоже происходят из этого же.
Из чего? Из-за того, что интерпритатор медленнее компилятора.
_>>>Ещё раз повторяю. Сравнение скорости 1C и C# — это бред. S>> Зато сравнивать C# и С++ это нормально. У них разная ниша. Если на C# написать код аля 1С это не далеко от оригинала. Приблизительно как TS от JC S>>то разница между 1С и С++ это уже разные миры.
_>C++ и C# — это оба языка общего назначения, а не DSL. Конечно их целевые ниши различаются, но на фоне разницы со скриптовыми языками это не принципиально.
_>И я очень сомневаюсь, что какие-то профильные задачи решаются похоже на C# и 1C. Озвучь задачи тогда уж... Может это не профильные задачи 1C, а что-то Обычное? ) Тогда оно и на C++ будет выглядеть тривиально.
Я тебе уже приводил Microsoft Dynamics CRM _>>>Дело не только в этом, а ещё и в том, что 1C — это DSL. И соответственно в своей узкой области он в любом случае будет удобнее, чем любые универсальные языки. S>> Я утвеждаю, что C# для решения подобных задач удобнее как TS vs JS. А я пишу на этих языках. Единственно, что нужно заменить англоязычные операторы на русские.
_>Сомнительно) Озвучь ка эти самые задачи...
Оооо ты не знаешь задачи 1С? Тогда сложно объяснять. На самом деле 1С и прочие системы имеют свой язык прежде всего, что бы на них зарабатывать. Когда они создавались C# был еще в так же как TS vs JS.
Как я тебе показывал ссылки есть некая иерархия классов Справочники,документы, регистры сведений,оборотов, остатков, Константы, Планы счетов итд
Я формирую аналогичные классы на C# только разумеется без модулей объектов.
Которые также можно написать. Некоторые люди даже переводят из одного языка в другой. https://infostart.ru/public/321282/
В свое время народ пытался сделать убийцу 1С.
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, alex_public, Вы писали:
_>Здравствуйте, Serginio1, Вы писали:
S>>>> Давай сравним с Microsoft Dynamics решающий аналогичную задачу _>>>Если ты про Microsoft Dynamics ERP, то причём тут .net? ) S>>Например https://msdn.microsoft.com/en-us/library/hh547453.aspx
_>По ссылке у тебя Microsoft Dynamics CRM. Там да, .net, но какое отношение CRM имеет к 1C?
Это учетные системы.
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, alex_public, Вы писали:
_>Здравствуйте, Serginio1, Вы писали:
_>>>Ещё раз повторяю. Сравнение скорости 1C и C# — это бред. S>>Казалось бы. Вот посмотри http://rsdn.ru/forum/dotnet/6185588.1
S>>С помощью этого можно делать Сайт, Вэб сервисы на Asp.Net но никому это не нужно. 1С ник не знает .Net, а писатели Вэб приложения не знают структуру 1С. S>>Поэтому проще написать вэб сервис на 1С, чем использовать быстрый доступ через Net. S>>А ты говоришь скорость. Никому она не нужна. Ни одного комментария.
_>А где кто-то говорил, что в задачах 1C нужна скорость? )
Она нужна и очень. Пока ниша 1С это мелкий и средний бизнес. А для выхода на компании от 1000 пользователей скорости уже не хватает.
А там несколько и суммы другие. Прежде всего поддержка
и солнце б утром не вставало, когда бы не было меня
Здравствуйте, alex_public, Вы писали:
_>>>Т.е. ты считаешь, что ограничения виртуальной машины связаны исключительно с доступом к OS API? ) _>·>Я считаю, что ты фигню какую-то говоришь, что я перестал тебя понимать. Да, Джава не всемогутер, а хороший инструмент. Да, можно найти области, где этот инструмент не работает или работает плохо. Скажем, дравйвер видеокарты на Яве писать бессмысленно, если вообще возможно, а вот критичный LL код — вполне. Когда мы обсуждаем конкретные пункты, то можно уже сравнивать. Тут приводились какие-то пункты, по ним Джава, как выяснилось, не уступает и имеет некоторые преимущества. _>Лично я знаю ровно одно преимущество Java/C#. Это возможность достаточно безопасного использования низкоквалифицированных программистов. Возможно с точки зрения технологии это звучит и не очень. Но с точки зрения бизнеса это очень существенное преимущество, правда актуальное в основном для не IT компаний. Так что благодаря этому преимуществу Java/C# заслуженно занимают доминирующее положение во внутрикорпоративном энтерпрайзе. И если их там кто-то и подвинет, то разве что JS, если обретёт соответствующую инфраструктуру (кстати шаги к этому уже наблюдаются). ))) А вот использование Java/C# в других областях, особенно со сложным кодом или тяжёлыми условиями, мне кажется неразумным. Но в нашей индустрии вообще много всего неразумного встречается. )))
Нет, это просто языки разного уровня. Ровно то же самое можно сказать заменив Java/C# -> C++, C++ -> ассемблер.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, alex_public, Вы писали:
_>·>Затем что в Джаве эта проблема решена. Такого типа некорректный код — невозможен. Это является сильным преимуществом в некоторых ситуациях. _>И в Java и в C++ будет одинаковый расклад для таких ситуаций — исключение (правда в C++ системное, а не родное). _>
_>delete x;
x->>v=1;
_>
_>
_>x=null;
_>x.v=1;
_>
Неправда. Будет не исключение, а undefined behaviour, в дебаг-моде в лучшем случае может и грохнется. А если учесть всякие кастомные аллокаторы...
Помню натыкался на багу:
class X
{
char *begin;
char *end;
int size() {return end - begin;}
}
Как думаешь что вернёт size() для удалённого объекта даже в дебаг моде?
Или что-то новое в стандарт ввели?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>>>Это уберёт линейную сложность? dot>>Да. Линейная сложность это худший случай, когда у тебя постоянно меняются указатели в большинтсве живых объектов и постоянно создаются новые. EP>Не обязательно в большинстве живых, достаточно в относительно малом количестве.
Нет, поиск осуществляется по тронутым с предыдущего цикла сборки регионам объектов.
dot>>В такой ситуации С++ тоже не поздоровится — и фрагментация кучи, и возрастающая сложность malloc/free. EP>Алгоритмическая сложность не "растёт" — это характеристика алгоритма. Например если брать Buddy — то она логарифмическая. EP>Да, — чем больше N, тем будет дольше, но чтобы прикинуть как это "дольше" зависит от N — как раз и используют алгоритмическую сложность.
Хорошо, скорость падать, а не сложность расти.
dot>>>>Если ты в С++ заведёшь очередь для очистки — столкнёшься ровно с той же проблемой. Не вижу разницы. EP>>>В очереди объекты которые уже готовы для очистки. А вот сколько там живых reachable объектов — не важно, пусть хоть десять миллиардов. dot>>Ты представляешь как поплохеет системе когда туча тредов будет пихать миллиарды объекты в одну единственную очередь, EP>Ещё раз, очередь у каждого потока своя, thread-local.
Если потоки не обмениваются объектами — это скучный случай, gc будет летать благодаря TLAB.
dot>>обрабатываемую одним несчастным потоком? EP>Необязательно одним
А сколькими? И как будешь раздавать по потокам? Как contention разруливать?
dot>>>>И кто из этой очереди будет обрабатывать элементы? Сам thread — не может отвлекаться, он LL, в любую наносекунду может прийти новый запрос. EP>>>От задачи зависит, может и сам thread. А может и отдельный thread, при этом contention с другими потоками всё равно не будет — так как очередь SPSC. dot>>Мы вроде рассматриваем случай, когда sharedPtr.release может начаться из разных тредов. Как ты собираешься использовать single writer структуру? EP>Объект управляемый shared_ptr всегда удаляется одним потоком, каким из них — определяется тем самым атомарным wait-free счётчиком. Это всё есть даже без отложенной очистки.
Я знаю что он удаляется одним потоком. Как сделать так, чтобы он не стал удаляться в критическом потоке?
dot>>>>Это тоже подзадача GC — решать сразу или отложить. EP>>>А ещё в подзадачах GC есть сложение целых. dot>>Хорошо, не подзадача, а средство достижения заявленных характеристик. EP>Короче, не нужно сами эти средства называть GC. Другой пример — копирующий аллокатор может быть без GC.
Я говорю это к тому, что эти средства уже доступны при использовании GC.
EP>>>Каким образом ты реализуешь деструкторы? Какое будет использование? Каким образом реализуешь например Expression Templates и прочие compile-time EDSL? dot>>А зачем compile time? Я знаю, что C++ compile time — Turing Complete, но в общем-то и в Java можно сделать плугин для компилятора или load-time кодогенерацию. EP>Так фишка EDSL (Embedded Domain Specific Language) именно в том что он embedded.
Так он и будет Embedded, пишешь обычный код на java с аннотациями, а во время компиляции или загрузки классов — происходит магия.
Ну вот... рассуждали о управлении памятью, LL и внезапно слились до type system и EDSL.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, alex_public, Вы писали:
_>>>·>А в плюсах ты явно будешь писать "vector<MyClass>()", "new vector<MyClass>()", "vector<MyClass*>()" или "new vector<MyClass*>()" — принимать это решение каждый раз на основе кучи предположений, умолчаний, ещё на стадии разработки архитектуры приложения, хотя в большинтсве случаев такие решения может принять JIT на основе знаний полученных на стадии исполнения кода у конечного пользователя. _>>>Да, всё верно. И я уверен что во всех случаях сделаю это лучше чем JIT. _>·>Про все случаи я уверен, что нет. Просто устанешь и начнёшь лепить что-то дефолтное, забивая на эффективность. _>Фокус в том, что дефолтное в C++ как раз и есть самое быстрое для 99% задач. )
Это которое? Я ещё забыл варианты "MyClass[]", "MyClass*[]", все комбинации "xxx_ptr<vector<xxx_ptr<MyClass> >"
_>·>А как же абстракция и прочие умные слова? Вместо простого "список объектов" начинаешь рассуждать как же оно будет в памяти, куча, стек, индирекции, индексы, указатели... И это чуть ли не для каждой строчки кода, а не для того 1% строчек где это действительно важно. _>А что, разве не все программисты такое обдумывают на автомате в процессе проектирования?
А ты часто обдумываешь в какие регистры процессора какая переменная попадёт?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, Evgeny.Panasyuk, Вы писали:
dot>>ну например List<MyObj> EP>1. На GC всё равно будет висеть граф множества объектов, пусть и меньшего размера.
Ничего, и не такое жевал.
EP>2. Теперь на этих ссылках будут висеть финализаторы, так? Для того чтобы знать когда очищать off-heap. То есть теперь ко всему прочему ещё и освобождение будет линейным.
Можно и финализаторы, а можно и явно реализовать delete, как в плюсах. Какое освобождение сделаешь, такое и будет.
EP>3. Лишние индерекции внутри List никуда не делись.
List будет с большим числом элементов, иначе смысла извращаться нет. А на фоне этого большого числа данных один индирект не будет заметен в микроскоп.
EP>>>И кто и как создаст эти newMyObj? dot>>Программист напишет. Притом можно описывать, скажем, интерфейсы, а их имплементации генерить при запуске. EP>"в общем-то и вся разница по факту." — да? EP>Придётся расписывать/генерировать не только пользовательские классы, но и воплощения всех контейнеров/алгоритмов в которых они участвую — иначе тормоза. EP>Покажи какую-нибудь библиотеку/утилиту которая берёт всё это на себя.
Берёт что? Код-то тривиальный.
Вместо
class BusinessData
{
private int a;
private int b;
int getA() {return a}
int getB() {return b}
}
пишем
class BusinessData
{
private Buffer buf;//замаплен например сразу на приходящий из сети блок байт или offheap хранилище или ещё чего.private int pos;
int getA() {return buf.getInt(pos)}
int getB() {return buf.getInt(pos+4)}
}
List<> просто проставляет pos соответственно.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ·, Вы писали:
EP>>>>Это уберёт линейную сложность? dot>>>Да. Линейная сложность это худший случай, когда у тебя постоянно меняются указатели в большинтсве живых объектов и постоянно создаются новые. EP>>Не обязательно в большинстве живых, достаточно в относительно малом количестве. dot>Нет, поиск осуществляется по тронутым с предыдущего цикла сборки регионам объектов.
Об этом и речь — что по регионам, а не отдельным затронутым объектам. Поэтому я и сказал "малом количестве", а не в одном.
И то — это отслеживание имеет свою цену, которая размазывается по всему коду.
dot>>>В такой ситуации С++ тоже не поздоровится — и фрагментация кучи, и возрастающая сложность malloc/free. EP>>Алгоритмическая сложность не "растёт" — это характеристика алгоритма. Например если брать Buddy — то она логарифмическая. EP>>Да, — чем больше N, тем будет дольше, но чтобы прикинуть как это "дольше" зависит от N — как раз и используют алгоритмическую сложность. dot>Хорошо, скорость падать, а не сложность расти.
При логарифмической сложности между N=1000 и N=1000000 — разница примерно в два раза. А в случае же линейной сложности — примерно в тысячу раз
Feel the paindifference.
dot>>>>>Если ты в С++ заведёшь очередь для очистки — столкнёшься ровно с той же проблемой. Не вижу разницы. EP>>>>В очереди объекты которые уже готовы для очистки. А вот сколько там живых reachable объектов — не важно, пусть хоть десять миллиардов. dot>>>Ты представляешь как поплохеет системе когда туча тредов будет пихать миллиарды объекты в одну единственную очередь, EP>>Ещё раз, очередь у каждого потока своя, thread-local. dot>Если потоки не обмениваются объектами — это скучный случай,
А зачем обмениваться практически мёртвыми объектами?
dot>>>обрабатываемую одним несчастным потоком? EP>>Необязательно одним dot>А сколькими? И как будешь раздавать по потокам? Как contention разруливать?
Так зависит от задачи — где-то просто достаточно thread pool. Где-то же будет один очищатель на один рабочий поток и т.п. И то, это только если такая проблема вообще возникнет.
Но это всё уже не важно — главное разгрузить рабочий поток
dot>>>>>И кто из этой очереди будет обрабатывать элементы? Сам thread — не может отвлекаться, он LL, в любую наносекунду может прийти новый запрос. EP>>>>От задачи зависит, может и сам thread. А может и отдельный thread, при этом contention с другими потоками всё равно не будет — так как очередь SPSC. dot>>>Мы вроде рассматриваем случай, когда sharedPtr.release может начаться из разных тредов. Как ты собираешься использовать single writer структуру? EP>>Объект управляемый shared_ptr всегда удаляется одним потоком, каким из них — определяется тем самым атомарным wait-free счётчиком. Это всё есть даже без отложенной очистки. dot>Я знаю что он удаляется одним потоком. Как сделать так, чтобы он не стал удаляться в критическом потоке?
Критический поток поставит его в очередь, а сам удалять не будет.
dot>>>>>Это тоже подзадача GC — решать сразу или отложить. EP>>>>А ещё в подзадачах GC есть сложение целых. dot>>>Хорошо, не подзадача, а средство достижения заявленных характеристик. EP>>Короче, не нужно сами эти средства называть GC. Другой пример — копирующий аллокатор может быть без GC. dot>Я говорю это к тому, что эти средства уже доступны при использовании GC.
Аналогия: "когда нарезаешь массив на структуру, переизобретаешь C++"
EP>>>>Каким образом ты реализуешь деструкторы? Какое будет использование? Каким образом реализуешь например Expression Templates и прочие compile-time EDSL? dot>>>А зачем compile time? Я знаю, что C++ compile time — Turing Complete, но в общем-то и в Java можно сделать плугин для компилятора или load-time кодогенерацию. EP>>Так фишка EDSL (Embedded Domain Specific Language) именно в том что он embedded. dot>Так он и будет Embedded, пишешь обычный код на java с аннотациями, а во время компиляции или загрузки классов — происходит магия.
Как сделаешь например элементарный анализ размерностей? Чтобы вот такое выражение выдавало ошибку на этапе компиляции 20m/3s + 2kg?
dot>Ну вот... рассуждали о управлении памятью, LL и внезапно слились до type system и EDSL.
Так это ты же "внезапно" кидаешься громкими заявлениями "и прочие С++ фишки можно реализовать в java"
Здравствуйте, ·, Вы писали:
dot>>>ну например List<MyObj> EP>>1. На GC всё равно будет висеть граф множества объектов, пусть и меньшего размера. dot>Ничего, и не такое жевал.
Так в off-heap тогда смысла мало
EP>>2. Теперь на этих ссылках будут висеть финализаторы, так? Для того чтобы знать когда очищать off-heap. То есть теперь ко всему прочему ещё и освобождение будет линейным. dot>Можно и финализаторы, а можно и явно реализовать delete, как в плюсах. Какое освобождение сделаешь, такое и будет.
В итоге получаем и линейный обход живых, и линейных обход мёртвых, да ещё и второй аллокатор/деаллокатор (кстати какой?), и в довесок ещё расстрел памяти.
Круто — чё
EP>>3. Лишние индерекции внутри List никуда не делись. dot>List будет с большим числом элементов, иначе смысла извращаться нет. А на фоне этого большого числа данных один индирект не будет заметен в микроскоп.
Это один индерект на каждый элемент, а не просто один индерект. Разница может быть на порядки
EP>>>>И кто и как создаст эти newMyObj? dot>>>Программист напишет. Притом можно описывать, скажем, интерфейсы, а их имплементации генерить при запуске. EP>>"в общем-то и вся разница по факту." — да? EP>>Придётся расписывать/генерировать не только пользовательские классы, но и воплощения всех контейнеров/алгоритмов в которых они участвую — иначе тормоза. EP>>Покажи какую-нибудь библиотеку/утилиту которая берёт всё это на себя. dot>Берёт что? Код-то тривиальный.
Берёт описание структур и генерирует все необходимые комбинации класс-контейнер/алгоритм.
dot>Вместо dot>
dot>class BusinessData
dot>{
dot> private int a;
dot> private int b;
dot> int getA() {return a}
dot> int getB() {return b}
dot>}
dot>
Тут вообще достаточно:
struct BusinessData
{
int a, b;
};
Зачем get/set?
dot>пишем
Та самая ручная нарезка на структуры:
dot>
dot>class BusinessData
dot>{
dot> private Buffer buf;//замаплен например сразу на приходящий из сети блок байт или offheap хранилище или ещё чего.
dot> private int pos;
dot> int getA() {return buf.getInt(pos)}
dot> int getB() {return buf.getInt(pos+4)}
dot>}
dot>
Так это же не спасает от тех же лишних индерекций которые есть в List<BusinessData>
Здравствуйте, alex_public, Вы писали:
_>>>Я ничего подобного не упоминал, т.к. это совсем не моя область. Да, и кстати, насколько я понял данные продукты упоминались в том контексте, что мол посмотрите как надо извратить Java, чтобы была возможность конкурировать в данной области. _>·>Вот возьми что-нибудь из http://codedependents.com/2014/01/27/11-best-practices-for-low-latency-systems/ и подумай что именно тут java-specific? Все те же извраты будут и в плюсах. _>Ну если смотреть на этот список с точки зрения C++, то там вообще нет никаких извращений. Вполне нормальные архитектурные элементы не приводящие ни к каким ужасам.
"Извращения" это непривычный код, не так как учат в букварях.
Почитай, кстати там комменты — о том же рассуждают. Хорошо сказано:
Java is a great platform none the less, but I think it’s biggest advantage is that ordinary business logic (90% of your code?) can still depend on GC and safety features and make use of highly tuned and tested libraries written with Unsafe. This is a great trade-off between getting the last 5% of perf and being productive. A trade-off that makes sense for a lot of people but a trade-off none the less. Writing complicated application code in C/C++ is a nightmare after all.
_>Если же взглянуть с точки зрения Java, то один пункт действительно становится извратом, реализация которого будет требовать страшного кода. Это пункт "Keep your reads sequential". Кстати как раз это мы обсуждали в соседнем сообщение.
И это ВНЕЗАПНО нужно делать и в плюсах, банальный vector<MyClass> может работать плохо, если, например, в классе куча полей, а тебя нужно последовательно читать только одно, вылезет structure of arrays и т.п.
_>Из опыта каких компаний?
Упомянутых в топике.
_>Где java системы реалтаймого видео? )
"«Ага!!!» — укоризненно сказали суровые сибирские плюсовики!"
даже не знаю что на такое возразить.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>>>Не обязательно в большинстве живых, достаточно в относительно малом количестве. dot>>Нет, поиск осуществляется по тронутым с предыдущего цикла сборки регионам объектов. EP>Об этом и речь — что по регионам, а не отдельным затронутым объектам. Поэтому я и сказал "малом количестве", а не в одном. EP>И то — это отслеживание имеет свою цену, которая размазывается по всему коду.
Я уже упоминал, что gc плохо работает в случае большого числа короткоживущих и толпы долгоживущих. При наличии среднеживущих — производительность падает. Это можно лечить настройкой GC, испоьзованием разных приёмов (flyweight или пулов например) или даже изменением архитектуры приложения.
dot>>>>В такой ситуации С++ тоже не поздоровится — и фрагментация кучи, и возрастающая сложность malloc/free. EP>>>Алгоритмическая сложность не "растёт" — это характеристика алгоритма. Например если брать Buddy — то она логарифмическая. EP>>>Да, — чем больше N, тем будет дольше, но чтобы прикинуть как это "дольше" зависит от N — как раз и используют алгоритмическую сложность. dot>>Хорошо, скорость падать, а не сложность расти. EP>При логарифмической сложности между N=1000 и N=1000000 — разница примерно в два раза. А в случае же линейной сложности — примерно в тысячу раз EP>Feel the paindifference.
Бывает. Лечится. Алгоритмы — сила.
dot>>>>Ты представляешь как поплохеет системе когда туча тредов будет пихать миллиарды объекты в одну единственную очередь, EP>>>Ещё раз, очередь у каждого потока своя, thread-local. dot>>Если потоки не обмениваются объектами — это скучный случай, EP>А зачем обмениваться практически мёртвыми объектами?
В смысле? Какие уж есть.
dot>>>>обрабатываемую одним несчастным потоком? EP>>>Необязательно одним dot>>А сколькими? И как будешь раздавать по потокам? Как contention разруливать? EP>Так зависит от задачи — где-то просто достаточно thread pool. Где-то же будет один очищатель на один рабочий поток и т.п. И то, это только если такая проблема вообще возникнет. EP>Но это всё уже не важно — главное разгрузить рабочий поток
Ну собственно и получается — gc солнца вручную. Понятно, что gc можно реализовать на С++, он и реализован на С++ в JVM.
EP>>>>>От задачи зависит, может и сам thread. А может и отдельный thread, при этом contention с другими потоками всё равно не будет — так как очередь SPSC. dot>>>>Мы вроде рассматриваем случай, когда sharedPtr.release может начаться из разных тредов. Как ты собираешься использовать single writer структуру? EP>>>Объект управляемый shared_ptr всегда удаляется одним потоком, каким из них — определяется тем самым атомарным wait-free счётчиком. Это всё есть даже без отложенной очистки. dot>>Я знаю что он удаляется одним потоком. Как сделать так, чтобы он не стал удаляться в критическом потоке? EP>Критический поток поставит его в очередь, а сам удалять не будет.
И возникнет та же проблема, которой ты меня мучил — а что если он будет ставить в очередь быстрее, чем эта очередь разгребаться?
EP>>>>>А ещё в подзадачах GC есть сложение целых. dot>>>>Хорошо, не подзадача, а средство достижения заявленных характеристик. EP>>>Короче, не нужно сами эти средства называть GC. Другой пример — копирующий аллокатор может быть без GC. dot>>Я говорю это к тому, что эти средства уже доступны при использовании GC. EP>Аналогия: "когда нарезаешь массив на структуру, переизобретаешь C++"
Не С++. Почему именно С++, а не С, не Алгол, не Фортран, не Паскаль, не ассемблер? Просто подгоняешь код к специфике железа.
EP>>>Так фишка EDSL (Embedded Domain Specific Language) именно в том что он embedded. dot>>Так он и будет Embedded, пишешь обычный код на java с аннотациями, а во время компиляции или загрузки классов — происходит магия. EP>Как сделаешь например элементарный анализ размерностей? Чтобы вот такое выражение выдавало ошибку на этапе компиляции 20m/3s + 2kg? http://types.cs.washington.edu/checker-framework/current/checker-framework-manual.html#units-checker
dot>>Ну вот... рассуждали о управлении памятью, LL и внезапно слились до type system и EDSL. EP>Так это ты же "внезапно" кидаешься громкими заявлениями "и прочие С++ фишки можно реализовать в java"
Так можно же, turing complete же. Нужно ли — вопрос.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ·, Вы писали:
EP>>>>Не обязательно в большинстве живых, достаточно в относительно малом количестве. dot>>>Нет, поиск осуществляется по тронутым с предыдущего цикла сборки регионам объектов. EP>>Об этом и речь — что по регионам, а не отдельным затронутым объектам. Поэтому я и сказал "малом количестве", а не в одном. EP>>И то — это отслеживание имеет свою цену, которая размазывается по всему коду. dot>Я уже упоминал, что gc плохо работает в случае большого числа короткоживущих и толпы долгоживущих. При наличии среднеживущих — производительность падает.
А как ты называешь объекты которые живут долго, но не до конца работы приложения?
dot>Это можно лечить настройкой GC, испоьзованием разных приёмов (flyweight или пулов например)
Причём тут flyweight? Объекты-то изменяются.
dot>или даже изменением архитектуры приложения.
Конечно, поэтому и появляются всякие GC-free и off-heap'ы
dot>>>>>В такой ситуации С++ тоже не поздоровится — и фрагментация кучи, и возрастающая сложность malloc/free. EP>>>>Алгоритмическая сложность не "растёт" — это характеристика алгоритма. Например если брать Buddy — то она логарифмическая. EP>>>>Да, — чем больше N, тем будет дольше, но чтобы прикинуть как это "дольше" зависит от N — как раз и используют алгоритмическую сложность. dot>>>Хорошо, скорость падать, а не сложность расти. EP>>При логарифмической сложности между N=1000 и N=1000000 — разница примерно в два раза. А в случае же линейной сложности — примерно в тысячу раз EP>>Feel the paindifference. dot>Бывает. Лечится. Алгоритмы — сила.
Конечно сила, о чём я тебе и говорю.
И при логарифмической сложности, и при линейной, чем больше N тем медленнее — но разница между ними колоссальная. И в результате ситуации совершенно разные, а ты говоришь "тоже не поздоровится"
dot>>>>>Ты представляешь как поплохеет системе когда туча тредов будет пихать миллиарды объекты в одну единственную очередь, EP>>>>Ещё раз, очередь у каждого потока своя, thread-local. dot>>>Если потоки не обмениваются объектами — это скучный случай, EP>>А зачем обмениваться практически мёртвыми объектами? dot>В смысле? Какие уж есть.
Контекст потерял? Ещё раз.
У нас есть объект, счётчик ссылок которого ушёл в ноль. Так? Зачем обмениваться им с другими рабочими потоками?
EP>>Критический поток поставит его в очередь, а сам удалять не будет. dot>И возникнет та же проблема, которой ты меня мучил — а что если он будет ставить в очередь быстрее, чем эта очередь разгребаться?
Проблема вообще-то была в другом. Если по аналогии — в том что грубо говоря pop одного элемента очереди линейно зависит от её размера. Здесь же такой зависимости нет.
EP>>>>>>А ещё в подзадачах GC есть сложение целых. dot>>>>>Хорошо, не подзадача, а средство достижения заявленных характеристик. EP>>>>Короче, не нужно сами эти средства называть GC. Другой пример — копирующий аллокатор может быть без GC. dot>>>Я говорю это к тому, что эти средства уже доступны при использовании GC. EP>>Аналогия: "когда нарезаешь массив на структуру, переизобретаешь C++" dot>Не С++. Почему именно С++, а не С, не Алгол, не Фортран, не Паскаль, не ассемблер?
Аналогично — а почему тогда GC?
EP>>>>Так фишка EDSL (Embedded Domain Specific Language) именно в том что он embedded. dot>>>Так он и будет Embedded, пишешь обычный код на java с аннотациями, а во время компиляции или загрузки классов — происходит магия. EP>>Как сделаешь например элементарный анализ размерностей? Чтобы вот такое выражение выдавало ошибку на этапе компиляции 20m/3s + 2kg? dot>http://types.cs.washington.edu/checker-framework/current/checker-framework-manual.html#units-checker
Какой объем реализации? На C++ — меньше ста строк.
Да и не сравнится ведь:
@m int meters = 5 * UnitsTools.m;
vs
auto length = 5m;
Да и нет интеграции в систему типов — например перегрузка не работает
dot>>>Ну вот... рассуждали о управлении памятью, LL и внезапно слились до type system и EDSL. EP>>Так это ты же "внезапно" кидаешься громкими заявлениями "и прочие С++ фишки можно реализовать в java" dot>Так можно же, turing complete же. Нужно ли — вопрос.
Полнота по Тьюрингу вообще о другом — о тех программах которые можно реализовать. Разговор же про языковые фичи, с помощью которых происходит реализация
Здравствуйте, Evgeny.Panasyuk, Вы писали:
dot>>>>Перенос объекта на стек это другая оптимизация. Может делаться только для маленьких оъбектов. Если у тебя выделяется 100мб массив и EA покажет, что ссылка на массив не убегает за пределы, то при выходе из стека объект грохнется. Т.е. по сути тот же unique_ptr. EP>>>То есть имелось в виду scoped_ptr — более ограниченная версия unique_ptr. Например unique_ptr можно возвращать из функции. dot>>EA работает и с множеством функций. Сделай "byte[] func() {return new byte[100500];}"- ничего страшного. EP>Ближе, но всё равно не то — unique_ptr можно передавать хоть вверх, хоть вниз, хоть сохранить в массиве, и это всё работает даже без инлайнинга. EP>Да даже в одном scope EA далеко не всегда сможет доказать что нет escape — банально упрётся в halting problem.
Не сможет — чуть тормозить начнёт. А если человек не сможет доказать передачу указателей — всё круто и непредсказуемо навернётся.
dot>>>>И молись что N не слишком большой и не получится stack overflow на пустом месте. _>>>Вектор не выделяет память на стеке. Ну во всяком случае со стандартным аллокатором. ))) Ты уверен, что ты реально знаком с C++? ) dot>>Ах, да, не заметил. Да ты меня запутал. Говорил о размещении объектов на стеке, и внезапно опять куча. EP>Да нет никакого запутывания. Если бы ты знал язык, без всякого углубления в advanced фичи — ты бы никогда не запутался.
Да знаю я язык прекрасно, опыта 5+ лет, просто уже несколько лет не использовал на работе.
EP>Это типичный миф обитающий в среде Java/C# — мол на каждый наш new, будет какой-нибудь *_ptr в C++.
... либо битый указатель.
EP>Этот феномен даже Страуструп высмеивал — https://youtu.be/OB-bdWKwXsU?t=1984 EP>Причём по сути это относится не только к *_ptr — а и в общем очень много мифов относительно C++. И в общем не так страшен чёртC++, как его малюют.
Вот и говорит, что нужен протокол для каждого объекта, решать кто что делает с объектами.
Тут ведь как:
было
//и вдруг мы решили поменять имплементацию doSomething чтобы что-то делалось в другом треде, и мы знаем, что он завершится до закрытия диалога. А потом вдруг тред стормозил или появился другой способ закрытия диалога...
dot>>>>массив не убегает за пределы, то при выходе из стека объект грохнется EP>>>Каким образом грохнется? Речь только про некомпактифицируемые кучи? dot>>Самое тривиальное — копирующим GC.
EP>Каким образом? Положили в память эти non-escape данные, дальше вызвали какую-нибудь стороннюю функцию (не давая ей наши non-escape) — она сделала дальше какие-то аллокации, которые пошли следом за нашими non-escape данным. EP>Выходим из scope — дальше какие действия? Какое преимущество даст EA в этом случае?
Тем что объект пойдёт на стек или в регистры. Какие аллокации?
EP>>>>>Именно голые не владеющие указатели — владелец то переживёт всех. Вполне обычная/нормальная/стандартная практика EP>>>>>Smart-pointer'ы прежде всего помогают избавиться от голых владеющих указателей, а не от того что ты подумал. dot>>>>Бррр... Я уж надеялся, что голые указатели постепенно изничтожаются. EP>>>Ты что, они же есть в реальности данной нам в ощущениях железом. Это же например самый быстрый способ указать на конкретный элемент массива, или передать куда-нибудь его часть. dot>>Железом нам даны адреса, а не указатели. EP>А указатель что по-твоему хранит?
Указатель ещё тип имеет. Ссылка в яве тоже адрес хранит и чё.
dot>>>>Это же сценарий high throughput, а не low latency. В такой ситуации и C++ грохнется — он будет делать ту же работу, просто в рабочих тредах, а на в отдельных gc-тредах как java. EP>>>Не грохнется — у него нет зависимости O(N) от количества живых объектов. dot>>new/malloc и delete/free работают не за O(1) внезапно. EP>Например для Buddy Allocation сложность логарифмическая. Есть же в том числе и O(1) алгоритмы, например TLSF.
Так и разные алгоритмы GC есть. И столько всего можно крутить... И я уверен, что вариантов даже больше, ибо ссылка в jvm более абстрактна чем указатели в плюсах, а значит больше простора для оптимизаций.
dot>>Как известно, gc работает хорошо при короткоживущих и длинноживущих объектах. EP>То есть при любых?
Нет, среднеживущие ещё есть.
dot>>Quick-sort имеет сложность O(N*N), как и пузырьковая... но это ещё ничего не значит. EP>Конечно значит, именно поэтому в реализациях std::sort используется introsort, которая хоть и основана на quicksort, но выдаёт линеаритмическую сложность
Да, неудачный пример. Лучше рассмотреть hashtable. Сложность O(n). и что? Просто крутят использование до тех пор пока не станет почти O(1). Примерно так и с GC.
EP>>>Ты постоянно говоришь о каком-то одном use-case'ы. EP>>>Даже если и мало — всё равно придётся опускаться на очень низкий уровень, исключительно из-за самого языка. dot>>Я и не изобретаю общую теорию всего, а решаю практические задачи. EP>А кто-то изобретает? В Java нет структур — это практический недостаток и факт
Зато он даёт больший простор для JVM/JIT/etc.
Ну вот есть структуры в C# — а толку? Где LL?
EP>>>Это ты о чём? EP>>>Например на C++ есть выгода от структур (в смысле хранения по значению) даже для вещей типа сбалансированных деревьев — так как уменьшает количество индерекций — само значение хранится в узле, а не указатель на значение. dot>>Так храни так же и в Java, т.е. приведённый выше array, не вижу проблему. EP>Проблема в том для каждого типа элемента нужен будет отдельный Java код.
Не смертельно.
dot>>Если ты про ссылочное дерево — то уже всё плохо, ибо оно не будет в памяти последовательно. EP>Деревья используются на практике. Хранение данных в самом узле позволяет избавится от лишней индерекции.
Это какие-то очень экзотические условия, у меня не получается представить когда в ссылочном дереве лишняя индирекция может стать серьёзной проблемой.
dot>>>>В C++ можно тоже кучу главных причин тормозов придумать. Скажем, передача by-value. Непонятно только к чему это. EP>>>Так тут есть простой выбор — by-value или by-reference. Это совершенно не тоже самое что и засучив рукава нарезать байт-буфера на структуры dot>>by-value — и тут ВНЕЗАПНО появляется O(N) от числа живых объектов. EP>Далеко не всегда. Да и почему внезапно-то?
Настолько же ВНЕЗАПНО как и в случае с GC.
dot>>by-reference — и тут начинаются проблемы с временем жизни, засучив рукава начинаешь решать проблемы владения. EP>В 99.999% случаях ничего "засучивать" не нужно, и никакие *_ptr не нужны.
Так в этих же случаях и в яве всё будет всё в YG, а значит никакого жуткого O(N).
dot>>Зато гарантируется отсутствие битых ссыслок. EP>Да, это плюс — с этим никто не спорил
Притом очень ценный плюс, что он перевешивает весь колбасокод с оффхипами и т.д.
EP>>>Да, на C++ нужно думать/помнить о владении (это не означает что каждый new на Java превращается в *_ptr). EP>>>Нет, уровни абстракции о которых я говорю решают далеко не только проблемы владения — они позволяют писать высокоуровневый И быстрый код. dot>>Это мы переходим в другую плоскость — выразительность языка, а не собственно JVM/GC. Для JVM есть и другие языки — Scala, Ceylon, Kotlin и прочее, которые позволяют и многие твои любимые абстракции. EP>А мы ВНЕЗАПНО не JVM обсуждаем, а вполне конкретную Java. Если брать JVM — то например если в неё скомпилировать C++ — то он там положит всех на лопатки
Не положит, к сожалению. Иначе бы уже давно компилировали.
Вся эта указательная магия и арифметика, юнионы, и прочий low level не может быть покрыт GC и JVM ссылками, поэтому при компиляции С/C++ память будет внутри byte[].
dot>>и прочее, которые позволяют и многие твои любимые абстракции EP>Я говорю не просто про абстракции, а про бесплатные, либо крайне дешёвые.
Ценой других абстракций.
dot>>Выразительность языка с другой стороны стреляет отстутсвием нормальной IDE.
EP>Это тоже передёргивание. EP>Нормальные IDE есть. Да автоматический анализ кода сложнее, но сложнее не из-за выразительности, а из-за внутренних особенностей сложившихся исторически. Языку больше тридцати лет, а если брать с базу C (из которой многие недостатки и произрастают) — то больше сорока. EP>При этом аналогичную выразительность можно достичь не делая проблемы анализаторам.
Может и можно... Я сходу не могу сделать такое заявление.
EP>Те же структуры есть в C#.
А толку-то от них...
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, Evgeny.Panasyuk, Вы писали:
dot>>>>ну например List<MyObj> EP>>>1. На GC всё равно будет висеть граф множества объектов, пусть и меньшего размера. dot>>Ничего, и не такое жевал. EP>Так в off-heap тогда смысла мало
Когда как. Очень зависит от юзкейсов. Невозможно сказать: "делай так и будет быстро".
EP>>>2. Теперь на этих ссылках будут висеть финализаторы, так? Для того чтобы знать когда очищать off-heap. То есть теперь ко всему прочему ещё и освобождение будет линейным. dot>>Можно и финализаторы, а можно и явно реализовать delete, как в плюсах. Какое освобождение сделаешь, такое и будет. EP>В итоге получаем и линейный обход живых, и линейных обход мёртвых, да ещё и второй аллокатор/деаллокатор (кстати какой?), и в довесок ещё расстрел памяти. EP>Круто — чё
Если очень надо и до машкодов дойдёшь. Но это очень экзотические случаи.
EP>>>3. Лишние индерекции внутри List никуда не делись. dot>>List будет с большим числом элементов, иначе смысла извращаться нет. А на фоне этого большого числа данных один индирект не будет заметен в микроскоп. EP>Это один индерект на каждый элемент, а не просто один индерект. Разница может быть на порядки
dot>>
dot>>class BusinessData
dot>>{
dot>> private Buffer buf;//замаплен например сразу на приходящий из сети блок байт или offheap хранилище или ещё чего.
dot>> private int pos;
dot>> int getA() {return buf.getInt(pos)}
dot>> int getB() {return buf.getInt(pos+4)}
dot>>}
dot>>
EP>Так это же не спасает от тех же лишних индерекций которые есть в List<BusinessData>
Двигаешь pos и получаешь элемент. Притом чтобы это всё имело смысл — pos должен двигаться строго вперёд, до следующего элемента, это и будет оптимизация sequencial access, а какие всякие другие алгоритмы ты тут хочешь воротить?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>>>Об этом и речь — что по регионам, а не отдельным затронутым объектам. Поэтому я и сказал "малом количестве", а не в одном. EP>>>И то — это отслеживание имеет свою цену, которая размазывается по всему коду. dot>>Я уже упоминал, что gc плохо работает в случае большого числа короткоживущих и толпы долгоживущих. При наличии среднеживущих — производительность падает. EP>А как ты называешь объекты которые живут долго, но не до конца работы приложения?
Old Gen. Данные этих объектов можно менять свободно, но не трогать сильно указатели.
dot>>Это можно лечить настройкой GC, испоьзованием разных приёмов (flyweight или пулов например) EP>Причём тут flyweight? Объекты-то изменяются.
Смотря как изменяются.
dot>>или даже изменением архитектуры приложения. EP>Конечно, поэтому и появляются всякие GC-free и off-heap'ы
Пусть появляются там где действительно надо.
EP>>>При логарифмической сложности между N=1000 и N=1000000 — разница примерно в два раза. А в случае же линейной сложности — примерно в тысячу раз EP>>>Feel the paindifference. dot>>Бывает. Лечится. Алгоритмы — сила. EP>Конечно сила, о чём я тебе и говорю. EP>И при логарифмической сложности, и при линейной, чем больше N тем медленнее — но разница между ними колоссальная. И в результате ситуации совершенно разные, а ты говоришь "тоже не поздоровится"
Так вот строгость ссылок явы позволяет применять гораздо более хитрые алгоритмы.
EP>>>А зачем обмениваться практически мёртвыми объектами? dot>>В смысле? Какие уж есть. EP>Контекст потерял? Ещё раз. EP>У нас есть объект, счётчик ссылок которого ушёл в ноль. Так? Зачем обмениваться им с другими рабочими потоками?
Так ты не знаешь в каком именно потоке и когда он уйдёт в ноль и что именно произойдёт в момент ухода в ноль. В этом и суть GC. Да, узнать можно, но это очень трудоёмко. Компьютер с этим справляется в подавляющем большинстве случаев лучше и надёжнее.
EP>>>Критический поток поставит его в очередь, а сам удалять не будет. dot>>И возникнет та же проблема, которой ты меня мучил — а что если он будет ставить в очередь быстрее, чем эта очередь разгребаться? EP>Проблема вообще-то была в другом. Если по аналогии — в том что грубо говоря pop одного элемента очереди линейно зависит от её размера. Здесь же такой зависимости нет.
Не одного элемента, а всей очереди. GC за цикл собирает не один объект.
dot>>>>>>Хорошо, не подзадача, а средство достижения заявленных характеристик. EP>>>>>Короче, не нужно сами эти средства называть GC. Другой пример — копирующий аллокатор может быть без GC. dot>>>>Я говорю это к тому, что эти средства уже доступны при использовании GC. EP>>>Аналогия: "когда нарезаешь массив на структуру, переизобретаешь C++" dot>>Не С++. Почему именно С++, а не С, не Алгол, не Фортран, не Паскаль, не ассемблер? EP>Аналогично — а почему тогда GC?
Потому что управление памятью — трудоёмкий процесс в прикладных приложениях. Автоматизировать его — дело святое.
EP>Какой объем реализации? На C++ — меньше ста строк.
Ну вот... сейчас ещё начнём строки считать... увольте.
EP>Да и не сравнится ведь: EP>
EP>@m int meters = 5 * UnitsTools.m;
EP>
EP>vs EP>
EP>auto length = 5m;
EP>
EP>Да и нет интеграции в систему типов — например перегрузка не работает
Перегрузка чего? Это же примитивы.
И вообще, чего опять о языке, мы же о GC вроде? Ну возьми Скалу, там есть перегрузка.
dot>>>>Ну вот... рассуждали о управлении памятью, LL и внезапно слились до type system и EDSL. EP>>>Так это ты же "внезапно" кидаешься громкими заявлениями "и прочие С++ фишки можно реализовать в java" dot>>Так можно же, turing complete же. Нужно ли — вопрос. EP>Полнота по Тьюрингу вообще о другом — о тех программах которые можно реализовать. Разговор же про языковые фичи, с помощью которых происходит реализация
GC это не совсем языковая фича. Возьми Скалу, если так нужны фичи.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ·, Вы писали:
dot>>>>>Перенос объекта на стек это другая оптимизация. Может делаться только для маленьких оъбектов. Если у тебя выделяется 100мб массив и EA покажет, что ссылка на массив не убегает за пределы, то при выходе из стека объект грохнется. Т.е. по сути тот же unique_ptr. EP>>>>То есть имелось в виду scoped_ptr — более ограниченная версия unique_ptr. Например unique_ptr можно возвращать из функции. dot>>>EA работает и с множеством функций. Сделай "byte[] func() {return new byte[100500];}"- ничего страшного. EP>>Ближе, но всё равно не то — unique_ptr можно передавать хоть вверх, хоть вниз, хоть сохранить в массиве, и это всё работает даже без инлайнинга. EP>>Да даже в одном scope EA далеко не всегда сможет доказать что нет escape — банально упрётся в halting problem. dot>Не сможет — чуть тормозить начнёт. А если человек не сможет доказать передачу указателей — всё круто и непредсказуемо навернётся.
А зачем человеку доказывать? Например:
auto foo()
{
return calc(make_unique<Something>());
}
Результат может содержать ссылку на этот unique, а может не содержать. Человек может например просто знать из документации что в результирующем объекте нет этого unique. Анализатору же придётся это доказывать проинлайнив весь код.
dot>>>>>И молись что N не слишком большой и не получится stack overflow на пустом месте. _>>>>Вектор не выделяет память на стеке. Ну во всяком случае со стандартным аллокатором. ))) Ты уверен, что ты реально знаком с C++? ) dot>>>Ах, да, не заметил. Да ты меня запутал. Говорил о размещении объектов на стеке, и внезапно опять куча. EP>>Да нет никакого запутывания. Если бы ты знал язык, без всякого углубления в advanced фичи — ты бы никогда не запутался. dot>Да знаю я язык прекрасно, опыта 5+ лет, просто уже несколько лет не использовал на работе.
Так ведь по-разному используют. Есть действительно много когда где лепят *_ptr на каждый чих. Виной тому отчасти например некоторые книги — есть авторы которые пишут сразу по трём языкам C++/C#/Java, просто копируя примеры с небольшими изменениями из книги в книгу. И отчасти те кто застрял в начале 90-х.
EP>>Это типичный миф обитающий в среде Java/C# — мол на каждый наш new, будет какой-нибудь *_ptr в C++. dot>... либо битый указатель.
*_ptr не спасают от битых указателей, их основанная цель это выражение семантики владения в коде, отсюда и имена shared/unique/scoped.
dot>Тут ведь как: dot>было dot>
dot>//и вдруг мы решили поменять имплементацию doSomething чтобы что-то делалось в другом треде, и мы знаем, что он завершится до закрытия диалога. А потом вдруг тред стормозил или появился другой способ закрытия диалога...
Это изменение контракта, которое прекрасно выражается в системе типов — у doSomething поменяется тип параметра и приведённый код не скомпилируется
dot>>>>>
массив не убегает за пределы, то при выходе из стека объект грохнется
EP>>>>Каким образом грохнется? Речь только про некомпактифицируемые кучи? dot>>>Самое тривиальное — копирующим GC. EP>>Каким образом? Положили в память эти non-escape данные, дальше вызвали какую-нибудь стороннюю функцию (не давая ей наши non-escape) — она сделала дальше какие-то аллокации, которые пошли следом за нашими non-escape данным. EP>>Выходим из scope — дальше какие действия? Какое преимущество даст EA в этом случае? dot>Тем что объект пойдёт на стек или в регистры. Какие аллокации?
Ещё раз. Прочитай свои слова выше (выделено), и ниже:
dot>Перенос объекта на стек это другая оптимизация. Может делаться только для маленьких оъбектов. Если у тебя выделяется 100мб массив и EA покажет, что ссылка на массив не убегает за пределы, то при выходе из стека объект грохнется. Т.е. по сути тот же unique_ptr.
Теперь скажи каким образом здесь поможет EA.
EP>>>>>>Именно голые не владеющие указатели — владелец то переживёт всех. Вполне обычная/нормальная/стандартная практика EP>>>>>>Smart-pointer'ы прежде всего помогают избавиться от голых владеющих указателей, а не от того что ты подумал. dot>>>>>Бррр... Я уж надеялся, что голые указатели постепенно изничтожаются. EP>>>>Ты что, они же есть в реальности данной нам в ощущениях железом. Это же например самый быстрый способ указать на конкретный элемент массива, или передать куда-нибудь его часть. dot>>>Железом нам даны адреса, а не указатели. EP>>А указатель что по-твоему хранит? dot>Указатель ещё тип имеет. Ссылка в яве тоже адрес хранит и чё.
Конечно, и в типе разница — на C++ можно иметь указатель на элемент массива, на Java — нет.
dot>>>>>Это же сценарий high throughput, а не low latency. В такой ситуации и C++ грохнется — он будет делать ту же работу, просто в рабочих тредах, а на в отдельных gc-тредах как java. EP>>>>Не грохнется — у него нет зависимости O(N) от количества живых объектов. dot>>>new/malloc и delete/free работают не за O(1) внезапно. EP>>Например для Buddy Allocation сложность логарифмическая. Есть же в том числе и O(1) алгоритмы, например TLSF. dot>Так и разные алгоритмы GC есть. И столько всего можно крутить... И я уверен, что вариантов даже больше, ибо ссылка в jvm более абстрактна чем указатели в плюсах, а значит больше простора для оптимизаций.
Так вот покажи GC не с сублинейной сложностью, желательно ещё чтобы был более-менее популярен.
dot>>>Quick-sort имеет сложность O(N*N), как и пузырьковая... но это ещё ничего не значит. EP>>Конечно значит, именно поэтому в реализациях std::sort используется introsort, которая хоть и основана на quicksort, но выдаёт линеаритмическую сложность dot>Да, неудачный пример. Лучше рассмотреть hashtable. Сложность O(n). и что? Просто крутят использование до тех пор пока не станет почти O(1). Примерно так и с GC.
Штуки типа cuckoo hashing не на ровном месте появились.
Для hashtable есть альтернативы с гарантированной сублинейной сложностью, и там где гарантия нужна — их и используют. Причём можно комбинировать — снаружи hashtable, а внутри узлов при превышении лимита что-то логарифмическое — тогда будет суб-линейная гарантия.
EP>>>>Ты постоянно говоришь о каком-то одном use-case'ы. EP>>>>Даже если и мало — всё равно придётся опускаться на очень низкий уровень, исключительно из-за самого языка. dot>>>Я и не изобретаю общую теорию всего, а решаю практические задачи. EP>>А кто-то изобретает? В Java нет структур — это практический недостаток и факт dot>Зато он даёт больший простор для JVM/JIT/etc.
Какой простор? Всё равно есть те же int/double, с семантикой как у структур
dot>Ну вот есть структуры в C# — а толку? Где LL?
В C# свои проблемы. У тех же структур есть много ограничений. Но тем не менее они есть, и не нужно их эмулировать руками.
EP>>>>Это ты о чём? EP>>>>Например на C++ есть выгода от структур (в смысле хранения по значению) даже для вещей типа сбалансированных деревьев — так как уменьшает количество индерекций — само значение хранится в узле, а не указатель на значение. dot>>>Так храни так же и в Java, т.е. приведённый выше array, не вижу проблему. EP>>Проблема в том для каждого типа элемента нужен будет отдельный Java код. dot>Не смертельно.
Отдельный код для каждой комбинации. Конечно не смертельно, можно и на ASM'е писать рабочий код.
dot>>>Если ты про ссылочное дерево — то уже всё плохо, ибо оно не будет в памяти последовательно. EP>>Деревья используются на практике. Хранение данных в самом узле позволяет избавится от лишней индерекции. dot>Это какие-то очень экзотические условия, у меня не получается представить когда в ссылочном дереве лишняя индирекция может стать серьёзной проблемой.
А например в хэш-таблице?
dot>>>>>В C++ можно тоже кучу главных причин тормозов придумать. Скажем, передача by-value. Непонятно только к чему это. EP>>>>Так тут есть простой выбор — by-value или by-reference. Это совершенно не тоже самое что и засучив рукава нарезать байт-буфера на структуры dot>>>by-value — и тут ВНЕЗАПНО появляется O(N) от числа живых объектов. EP>>Далеко не всегда. Да и почему внезапно-то? dot>Настолько же ВНЕЗАПНО как и в случае с GC.
Ок, допустим, в случае чего by-value заменим, не проблема — пара закорючек.
В случае с GC что будешь делать?
dot>>>by-reference — и тут начинаются проблемы с временем жизни, засучив рукава начинаешь решать проблемы владения. EP>>В 99.999% случаях ничего "засучивать" не нужно, и никакие *_ptr не нужны. dot>Так в этих же случаях и в яве всё будет всё в YG, а значит никакого жуткого O(N).
Нет же, случаи принципиально разные. Где-то наверху callstack делаем:
ничего не "засучивая".
Этот массив, по терминам GC, спокойно может попасть под классификацию OG
dot>>>Зато гарантируется отсутствие битых ссыслок. EP>>Да, это плюс — с этим никто не спорил dot>Притом очень ценный плюс, что он перевешивает весь колбасокод с оффхипами и т.д.
Этот же плюс есть в C#, в котором "колбасокод" нужен реже, как раз за счёт структур
EP>>>>Да, на C++ нужно думать/помнить о владении (это не означает что каждый new на Java превращается в *_ptr). EP>>>>Нет, уровни абстракции о которых я говорю решают далеко не только проблемы владения — они позволяют писать высокоуровневый И быстрый код. dot>>>Это мы переходим в другую плоскость — выразительность языка, а не собственно JVM/GC. Для JVM есть и другие языки — Scala, Ceylon, Kotlin и прочее, которые позволяют и многие твои любимые абстракции. EP>>А мы ВНЕЗАПНО не JVM обсуждаем, а вполне конкретную Java. Если брать JVM — то например если в неё скомпилировать C++ — то он там положит всех на лопатки dot>Не положит, к сожалению. Иначе бы уже давно компилировали. dot>Вся эта указательная магия и арифметика, юнионы, и прочий low level не может быть покрыт GC и JVM ссылками, поэтому при компиляции С/C++ память будет внутри byte[].
Конечно будет, и это одна из причин почему положит на лопатки.
Именно так и происходит
в Emscripten, который компилирует C++ в JS.
И JS кстати получается реально быстрый. На одном из тестов работает практически в два раза быстрее чем аналогичный код на C#. JS, в веб-браузере, Карл! И этом при том что в C# версии были структуры. Если же брать аналогичный код на Java — то всё будет ещё круче Можем кстати сравнить.
dot>>>Выразительность языка с другой стороны стреляет отстутсвием нормальной IDE. EP>>Это тоже передёргивание. EP>>Нормальные IDE есть. Да автоматический анализ кода сложнее, но сложнее не из-за выразительности, а из-за внутренних особенностей сложившихся исторически. Языку больше тридцати лет, а если брать с базу C (из которой многие недостатки и произрастают) — то больше сорока. EP>>При этом аналогичную выразительность можно достичь не делая проблемы анализаторам. dot>Может и можно... Я сходу не могу сделать такое заявление. EP>>Те же структуры есть в C#. dot>А толку-то от них...
Здравствуйте, ·, Вы писали:
EP>>>>2. Теперь на этих ссылках будут висеть финализаторы, так? Для того чтобы знать когда очищать off-heap. То есть теперь ко всему прочему ещё и освобождение будет линейным. dot>>>Можно и финализаторы, а можно и явно реализовать delete, как в плюсах. Какое освобождение сделаешь, такое и будет. EP>>В итоге получаем и линейный обход живых, и линейных обход мёртвых, да ещё и второй аллокатор/деаллокатор (кстати какой?), и в довесок ещё расстрел памяти. EP>>Круто — чё dot>Если очень надо и до машкодов дойдёшь. Но это очень экзотические случаи.
Так дело даже не сложности создания, а в том что это всё может тормозить. То есть получается взяли самое плохое от обхода живых и мёртвых объектов — обходим все.
EP>>>>3. Лишние индерекции внутри List никуда не делись. dot>>>List будет с большим числом элементов, иначе смысла извращаться нет. А на фоне этого большого числа данных один индирект не будет заметен в микроскоп. EP>>Это один индерект на каждый элемент, а не просто один индерект. Разница может быть на порядки dot>>>
dot>>>class BusinessData
dot>>>{
dot>>> private Buffer buf;//замаплен например сразу на приходящий из сети блок байт или offheap хранилище или ещё чего.
dot>>> private int pos;
dot>>> int getA() {return buf.getInt(pos)}
dot>>> int getB() {return buf.getInt(pos+4)}
dot>>>}
dot>>>
EP>>Так это же не спасает от тех же лишних индерекций которые есть в List<BusinessData> dot>Двигаешь pos и получаешь элемент.
То есть предлагаешь не использовать List? Или писать свой?
dot>Притом чтобы это всё имело смысл — pos должен двигаться строго вперёд, до следующего элемента, это и будет оптимизация sequencial access, а какие всякие другие алгоритмы ты тут хочешь воротить?
Убрать лишние индерекции из random access — например элементарный бинарный поиск.
Та же сортировка или например min/max heap на основе массива, а-ля std::pop_heap.
Здравствуйте, ·, Вы писали:
EP>>>>Об этом и речь — что по регионам, а не отдельным затронутым объектам. Поэтому я и сказал "малом количестве", а не в одном. EP>>>>И то — это отслеживание имеет свою цену, которая размазывается по всему коду. dot>>>Я уже упоминал, что gc плохо работает в случае большого числа короткоживущих и толпы долгоживущих. При наличии среднеживущих — производительность падает. EP>>А как ты называешь объекты которые живут долго, но не до конца работы приложения? dot>Old Gen. Данные этих объектов можно менять свободно, но не трогать сильно указатели.
Так они долгоживущие по твоей терминологии или нет?
dot>>>Это можно лечить настройкой GC, испоьзованием разных приёмов (flyweight или пулов например) EP>>Причём тут flyweight? Объекты-то изменяются. dot>Смотря как изменяются.
flyweight-ы обычно неизменяемые.
EP>>>>При логарифмической сложности между N=1000 и N=1000000 — разница примерно в два раза. А в случае же линейной сложности — примерно в тысячу раз EP>>>>Feel the paindifference. dot>>>Бывает. Лечится. Алгоритмы — сила. EP>>Конечно сила, о чём я тебе и говорю. EP>>И при логарифмической сложности, и при линейной, чем больше N тем медленнее — но разница между ними колоссальная. И в результате ситуации совершенно разные, а ты говоришь "тоже не поздоровится" dot>Так вот строгость ссылок явы позволяет применять гораздо более хитрые алгоритмы.
Например?
EP>>>>А зачем обмениваться практически мёртвыми объектами? dot>>>В смысле? Какие уж есть. EP>>Контекст потерял? Ещё раз. EP>>У нас есть объект, счётчик ссылок которого ушёл в ноль. Так? Зачем обмениваться им с другими рабочими потоками? dot>Так ты не знаешь в каком именно потоке и когда он уйдёт в ноль и что именно произойдёт в момент ухода в ноль. В этом и суть GC. Да, узнать можно, но это очень трудоёмко. Компьютер с этим справляется в подавляющем большинстве случаев лучше и надёжнее.
Ещё раз, зачем обмениваться практически мёртвыми объектами между рабочими потоками?
EP>>>>Критический поток поставит его в очередь, а сам удалять не будет. dot>>>И возникнет та же проблема, которой ты меня мучил — а что если он будет ставить в очередь быстрее, чем эта очередь разгребаться? EP>>Проблема вообще-то была в другом. Если по аналогии — в том что грубо говоря pop одного элемента очереди линейно зависит от её размера. Здесь же такой зависимости нет. dot>Не одного элемента, а всей очереди. GC за цикл собирает не один объект.
Чтобы убрать малое колличество мусорных объектов, GC приходится обходить большое количество живых. И скорость сбора мусорных объектов зависит от количества живых. В этом и аналогия.
dot>>>>>>>Хорошо, не подзадача, а средство достижения заявленных характеристик. EP>>>>>>Короче, не нужно сами эти средства называть GC. Другой пример — копирующий аллокатор может быть без GC. dot>>>>>Я говорю это к тому, что эти средства уже доступны при использовании GC. EP>>>>Аналогия: "когда нарезаешь массив на структуру, переизобретаешь C++" dot>>>Не С++. Почему именно С++, а не С, не Алгол, не Фортран, не Паскаль, не ассемблер? EP>>Аналогично — а почему тогда GC? dot>Потому что управление памятью — трудоёмкий процесс в прикладных приложениях. Автоматизировать его — дело святое. EP>>Какой объем реализации? На C++ — меньше ста строк. dot>Ну вот... сейчас ещё начнём строки считать... увольте.
Достаточно грубой прикидки. Если объём отличается на порядки — то с тем же успехом можно и компилятор модифицировать.
EP>>Да и не сравнится ведь: EP>>
EP>>@m int meters = 5 * UnitsTools.m;
EP>>
EP>>vs EP>>
EP>>auto length = 5m;
EP>>
EP>>Да и нет интеграции в систему типов — например перегрузка не работает dot>Перегрузка чего? Это же примитивы.
На C++ это отдельные типы. И по разным типам можно сделать разные перегрузки.
dot>И вообще, чего опять о языке, мы же о GC вроде? Ну возьми Скалу, там есть перегрузка.
Ты же сказал что "и прочие С++ фишки можно реализовать в java" — мы в этой ветке сейчас и находимся.