LINUX.ORG.RU

А почему умерли разные типы указателей?

 ,


0

2

Привет, ЛОР!

Тащемта, вопрос. В x86-16 были near и far pointers, что позволяло экономить на размере указателя в ту глубокую древность. Почему этот концепт не попал в 64-битные архитектуры? Ведь с учётом локальности, делать все указатели 64-битными выходит в конский расход памяти при том, что большая часть бит указателей в рамках одного экземпляра структуры данных (допустим, связанный список или дерево) будут одинаковыми. А значит, можно сэкономить кучу памяти, сохраняя только последние N бит указателя и хранить полный указатель, например, только в заголовке структуры данных.

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

Update:

Вообще, такой подход дохрена где применяется. Гуглить «succinct data structures». Например, вот это: https://web.archive.org/web/20160312010342/https://www.computer.org/csdl/proceedings/focs/1989/1982/00/063533.pdf

Но мой вопрос скорее про то, почему этого нет на уровне языков/компиляторов.

★★★★★

Последнее исправление: hateyoufeel (всего исправлений: 1)
Ответ на: комментарий от V1KT0P

Так предложи который не просадит производительность и удобство разработки.

У нас с тобой странный диалог выходит. Смотри:

Я: "Посоны, а вот есть такая идея. Скажите, почему так никто не делает? Вроде как есть плюсы."
Ты: "HURRR DURRR это сложно, но я не знаю почему, но ты попробуй и расскажешь там".

Не находишь это странным? В том же DOS и софте под него можно же было использовать near и far pointers в одном бинарике. Неужели современные говносистемы сильно хуже?

hateyoufeel ★★★★★
() автор топика

делать все указатели 64-битными выходит в конский расход памяти

2x в пределе, а на практике десятки процентов (потому что не всё указатели, не все указатели можно сократить, а те что можно не обязательно сократятся из-за выравнивания соседних членов) - это не «конский», а жалкие копейки, не окупающие даже страницы лишнего кода в компиляторе. Почитай треды времён когда переезжали на 64бита (а особенно про выродка x32 ABI), там всё это давно посчитали и обмусолили.

Ведь с учётом локальности

при том, что большая часть бит указателей в рамках одного экземпляра структуры данных (допустим, связанный список или дерево) будут одинаковыми

Конечно же не будет, нет никакой локальности при динамической аллокации. Два элемента одного дерева запросто могут быть аллоцированы по адресам различающимся на >> 2^32.

Но мой вопрос скорее про то, почему этого нет на уровне языков/компиляторов.

А что тебе нужно от языка/компилятора? Возьми вектор элементов и храни смещения в нём.

Если думаешь что компилятор мог бы это сам делать, то нет - у него нет ни информации ни о характере твоих аллокаций и максимальном их количестве (которая позволила бы автоматически выделить фиксированный пул для какой-то конкретной аллокации), ни информации о том как ты используешь поля структур, хранишь ли ты там указатели внутри, условно, одного дерева которое предполагается аллоцировать локально, или на произвольные элементы того же типа раскиданные по всему адресному пространству.

anonymous
()
Ответ на: комментарий от hateyoufeel

Окей, вот тебе пример: в качестве основного указателя берётся указатель на саму структуру/заголовок/первый элемент, засовывается в регистр, а все остальные указатели внутри структуры отсчитываются от него.

Ну то есть ты изобрёл offsets ?!? ;-)

Ну то есть ты никогда не смотрел что там Си генерит для доступа к массивам?

«О сколько нам открытий чудных…» (С) Наше всиО - АСП

Для многих случаев это снизит расход памяти весьма существенно.

Ни на бит не сократит! ;-)

Железо поймёт только full-сайз указатель, что бы ты там не намудрил, процу всё равно ПРИДЁТСЯ скормить полноценный, полноразмерный указатель. Как бы ты его не конструировал… Ну и оправдается это только в экстремальных случаях, уже когда заниматься лютым байтодрочем - нормально…

Этим занимаются высокие профессионалы в HP computing и зелёные суперхацкеры ;-)

Это жЫрный намЁк :)))))

anonymous
()
Ответ на: комментарий от hateyoufeel

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

cobold ★★★★★
()
Ответ на: комментарий от hateyoufeel

