-2 / 8 / 2
Регистрация: 10.11.2008
Сообщений: 776
1

Размер указателя

25.04.2016, 18:36. Показов 14739. Ответов 75
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
sizeof(void*);// Размер указателя на любой тип равен 8, почему так много? И можно ли как-нибудь узнать сколько под указатель выделено памяти не смотря в исходники?
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
25.04.2016, 18:36
Ответы с готовыми решениями:

Размер строки указателя
char *str = new char ; str = "123"; sizeof(str) - отображает размер указателя, а как вывести...

Размер указателя short int
Доброе утро. Возник вопрос почему short int = 8 байт? Он же должен занимать 2 байта в 32-х...

Размер указателя на разные типы данных
еще один вопрос к етой теме почуму придавая указателю * prt тип short int или double функция sizeof...

Как верно узнать размер указателя ?
СBaseTest* pTest = { new СBaseTest, new СOther }; Нужно узнать размер pTest

75
Вездепух
Эксперт CЭксперт С++
11688 / 6367 / 1723
Регистрация: 18.10.2014
Сообщений: 16,050
27.04.2016, 20:10 41
Author24 — интернет-сервис помощи студентам
Цитата Сообщение от Renji Посмотреть сообщение
Корректировать this надо только при множественном наследовании в потомке.
Хоть в контексте темы это и не принципиально, но это не совсем точное утверждение. Большинство реализаций буду корректировать this и вот в таком случае, хотя никакого множественного наследования тут нет

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
 
struct P {
  int i;
  void print_this() { std::cout << this << std::endl; }
};
 
struct C : P {
  virtual void foo() {}
  void print_this() { std::cout << this << std::endl; }
};
 
int main()
{
  C c;
  c.C::print_this();
  c.P::print_this();
}
Добавлено через 5 минут
Цитата Сообщение от Renji Посмотреть сообщение
Вызов по 8-байтовому указателю:
1) Прочитать адрес метода.
2) Вызвать метод. "Вычесть ptrdiff_t из указателя на объект" делает код метода, в который ptrdiff_t Вхардкорден один раз.
Не замечаете что в моем варианте число действий меньше, а код компакней и быстрее?
Тут вы снова хитрите. Вызов по вашему 8-байтовому указателю - это не вызов метода. Это вызов вызывалки. А внутри вызывалки будет по-прежнему тот же самый ptrdiff_t, только константный, а не времени выполнения и затем уже вызов собственно метода.

Код возможно-спорно компактней, но уж точно не быстрее. Ваш код медленее.

----------------------------------

Тут еще мешает от факт, что язык С++ требует четко определенной сравниваемости на равенство-неравенство указателей на невиртуальные методы. Это означает, в частности, что сэкономить на генерации ваших "вызывалок" не получится: для каждого целевого невиртуального метода придется генерировать отдельную вызывалку, даже если "дельта" коррекции this у них одна и та же. Просто для того, чтобы ваши указатели на разные методы получились разными.

Тут надо еще заметить, что компиляторы давно уже используют ваш метод с промежуточной вызывалкой в ситуациях, когда указатель инициализируется адресом виртуального метода. Указатель в такой ситуации ставится именно на промежуточную вызывалку. При этом они старательно экономят на генерации вызывалок - используется одна и та же промежуточная вызывалка для методов разных классов, занимающих одну и ту же позицию в VMT (и, возможно, одну и ту же "дельту" для this). Т.е. на низком уровне вызывалки с одинаковым результирующим кодом смело сливаются в одну. Таким образом обвинять авторов компиляторов в "лени" следовать этому подходу не приходится: они ему уже следуют в случае с виртуальными методами.

