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)

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

В линуксах есть архитектура x32 где 32-битные указатели и 64-битные данные, но она оказалась невостребованной.

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

firkax ★★★★★
()

near pointers отсчитывались не от структуры данных, а от адреса, на который указывает сегметный регистр.

То есть соотвествовали некоей (спорной) аппаратной фиче.

В других архитектурах такой аппаратной фичи нет. А чисто программно - её можно и умулировать. Завести указатель-переменную base, и прибавлять к ней int32-индексы.

GPFault ★★
()

Скорее всего из-за размера регистров и требований выравнивания.
Под капотом маленькие указатели никуда не делись, если у тебя не large memory model конечно - в коде генерируется вместо полного 64битного указателя 32 или даже 16битный оффсет, чтобы уменьшить размер инструкции, а чтобы сделать такое же в данных - обычно явно использует оффсет (например, записав индекс вместо сырого указателя в структуру)
Абсолютные малые указатели бессмысленны в эпоху 64битного адресного пространства и ASLR. Да, можно было бы сделать какой-нибудь специальный тип, в который компилятор писал бы оффсет, но оффсет от чего это должен быть? Раньше это был оффсет от начала сегмента, но никто не хочет возвращаться к сегментной памяти. Сегменты сейчас разве что для TLS используют.
Осталось правда одно применение, где малые указатели были бы полезны - это прослойки вроде Wow64 в wine. и одна их реализаций даже патчила clang чтобы добавить туда такие типы. Но даже там судя по всему от этого отказались, просто обернув всё в nt-сисколы

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

near pointers отсчитывались не от структуры данных, а от адреса, на который указывает сегметный регистр.

Да, я в курсе.

В других архитектурах такой аппаратной фичи нет. А чисто программно - её можно и умулировать. Завести указатель-переменную base, и прибавлять к ней int32-индексы.

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

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

Под капотом маленькие указатели никуда не делись, если у тебя не large memory model конечно - в коде генерируется вместо полного 64битного указателя 32 или даже 16битный оффсет, чтобы уменьшить размер инструкции, а чтобы сделать такое же в данных - обычно явно использует оффсет (например, записав индекс вместо сырого указателя в структуру)

Что ты несёшь? Если я открою дамп памяти любого процесса, там везде будут полные указатели в динамической памяти. Относительная адресация в коде меня мало заботит.

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

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

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

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

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

Очень хорошо, что оно сдохло!
Это костыль к сегментно-страничной интеловской организации памяти которая появилась в 286 процессоре.

Эти уродские указатели — адский геморрой!
Как сравнивать нормальный и уродский указатели?
Как отслеживать переполнения смещения при арифметических операциях?

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

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

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

Конскими вряд ли, но будет наверно чуть медленнее. И дело не только в тормозах, сравни:

  i = *st->p;
и
  i = *(int32*)(st->base+st->p);
Второе мало того что просто длиннее и засоряет экран, оно ещё и лишает тебя возможности нормально проверять типы на этапе компиляции. А еще у тебя сразу возникают вопросы:

1) а где хранить base, в этой же структуре или в какой-то другой?

2) а какой битности делать короткие указатели и что делать если вдруг её не хватит?

3) что делать если вдруг указатель из одного места надо скопировать в другое где другое base? рефакторить всю прогу чтоб он стал «far»?

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

сегментно-страничной интеловской организации памяти которая появилась в 286 процессоре.

Дважды враньё. Ни сегменты, ни страницы не появились в 286. Сегменты появили раньше, страницы - позже.

firkax ★★★★★
()

Почему этого сейчас нет нигде?

Потому что так проще? Я имею в виду есть 64-битная архитектура с 32 битными указателями, но мало кто хочет с этим возится.

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

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

anonymous
()

Помянем x32abi. Причины: «как это поможет мне в моем дедлайне на javascript», «я [s]художник[/s] кампутер сцаентист а не байтоёб, нинужно», «компьютеры сейчас быстрые(кстати почему мой калькулятор на электроне тормозит?)» и тд.

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

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

Это сишные проблемы. В нормальном языке это убирается под капот и не отсвечивает.

  1. а где хранить base, в этой же структуре или в какой-то другой?

Не так важно.

  1. а какой битности делать короткие указатели и что делать если вдруг её не хватит?

Опять же, не так важно. Это деталь реализации. Меня волнует жизнеспособность подхода.

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

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

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

Это не детали реализации

Это детали реализации.

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

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

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

понятно почему короткие указатели популярность не получили.

Нет, непонятно.

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

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

Память может и дешёвая, а какой-нибудь кеш первого уровня не очень.

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

Память может и дешёвая, а какой-нибудь кеш первого уровня не очень.

Память-то дешёвая, но не бесконечная. У меня были такие случаи, когда в каком-нибудь raidix-tree большая часть памяти памяти была забита указателями, а совсем не данными, и приходилось ручками переписывать в нечто более приличное. И мне кажется, такая проблема встречается на самом деле достаточно часто.

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

В принципе она есть на большинстве архитектур.
Например, слинкованный (с помощью llvm и такой-то матери) 32битный x86 код в 64 бинарь функционирует (при условиии конечно, что бинарь загружен в нижние 32 бита). На arm это должен быть aarch64 код, не aarch32, даже если 32битный т.к aarch64 и aarch32 несовместимы. llvm умеет с таким работать, а вот clang/gcc уже нет. Но это не far/near указатели, это просто малые указатели относительно нуля

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

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