Не находишь это странным? В том же DOS и софте под него можно же было использовать near и far pointers в одном бинарике. Неужели современные говносистемы сильно хуже?

В том то и дело что near и far указатели были не от хорошей жизни. Сейчас в них просто нет необходимости и история с x32 ABI показала что никакого значительного увеличения производительности не происходит при уменьшении указателя с 64-бит до 32-бит. Это проверили на практике а не в теории. И даже какое-то уменьшение потребление памяти не позволило стать хоть сколько-либо популярным чтоб его не выкинули отовсюду.

V1KT0P ★★
()
Ответ на: комментарий от V1KT0P

Сейчас в них просто нет необходимости и история с x32 ABI показала что никакого значительного увеличения производительности не происходит при уменьшении указателя с 64-бит до 32-бит.

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

hateyoufeel ★★★★★
() автор топика
Ответ на: комментарий от hateyoufeel

Твоя прога использовала либо 32 бита, либо 64.

Вот именно были доступны все регистры и преимущества x86_64. Но разница в скорости выполнения при 64 битных указателях и при 32 битных не показала значительного преимущества. Еще раз, замерялись в программах которые потребляли не более 4 гигов, и там сокращение не дало хороших результатов. То-есть смысла в уменьшении 64-bit указателя до 32-bit не имеет смысла. Ни увеличения производительности ни значительного снижение потребления памяти нет.

V1KT0P ★★
()
Ответ на: комментарий от V1KT0P

Вот именно были доступны все регистры и преимущества x86_64. Но разница в скорости выполнения при 64 битных указателях и при 32 битных не показала значительного преимущества. Еще раз, замерялись в программах которые потребляли не более 4 гигов, и там сокращение не дало хороших результатов. То-есть смысла в уменьшении 64-bit указателя до 32-bit не имеет смысла. Ни увеличения производительности ни значительного снижение потребления памяти нет.

Я нашёл про увеличение производительности на 5-10% в среднем. Что достаточно неплохо. Проблема x32 abi была в недоступности более 4 гигабайт памяти процессу, здесь же такой проблемы нет.

Но в любом случае, история с x32 abi тут вообще не причём. Я пишу скорее о разделении памяти на арены и адресацию по смещению внутри арены в рамках одного экземпляра структуры данных.

hateyoufeel ★★★★★
() автор топика
Последнее исправление: hateyoufeel (всего исправлений: 1)
Ответ на: комментарий от anonymous

процу всё равно ПРИДЁТСЯ скормить полноценный, полноразмерный указатель

А теперь представь следующую ситуацию: у тебя есть таблица в памяти, где этих указателей – до жопы. Просто дохренища. И тебе надо хранить там много-много указателей. А они жирные, иногда жирнее данных, которые хранят. И тебе хочется сэкономить и делать вместо прямых жирных указателей, которые нужны в общем-то только в момент разименования, на относительные. Например, так работает poptrie.

gaylord
()
Ответ на: комментарий от hateyoufeel

Я как-то раз делал бенч с немного вырожденным JSON: тупо объект на 10 миллионов полей, имя и значение каждого поля – строка на 5 символов. Например, { «aaaaa»: «11111», «aaaab»: «11112», … }. Получается файлик мегабайт на 15. При этом, даже jsoncpp отожрал порядка 150 метров, чтобы его распарсить. Всякие жабы и хацкелли отжирали по полгига. Вот настолько вот всё херово, чувак.

Если у тебя в реальности будет JSON с такими характеристиками, то это означает, что пространство ключей будет 26^5 = 11881376 элементов, то есть проще всего будет аллоцировать столько объектов char[8] и тупо вычислять индекс по ключу. В памяти при этом они займут 26^5 * 8 = 95051008 байт = 90,6 МБ. То есть экономия немногим меньше половины. Если же аллоцировать char[5], то использованная память составит 56,7 МБ — в три раза меньше.

Для парсинга такого специализированного файла тупо пишется свой SAX-подобный парсер, который на ходу раскладывает объекты по нужным индексам.

Всё это к чему: получается, что jsoncpp как универсальный парсер, который не вникает в схему данных, потратил не так уж много памяти на дополнительные расходы. Ведь даже просто хранение size_t для всех строковых ключей и значений должно было бы занять порядка 180 МБ.