Однако в случае с невиртуальными методами этот подход не используется. Возможно, именно из-за невозможности "сэкономить" не генерации отдельных вызывалок.
0
2782 / 1935 / 570
Регистрация: 05.06.2014
Сообщений: 5,600
27.04.2016, 20:43 42
Цитата Сообщение от avgoor Посмотреть сообщение
Будут ли ваши обертки инлайниться?
Если обертка для метода одна и метод нигде больше не вызывается напрямую, он сам заинлайнится в обертку. Если же не заинлайнится, в обертке будет не вызов метода, а jmp (goto) на него. Ну, при условии что оптимизация кода приличная. И я очень сомневаюсь что этот jmp создаст какие-то тормоза на современном процессоре с внеочередным исполнением команд. Ну разве что на начальной закачке кода в кэш.
Цитата Сообщение от avgoor Посмотреть сообщение
Если да, то какая разница между ними и ptrdiff (учитывая, что и то и то в кэше)? Если нет то как два вызова улучшат код?
Как я понимаю, ассемблер вы не изучали?
1) Процессор работает с машинными словами. void* можно записать куда ни будь одной командой, потому что void* влезает в машинное слово. А вот std::pair<void*,size_t> в машинное слово не влезает и одной командой его записать нельзя. Значит, нужно две команды и код раздувается.
2) Чтобы передать указатель в callFoo, его надо записать в специально отведенное место. Стек или регистры процессора - не важно. Чем толще указатель, тем дольше будете записывать. Прямое следствие пункта 1.
3) Чтобы вызвать метод, вам нужно вычесть ptrdiff из this. На уровне ассемблера это делается одной командой. А вот под капотом процессора это все равно превратится в батарею "вычислить адрес, прочитать, вычесть". Это опять же тормознутей чем "вычесть вшитую в код константу". Хотя, может вам повезет и ptrdiff будет изначально в регистре.
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
Тут вы снова хитрите. Вызов по вашему 8-байтовому указателю - это не вызов метода. Это вызов вызывалки.
Так понятней?
Assembler
1
2
3
4
5
//Точка входа в "вызывалку"
sub ecx,1234;
//точка входа в метод
bla-bla-bla
ret;
0
1550 / 875 / 179
Регистрация: 05.12.2015
Сообщений: 2,555
27.04.2016, 20:56 43
Цитата Сообщение от Renji Посмотреть сообщение
Как я понимаю, ассемблер вы не изучали?
А вы похоже изучали ассемблер 8086.
Если мы прочитали указатель с ptrdiff - он гарантированно в кэше. А ваша вызывалка? Сперва дергаем в кэш вызывалку, потом дергаем в кэш сам метод. Разницы не видите?

Добавлено через 4 минуты
Цитата Сообщение от Renji Посмотреть сообщение
Так понятней?
Так не получится.
0
Вездепух
Эксперт CЭксперт С++
11688 / 6367 / 1723
Регистрация: 18.10.2014
Сообщений: 16,050
27.04.2016, 20:58 44
Цитата Сообщение от Renji Посмотреть сообщение
Так понятней?
Assembler
1
2
3
4
5
//Точка входа в "вызывалку"
sub ecx,1234;
//точка входа в метод
bla-bla-bla
ret;
Ну, так красиво, разумеется, не получится. Самое лучшее, что может получиться это

Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  ; Точка входа в "вызывалку"
  sub ecx, 1234
  jmp точка входа в метод
 
  ; There be dragons...
 
  ; Точка входа в другую "вызывалку"
  sub ecx, 5678
  jmp точка входа в метод
 
  ; There be dragons...
 
точка входа в метод:
  bla-bla-bla
  ret
При этом, так как код вызывалки и код метода будут в общем случае генерироваться в изолированных, ничего друг о друге не знающих контекстах, вызывалка и код метода будут разнесены по коду весьма далеко друг от друга.
0
2782 / 1935 / 570
Регистрация: 05.06.2014
Сообщений: 5,600
27.04.2016, 21:07 45
Цитата Сообщение от avgoor Посмотреть сообщение
Если мы прочитали указатель с ptrdiff - он гарантированно в кэше. А ваша вызывалка? Сперва дергаем в кэш вызывалку, потом дергаем в кэш сам метод. Разницы не видите?
Если мы при каждом вызове "дергаем в кэш", то процессор превращается в 8086 независимо от использования ptrdiff. Потому как исполнение кода в первый раз очень дорогое. В норме код должен один раз загружаться и затем много-много раз исполняться.
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
Ну, так красиво, разумеется, не получится.
Получится, если вызывалка у каждого метода одна. И если мы собираем виртуальные методы, то вызывалка таки одна, так как запись в таблице виртуальных функций тоже одна. На разносолы места нету.
0
1550 / 875 / 179
Регистрация: 05.12.2015
Сообщений: 2,555
27.04.2016, 21:14 46
Цитата Сообщение от Renji Посмотреть сообщение
независимо от использования ptrdiff.
Один кэш мисматч от двух не отличаете? Тогда и виртуальные функции от обычных по быстродействию не отличаются, че уж там.
0
2782 / 1935 / 570
Регистрация: 05.06.2014
Сообщений: 5,600
27.04.2016, 21:30 47
Цитата Сообщение от avgoor Посмотреть сообщение
Один кэш мисматч от двух не отличаете?
Едем дальше. По каким таким мистическим причинам sub rcx,[rdi+8] перед каждым call кэш-мисматчи не плодит, а один разнесчастный sub rcx,1234 в начале тела метода - плодит? У вас в месте появления sub rcx,[rdi+8] кэш обретает свойства резины?
И да, один кэш-мисматч на тысячу вызовов, от двух кэш-мисматчей на тысячу вызовов я без лупы не отличаю.
Цитата Сообщение от avgoor Посмотреть сообщение
Тогда и виртуальные функции от обычных по быстродействию не отличаются, че уж там.
А там обрушивается механизм предсказания переходов. Это отдельная песня.
0
1550 / 875 / 179
Регистрация: 05.12.2015
Сообщений: 2,555
28.04.2016, 10:19 48
Цитата Сообщение от Renji Посмотреть сообщение
перед каждым call кэш-мисматчи не плодит
Потому, что и ptr ptrdiff находятся рядом и как правило в стеке.
Цитата Сообщение от Renji Посмотреть сообщение
разнесчастный sub rcx,1234 в начале тела метода - плодит?
Потому, что в общем случае необходимость в sub rcx, 1234 возникает в другой единице трансляции.
0
2782 / 1935 / 570
Регистрация: 05.06.2014
Сообщений: 5,600
28.04.2016, 11:56 49
Цитата Сообщение от avgoor Посмотреть сообщение
Потому, что и ptr ptrdiff находятся рядом и как правило в стеке.
У вас, похоже, каша в голове. "Дергают вызывалку" в кэш кода, ptrdiff находится в кэше данных. Одно с другим не связано никак. Чтобы у вас возникали постоянные кэш-мисматчи кода, нужно чтобы этот код постоянно из кэша вываливался. А это произойдет тогда, когда код слишком разжиреет. Что и обеспечивается тучей sub rcx,[rdi+8].
Цитата Сообщение от avgoor Посмотреть сообщение
Потому, что в общем случае необходимость в sub rcx, 1234 возникает в другой единице трансляции.
Кэш наполняется кэш-рядами, а не единицами трансляции.
0
1550 / 875 / 179
Регистрация: 05.12.2015
Сообщений: 2,555
28.04.2016, 12:22 50
Цитата Сообщение от Renji Посмотреть сообщение
Кэш наполняется кэш-рядами, а не единицами трансляции.
Имелось ввиду, что ваша обертка, в общем случае, не будет рядом с методом.