Ещё аллокатор памяти надо дорабатывать для этого, либо структуру выделять сразу большим блоком чтобы влезла не только сама структура, как описано в хедере, но и всё что для неё динамически выделено. Учесть надо будет ещё и то, что иногда данные структуры надо будет менять, соответственно либо заранее выделить запас помяти под возможные добавки расходов места, либо делать realloc на весь выделенный блок ради того чтобы какую-нить строку по указателю изменить с «abc» до «abcd». Разумеется, это всё решаемо, но большинство программистов не хотят заниматься решением данных вопросов, они хотят чтобы всё просто работало (пока не упираются в узкое место по памяти). Я, там где надо экономить память, делал и это и кастомные аллокаторы, но обычно тоже просто использую обычные указатели.

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

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

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

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

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

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

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

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

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

Таких случаев бывает вагон. Я серьёзно думаю, что очень здоровая часть расходов на память в софте сегодня – сраные указатели.

Возьми тот же наивный парсинг JSON ради примера: обычно его парсят в AST с самыми обычными указателями. При этом, размер данных в каждой ноде может быть сильно меньше размеров указателей в ней же. Типа, какое-нибудь { "tag": [1,2,3,4] } будет иметь ~36 байт полезных данных (4 инта по 64 бита (да, я знаю что в JS float, ну да насрать), строка на 3 байта, её размер, мб что ещё по мелочи) и пачку указателей, по одному на каждый элемент массива, на сам массив и т.д. и т.п. В итоге получается полная жопа по части объёмов памяти.

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

Часто ли парсится JSON в современном софте? Мне почему-то кажется, что очень часто.

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

Часто ли парсится JSON в современном софте? Мне почему-то кажется, что очень часто.

В такоем случае прийдется вместо одной функции которая работает только с 64 битными указателями делать несколько: с 8, 16, 24, 32 и т.д. указателями. А если будет смесь? Значит надо действовать через интерфейс, а это значит что вместо прямого вызова функций нужно будет использовать косвенный вызов через промежуточный указатель а это лишнее замедление. И если памяти сейчас дохрена, две планки памяти сейчас дают 96 гигов. То вот производительность однопоточная в лучшем случае на пару процентов каждый год растет.

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

В такоем случае прийдется вместо одной функции которая работает только с 64 битными указателями делать несколько: с 8, 16, 24, 32 и т.д. указателями.

Нет, не придётся.

И если памяти сейчас дохрена, две планки памяти сейчас дают 96 гигов.

Этот аргумент работает только если у тебя отдельный компьютер под каждую программу.

И если памяти сейчас дохрена, две планки памяти сейчас дают 96 гигов.

Покажи мне как ты вставляешь две планки памяти в средний макбук.

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

Ну и? Такого, что ты скомпилируешь json-парсер магическим компилятором и он вдруг станет в 5 раз меньше жрать - не выйдет.

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

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

(оффтоп)

Этот аргумент работает только если у тебя отдельный компьютер под каждую программу.

В идеале так и должно быть, кстати.

Покажи мне как ты вставляешь две планки памяти в средний макбук.

А макбуки не нужны.

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

Ну и? Такого, что ты скомпилируешь json-парсер магическим компилятором и он вдруг станет в 5 раз меньше жрать - не выйдет.

Магии конечно не выйдет, код придётся править. Мой вопрос в том, почему этого вообще никто не делает. Ну то есть, за исключением «succinct data structures», которые я упомянул в верхнем посте и которые встречаются примерно почти нигде, я подвижек таких не видел.

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

ТЫ НЕ ПОВЕРИШЬ! Но так делают почти все. Так вышло, что JS стал фактически стандартом де-факто для хранения и передачи данных в индустрии.

Этот аргумент работает только если у тебя отдельный компьютер под каждую программу.

В идеале так и должно быть, кстати.

Охлол.. сколько у тебя компьютеров дома? Десять? Двадцать? Тридцать?

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

Нет, не придётся.

В смысле нет? Ты заранее не знаешь размер json который надо парсить а значит и размер указателей либо выбираешь самый большой либо через интерфейс. Плюс если парсинг это часть программы то выделенная память может превысить 4 гига, а значит если хочешь маленькие указатели то надо через base+offset что снова приводит к замедлению.

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

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

Потому что веб-макаки (а js разносят именно они) вообще мало что умеют.

ТЫ НЕ ПОВЕРИШЬ! Но так делают почти все.

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

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

(оффтоп)

Охлол.. сколько у тебя компьютеров дома? Десять? Двадцать? Тридцать?

Два на рабочем столе, ещё 6 в стойке. И да, ни на одном из них загрузка и близко не 100%, разделение по железу именно ради структуризации работы.

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

Ты заранее не знаешь размер json который надо парсить

чо? Прочитал JSON, засунул в буфер, парсишь.

Плюс если парсинг это часть программы то выделенная память может превысить 4 гига, а значит если хочешь маленькие указатели то надо через base+offset что снова приводит к замедлению.

Что значит «может»? Она либо превысит, либо не превысит. В любом случае, это не выглядит невыполнимой проблемой и я навскидку могу предложить пару способов как это решить.

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

Два на рабочем столе, ещё 6 в стойке. И да, ни на одном из них загрузка и близко не 100%, разделение по железу именно ради структуризации работы.

То есть, ты используешь всего 8 программ? Одна из них – браузер, какие остальные 7?

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

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

чо? Прочитал JSON, засунул в буфер, парсишь.

И какой размер указателя ты предлашаешь для этого использовать?

могу предложить пару способов как это решить.

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

V1KT0P ★★
()