static_lab ★★★★★
()
Ответ на: комментарий от static_lab

Всё это к чему: получается, что jsoncpp как универсальный парсер, который не вникает в схему данных, потратил не так уж много памяти на дополнительные расходы. Ведь даже просто хранение size_t для всех строковых ключей и значений должно было бы занять порядка 180 МБ.

Ты сейчас серьёзно пишешь, что потратить >10x памяти от размера данных на парсинг этих данных – это немного?

hateyoufeel ★★★★★
() автор топика

Но мой вопрос скорее про то, почему этого нет на уровне языков/компиляторов.

В Java есть. Гуглить «Java Compressed References». Из Parallel и Serial GC это, насколько мне известно, не выкинули. Насчёт G1 и ZGC не скажу, но, если не ошибаюсь, даже последние версии ShenandoahGC эту фичу поддерживают.

QsUPt7S ★★
()

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

… и очень сильно надеяться, что весь этот мусор есть в кэше процессора. иначе оптимизатор добавит дикое пенальти к производительности

router ★★★★★
()
Ответ на: комментарий от V1KT0P

не происходит при уменьшении указателя с 64-бит до 32-бит

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

vbcnthfkmnth123 ★★★★★
()
Ответ на: комментарий от hateyoufeel

Я пишу скорее о разделении памяти на арены и адресацию по смещению внутри арены в рамках одного экземпляра структуры данных.

Разделяй и адресуйся руками, в чём проблема-то?

anonymous
()

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

тогда чтобы увеличить разрядность адреса относительно разрядности данных и взяли схему - адрес = база << N + смещение, таким образом увеличив разрядность адреса на N.

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

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

alysnix ★★★
()
Ответ на: комментарий от vbcnthfkmnth123

то в теории все можно запихать в процессорный кеш, что увеличит скорость, насколько я понял.

Это если узким местом является кеш. Как пример когда вместо Array of structures (AoS) используют Structure of arrays (SoA) что позволяет увеличить производительность. Но опыт целых дистрибутивов на x32 ABI показывает что в общем случае не особо работает. А узкие места и без этого оптимизируют.

V1KT0P ★★
()
Ответ на: комментарий от vbcnthfkmnth123

Современные процессоры под 90 мегабайт кеша имеют. То есть запихать при желании можно.

Что при желании запихать можно? Я вот сейчас смотрю на свой датасет, там порядка 250GiB. Заталкивай :DDD

gaylord
()
Ответ на: комментарий от gaylord

Что при желании запихать можно?

Программы. Данные программ, чтобы они там их хранили и к них обращались.

датасет

Это-то тут причем?

vbcnthfkmnth123 ★★★★★
()
Последнее исправление: vbcnthfkmnth123 (всего исправлений: 1)
Ответ на: комментарий от hateyoufeel

Я нашёл про увеличение производительности на 5-10% в среднем. Что достаточно неплохо.

Этой информации 10-15 лет, небось. В то время у процессоров поддержка 64-битного режима была странной, и были разные неочевидные просадки производительности на ровном месте.

Сделай тесты заново. Так и узнаем, есть ли сейчас на современном железе хоть какой-то толк.

i-rinat ★★★★★
()
Ответ на: комментарий от firkax

Пиши тогда новую java vm с автоматическим использованием локальных указателей

Огонь решение! Гораздо лучше, чем просто написать для уже готовой:

java -XX:-UseCompressedOops ...

DarkAmateur ★★★★
()
Последнее исправление: DarkAmateur (всего исправлений: 1)
Ответ на: комментарий от firkax

Если кратко, то потому что память очень дешёвая, а нативной поддержки коротких указателей в процах не заявлено.

А зачем нативная поддержка (она же уже есть)? Обращение по указателю происходит как и раньше inc [rax], загрузка такого указателя происходит тоже как раньше, mov eax, [varptr]

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 1)
Ответ на: комментарий от i-rinat

Если идет работа только с указателями, то в кеш помещается в два раза больше, я думаю это тоже влияет. На код это тоже влияет, 32 битный код занимает меньше места.

В Intel Manual все еще есть строки что 32 битные регистры предпочтительнее.

MOPKOBKA ★★★★★
()
Ответ на: комментарий от MOPKOBKA