Добавлено через 10 минут
Renji, Чтобы скорректировать указатель, в любом случае надо прочитать величину коррекции. Только в вашем случае читается адрес по которому лежит вызывалка (часть call вызывалка), а потом читается смещение (внутри вызывалки), а потом читается адрес метода (как jmp метод).
В моем случае читатется смещение и читается адрес метода (причем все лежит рядом и в кэше).
0
2782 / 1935 / 570
Регистрация: 05.06.2014
Сообщений: 5,600
28.04.2016, 13:10 51
Цитата Сообщение от avgoor Посмотреть сообщение
Имелось ввиду, что ваша обертка, в общем случае, не будет рядом с методом.
Это да, в худшем случае обертка скушает целый кэш-ряд. Но ваши sub rcx,[rdi+8] в худшем случае скушают весь кэш и еще попросят. Их количество ведь ничем не ограничено.
Цитата Сообщение от avgoor Посмотреть сообщение
Renji, Чтобы скорректировать указатель, в любом случае надо прочитать величину коррекции.
Вот только у меня надо прочитать только на стадии декодирования команды, а потом все осядет в кэше трасс. У вас же надо читать при каждом исполнении команды. Причем, прежде чем прочитать величину коррекции, вам надо узнать адрес этой величины. И с виду простое "ну, адрес указателя+8 же" все равно не проходит бесплатно.
0
1550 / 875 / 179
Регистрация: 05.12.2015
Сообщений: 2,555
28.04.2016, 16:01 52
Цитата Сообщение от Renji Посмотреть сообщение
Их количество ведь ничем не ограничено.
Но, как правило, указателей на член очень мало (Если они используются как делегаты дополнительные 8 байт в классе роли не сыграют).
Цитата Сообщение от Renji Посмотреть сообщение
У вас же надо читать при каждом исполнении команды.
sub rcx, 8 и sub rcx, [rbp+off] при прочих равных (все в кэше) выполнятся практически за одно и то же время (На этом даже был хак - быстрое умножение на адресной арифметике). Но! Если что-то из данных не в кэше...
0
2782 / 1935 / 570
Регистрация: 05.06.2014
Сообщений: 5,600
28.04.2016, 16:35 53
Цитата Сообщение от avgoor Посмотреть сообщение
Но, как правило, указателей на член очень мало
Я не про число указателей, а про число обращений к нему. Вы, похоже, не понимаете что исполняемый код жрет кэш одним фактом своего исполнения. Даже если он из памяти вообще ничего не читает.
Цитата Сообщение от avgoor Посмотреть сообщение
Но! Если что-то из данных не в кэше...
sub rcx, 8, в отличие от, не использует кэш данных. Если же данных нет в кэше кода, то тормозить начнет любой код, будь то sub rcx, [rbp+off], sub rcx, 8 или вообще nop.
0
1550 / 875 / 179
Регистрация: 05.12.2015
Сообщений: 2,555
29.04.2016, 10:41 54
Цитата Сообщение от Renji Посмотреть сообщение
sub rcx, 8, в отличие от, не использует кэш данных
Какая разница, какой кэш он жрет. Важно, что сама ваша вызывалка будет находиться далеко и от вызова метода, и от самого метода, и выжрет целую страницу кэша для всего лишь двух комманд.