Ты походу не понял о чём речь. То что ты пишешь это x32, оно существует но почему-то никто его себе не устанавливает.

Он хочет адресовать всё 64-битное пространство 32-битными указателями с 64-битными базовыми адресами.

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 1)
Ответ на: комментарий от firkax

Он хочет адресовать всё 64-битное пространство 32-битными указателями с 64-битными базовыми адресами.

Ну пусть адресует, база к примеру в rbx, тогда *varptr = 132 это

mov eax, [varptr]
mov [rbx+rax], 123 
В чем будет сложность?

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 1)

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

MOPKOBKA ★★★★★
()
Ответ на: комментарий от i-rinat

Ну напиши код работающий много с числами 32/64 или указателями и посмотри, будут тебе те самые 15%. Ты в чем сомневаешься, в том что 64 битные числа занимают память, или в том что команды для работы с 64 битами занимают больше места? Или думаешь в Intel Manual решили приврать?

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 1)
Ответ на: комментарий от firkax

Это да, но что бы изменилось с процессорной поддержкой? Регистр может и выделили бы, но загружать то его все равно придется, и это скорее проблема языка, в С пришлось бы создавать странные синтаксические конструкции, а в Java, JavaScript сжатые указатели и так уже используются.

MOPKOBKA ★★★★★
()
Последнее исправление: MOPKOBKA (всего исправлений: 1)
Ответ на: комментарий от MOPKOBKA

Речь была не про это, а про то что far/near указатели, на которые ссылается автор, были с нативной поддержкой в которой всё было просто, потому что все near-указатели ссылались на один и тот же сегмент данных. Сделать аналогичную для произвольных структур я даже не знаю как, и вот её и нету.

firkax ★★★★★
()
Ответ на: комментарий от firkax

Он хочет адресовать всё 64-битное пространство 32-битными указателями с 64-битными базовыми адресами.

Ну дык - сегментная модель как она есть! Это понятно как раз :)

И даже понятно почему он за неё топит - увидел небось как реальные профи из HPC юзают для себя и оно у них работает …

Только вот те-то дядечки точно знают что делают, понимают как оно на их юзкейс ложится и не применяют это как универсальное решение.

А вот ТС - наверное «зелёный» и не понимает, думает что нарыл «серебряную пулю» :)

Я вот помню как программеры во всем мире радовались когда перешли на x64 flat memory model :) Было две недели сплошной пьянки, фейверков, поэтэсс с низкой социальной :) и другого отрыва … говорят даже что программеры десантуру в фонтанах наперегонки плавать заставили :) На радостях то! :))))

anonymous
()

Вообще-то в получающемся после компиляции бинарнике сплошняком испольуются короткие оффсеты вместо полных адресов. Начиная со структур и массивов и заканчивая стеком. И не только для x86, кстати.

В коде, использование индекса вместо полного адреса тоже не редкость. Или там адреса конкретных регистров периферии обычно указываются как PERIPHERAL_BASE_ADDR + offset. В общем, где удобно и оправдано - там и «коротким» адресам найдётся место.

Stanson ★★★★★
()
Ответ на: комментарий от hateyoufeel

У тебя в файле каждая пара ключ-значение без пробелов занимает 16 байт:

"aaaaa":"11111",

Если у тебя в объекте 10 млн полей, то есть 10 млн таких пар, то они должны занять 16 * 10 = 160 млн байт, что никак не 15 МБ.

static_lab ★★★★★
()
Ответ на: комментарий от Stanson

Вообще-то в получающемся после компиляции бинарнике сплошняком испольуются короткие оффсеты вместо полных адресов

На intel64 нельзя даже сделать call/ret по абсолютному адресу, только в APX добавят. Но он про структуры говорит, их компилятор не меняет если они располагаются в памяти, понятно почему, но интересно было бы посмотреть на сокращение указателей в структурах и перестановку полей для оптимизации.

MOPKOBKA ★★★★★
()
Ответ на: комментарий от MOPKOBKA

но интересно было бы посмотреть на сокращение указателей в структурах

x32 ABI, его конечно выкинули вроде из всех компиляторов из-за ненужности но можешь взять последнюю версию где еще поддерживалось и сравнить на нужном примере x64 vs x32 vs x86.

V1KT0P ★★
()