Добавлено через 4 минуты
Предупреждая отсылки к предсказанию ветвей и пр. - они принципиально ничего не меняют.
0
2782 / 1935 / 570
Регистрация: 05.06.2014
Сообщений: 5,600
29.04.2016, 12:39 55
Цитата Сообщение от avgoor Посмотреть сообщение
Какая разница, какой кэш он жрет.
Такая разница, что код занимает 16 килобайт, а его данные - 16 мегабайт. Угадайте в каком кэше будут постоянные кэш-мисматчи, а какой будет стоять полупустым.
Цитата Сообщение от avgoor Посмотреть сообщение
Важно, что сама ваша вызывалка будет находиться далеко и от вызова метода, и от самого метода, и выжрет целую страницу кэша для всего лишь двух комманд.
Во-первых, не страницу (четыре кило), а ряд (32/64 байта). Во-вторых, пачка sub rcx, [rbp+off] сожрет ровно тот же ряд. Но пачек, в который раз повторяю, может быть 100500, а вызывалка одна.
0
Kastaneda
29.04.2016, 14:46
  #56

Не по теме:

Цитата Сообщение от Renji Посмотреть сообщение
Используя сегментную модель памяти и PAE-костыль,
PAE как раз предназначена для страничной организации памяти, а не сегментной.

0
2782 / 1935 / 570
Регистрация: 05.06.2014
Сообщений: 5,600
29.04.2016, 15:02 57
Цитата Сообщение от Kastaneda Посмотреть сообщение

Не по теме:

PAE как раз предназначена для страничной организации памяти, а не сегментной.

Не по теме:

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

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

0
1550 / 875 / 179
Регистрация: 05.12.2015
Сообщений: 2,555
29.04.2016, 15:20 58
Цитата Сообщение от Renji Посмотреть сообщение
а ряд (32/64 байта)
И для L1, и для L2, L3?

Добавлено через 7 минут

Не по теме:

Цитата Сообщение от Renji Посмотреть сообщение
ей все равно придется отказаться от плоской модели (использования одного сегмента)
Как в ДОСе (com и exe)? Че-то не хочется.:)



Добавлено через 4 минуты
Цитата Сообщение от Renji Посмотреть сообщение
32/64 байта
К тому же, если повезет, ptr и ptrdiff будут в одной кэш линии (а чтоб гарантированно повезло - химичим с выравниванием).
0
2782 / 1935 / 570
Регистрация: 05.06.2014
Сообщений: 5,600
29.04.2016, 15:35 59
Цитата Сообщение от avgoor Посмотреть сообщение
И для L1, и для L2, L3?
Для L2 на четвертом пне было 128 байт. Тоже, до страницы далеко.

Добавлено через 2 минуты
Цитата Сообщение от avgoor Посмотреть сообщение
К тому же, если повезет, ptr и ptrdiff будут в одной кэш линии
Если повезет, нагрузку на процессор будет создавать 16 килобайтовый код, повторяющийся миллион раз. Что возвращает нас к исходному - если L1 кэш - 32 килобайта, а код в нем - 16 килобайт, то не все ли равно добавится к этому коду еще один кэш-ряд или нет?
0
1550 / 875 / 179
Регистрация: 05.12.2015
Сообщений: 2,555
29.04.2016, 16:09 60
Цитата Сообщение от Renji Посмотреть сообщение
то не все ли равно добавится к этому коду еще один кэш-ряд или нет?
По сути - все равно.
Но, зачем городить огород (особенно новый keyword) если это не дает никакого профита?
0
29.04.2016, 16:09
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
29.04.2016, 16:09
Помогаю со студенческими работами здесь

Объяснить от чего зависит размер указателя и что такое арифметика указателей
Уважаемые программисты, возник вопрос. По умолчанию указатель любого типа может иметь только 4...

Почему увеличение указателя на sizeof(тип) не тождественно инкременту этого же указателя?
Всем доброго дня.:) Можете обьяснить ,почему при инкриментировании указателя,его значение(адресс)...

Создание указателя на экземпляр класса, описанного после объявления указателя
Здравствуйте! Проблема в том, что нужно сделать так: class A{ public: B* b = nullptr; }; ...

Преобразование кода без указателя в код с использованием указателя
Правильно ли выполнил? Исходный код без указателя #include &lt;iostream&gt; #include &lt;cstdlib&gt;...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
60
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru