Сообщение Re[8]: А С++ то схлопывается... от 09.11.2019 11:04
Изменено 09.11.2019 11:06 vdimas
Re[8]: А С++ то схлопывается...
Здравствуйте, Denis Ivlev, Вы писали:
DI>А ты вот, например, понимаешь как работают виртуальные вызовы? Я расскажу:
Думаю, понимает.
DI>1. В каждом объекте появляется указатель на таблицу виртуальных функций, выросший размер объекта приводит, например, к тому, что твой объект перестает помещаться в кеш линию, а это значит, что в память надо ходить чаще
И?
В Си-ядре Linux именно это и происходит:
https://github.com/torvalds/linux/blob/master/include/linux/fs.h
Смотрим строки:
https://github.com/torvalds/linux/blob/master/include/linux/fs.h#L362
Это классическая таблица виртуальных ф-ий.
Еще:
Вот виртуальный объект, ссылающийся на экземпляр таблицы виртуальных ф-ий:
https://github.com/torvalds/linux/blob/master/include/linux/fs.h#L935
А вот множественное наследование от виртуальных типов:
DI>2. С девиртуализацией компилятор может не справится, поэтому:
По моим наблюдениям, при включённом LTO компилятор слишком часто применяет девиртуализацию.
Т.е. намного чаще, чем можно было представить при взгляде со стороны.
У меня часто исчезают как сами виртуальные методы, так и экземпляры таблиц виртуальных методов при включенной опции /OPT:REF.
А вот если делать эмуляцию виртуальных методов как по ссылке на исходники ядра Linux, то там компилятор честным образом будет скакать через два уровня косвенности каждый божий раз при вызове, потому что ему недостаточно информации, чтобы делать предположения о семантике происходящего.
DI>3. Чтение таблицы ВФ по этому указателю тоже может означать поход по памяти и плюс к этому вытеснение из кеша данных, которые могут скоро понадобится
Таблица виртуальных ф-ий одна на разные экземпляры.
Для частоиспользуемых типов эта таблица живёт в "разогретой" области кеша.
В любом случае, при ручной эмуляции будет аналогично, т.е. в сравнительном плане ты ничего полезного для целей спора из этого не вытащишь.
DI>Вот теперь, когда ты знаешь про стоимость этой абстракции, скажи будут в ядре ОС использовать ВФ?
Ес-но.
Рядом ты добавлял про возможность вместо размещения указателя на таблицу хранить в области памяти экземпляра типа сразу указатели на целевые ф-ии.
Это тоже небезупречный аргумент, т.к.:
— подобная техника применима и для С++ и мы в своих проектах тоже иногда (редко) так делаем (и выходит как минимум типобезопасней, чем в Си);
— техника эта оправдана или когда кол-во таблиц виртуальных ф-ий и кол-во экземпляров близко к 1:1, или когда кол-ва ф-ий в наборе мало (1-3 от силы);
— компилятор умывает руки в плане девиртуализации; поэтому, подобную технику стоит тщательно тестировать в реальной архитектуре и на реальных данных, бо она может дать замедление чаще, чем кажется в "теории" (что лично меня не перестаёт удивлять, положа руку на).
DI>А ты вот, например, понимаешь как работают виртуальные вызовы? Я расскажу:
Думаю, понимает.
DI>1. В каждом объекте появляется указатель на таблицу виртуальных функций, выросший размер объекта приводит, например, к тому, что твой объект перестает помещаться в кеш линию, а это значит, что в память надо ходить чаще
И?
В Си-ядре Linux именно это и происходит:
https://github.com/torvalds/linux/blob/master/include/linux/fs.h
Смотрим строки:
https://github.com/torvalds/linux/blob/master/include/linux/fs.h#L362
struct address_space_operations {
int (*writepage)(struct page *page, struct writeback_control *wbc);
int (*readpage)(struct file *, struct page *);
...
Это классическая таблица виртуальных ф-ий.
Еще:
struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
...
Вот виртуальный объект, ссылающийся на экземпляр таблицы виртуальных ф-ий:
https://github.com/torvalds/linux/blob/master/include/linux/fs.h#L935
struct file {
...
const struct file_operations *f_op;
...
А вот множественное наследование от виртуальных типов:
struct super_block {
...
const struct super_operations *s_op;
const struct dquot_operations *dq_op;
const struct quotactl_ops *s_qcop;
const struct export_operations *s_export_op;
...
DI>2. С девиртуализацией компилятор может не справится, поэтому:
По моим наблюдениям, при включённом LTO компилятор слишком часто применяет девиртуализацию.
Т.е. намного чаще, чем можно было представить при взгляде со стороны.
У меня часто исчезают как сами виртуальные методы, так и экземпляры таблиц виртуальных методов при включенной опции /OPT:REF.
А вот если делать эмуляцию виртуальных методов как по ссылке на исходники ядра Linux, то там компилятор честным образом будет скакать через два уровня косвенности каждый божий раз при вызове, потому что ему недостаточно информации, чтобы делать предположения о семантике происходящего.
DI>3. Чтение таблицы ВФ по этому указателю тоже может означать поход по памяти и плюс к этому вытеснение из кеша данных, которые могут скоро понадобится
Таблица виртуальных ф-ий одна на разные экземпляры.
Для частоиспользуемых типов эта таблица живёт в "разогретой" области кеша.
В любом случае, при ручной эмуляции будет аналогично, т.е. в сравнительном плане ты ничего полезного для целей спора из этого не вытащишь.
DI>Вот теперь, когда ты знаешь про стоимость этой абстракции, скажи будут в ядре ОС использовать ВФ?
Ес-но.
Рядом ты добавлял про возможность вместо размещения указателя на таблицу хранить в области памяти экземпляра типа сразу указатели на целевые ф-ии.
Это тоже небезупречный аргумент, т.к.:
— подобная техника применима и для С++ и мы в своих проектах тоже иногда (редко) так делаем (и выходит как минимум типобезопасней, чем в Си);
— техника эта оправдана или когда кол-во таблиц виртуальных ф-ий и кол-во экземпляров близко к 1:1, или когда кол-ва ф-ий в наборе мало (1-3 от силы);
— компилятор умывает руки в плане девиртуализации; поэтому, подобную технику стоит тщательно тестировать в реальной архитектуре и на реальных данных, бо она может дать замедление чаще, чем кажется в "теории" (что лично меня не перестаёт удивлять, положа руку на).
Re[8]: А С++ то схлопывается...
Здравствуйте, Denis Ivlev, Вы писали:
DI>А ты вот, например, понимаешь как работают виртуальные вызовы? Я расскажу:
Думаю, понимает.
DI>1. В каждом объекте появляется указатель на таблицу виртуальных функций, выросший размер объекта приводит, например, к тому, что твой объект перестает помещаться в кеш линию, а это значит, что в память надо ходить чаще
И?
В Си-ядре Linux именно это и происходит:
https://github.com/torvalds/linux/blob/master/include/linux/fs.h
Смотрим строки:
https://github.com/torvalds/linux/blob/master/include/linux/fs.h#L362
Это классическая таблица виртуальных ф-ий.
Еще:
Вот виртуальный объект, ссылающийся на экземпляр таблицы виртуальных ф-ий:
https://github.com/torvalds/linux/blob/master/include/linux/fs.h#L935
А вот множественное наследование от виртуальных типов:
DI>2. С девиртуализацией компилятор может не справится, поэтому:
По моим наблюдениям, при включённом LTO компилятор слишком часто применяет девиртуализацию.
Т.е. намного чаще, чем можно было представить при взгляде со стороны.
У меня часто исчезают как сами виртуальные методы, так и экземпляры таблиц виртуальных методов при включенной опции /OPT:REF.
А вот если делать эмуляцию виртуальных методов как по ссылке на исходники ядра Linux, то там компилятор честным образом будет скакать через два уровня косвенности каждый божий раз при вызове, потому что ему недостаточно информации, чтобы делать предположения о семантике происходящего.
DI>3. Чтение таблицы ВФ по этому указателю тоже может означать поход по памяти и плюс к этому вытеснение из кеша данных, которые могут скоро понадобится
Таблица виртуальных ф-ий одна на разные экземпляры.
Для частоиспользуемых типов эта таблица живёт в "разогретой" области кеша.
В любом случае, при ручной эмуляции будет аналогично, т.е. в сравнительном плане ты ничего полезного для целей спора из этого не вытащишь.
DI>Вот теперь, когда ты знаешь про стоимость этой абстракции, скажи будут в ядре ОС использовать ВФ?
Ес-но.
Рядом ты добавлял про возможность вместо размещения указателя на таблицу хранить в области памяти экземпляра типа сразу указатели на целевые ф-ии.
Это тоже небезупречный аргумент, т.к.:
— подобная техника применима и для С++ и мы в своих проектах тоже иногда (редко) так делаем (и выходит как минимум типобезопасней, чем в Си);
— техника эта оправдана или когда кол-во таблиц виртуальных ф-ий и кол-во экземпляров близко к 1:1, или когда кол-во ф-ий в наборе мало (1-3 от силы);
— компилятор умывает руки в плане девиртуализации; поэтому, подобную технику стоит тщательно тестировать в реальной архитектуре и на реальных данных, бо она может дать замедление чаще, чем кажется в "теории" (что лично меня не перестаёт удивлять, положа руку на).
DI>А ты вот, например, понимаешь как работают виртуальные вызовы? Я расскажу:
Думаю, понимает.
DI>1. В каждом объекте появляется указатель на таблицу виртуальных функций, выросший размер объекта приводит, например, к тому, что твой объект перестает помещаться в кеш линию, а это значит, что в память надо ходить чаще
И?
В Си-ядре Linux именно это и происходит:
https://github.com/torvalds/linux/blob/master/include/linux/fs.h
Смотрим строки:
https://github.com/torvalds/linux/blob/master/include/linux/fs.h#L362
struct address_space_operations {
int (*writepage)(struct page *page, struct writeback_control *wbc);
int (*readpage)(struct file *, struct page *);
...
Это классическая таблица виртуальных ф-ий.
Еще:
struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
...
Вот виртуальный объект, ссылающийся на экземпляр таблицы виртуальных ф-ий:
https://github.com/torvalds/linux/blob/master/include/linux/fs.h#L935
struct file {
...
const struct file_operations *f_op;
...
А вот множественное наследование от виртуальных типов:
struct super_block {
...
const struct super_operations *s_op;
const struct dquot_operations *dq_op;
const struct quotactl_ops *s_qcop;
const struct export_operations *s_export_op;
...
DI>2. С девиртуализацией компилятор может не справится, поэтому:
По моим наблюдениям, при включённом LTO компилятор слишком часто применяет девиртуализацию.
Т.е. намного чаще, чем можно было представить при взгляде со стороны.
У меня часто исчезают как сами виртуальные методы, так и экземпляры таблиц виртуальных методов при включенной опции /OPT:REF.
А вот если делать эмуляцию виртуальных методов как по ссылке на исходники ядра Linux, то там компилятор честным образом будет скакать через два уровня косвенности каждый божий раз при вызове, потому что ему недостаточно информации, чтобы делать предположения о семантике происходящего.
DI>3. Чтение таблицы ВФ по этому указателю тоже может означать поход по памяти и плюс к этому вытеснение из кеша данных, которые могут скоро понадобится
Таблица виртуальных ф-ий одна на разные экземпляры.
Для частоиспользуемых типов эта таблица живёт в "разогретой" области кеша.
В любом случае, при ручной эмуляции будет аналогично, т.е. в сравнительном плане ты ничего полезного для целей спора из этого не вытащишь.
DI>Вот теперь, когда ты знаешь про стоимость этой абстракции, скажи будут в ядре ОС использовать ВФ?
Ес-но.
Рядом ты добавлял про возможность вместо размещения указателя на таблицу хранить в области памяти экземпляра типа сразу указатели на целевые ф-ии.
Это тоже небезупречный аргумент, т.к.:
— подобная техника применима и для С++ и мы в своих проектах тоже иногда (редко) так делаем (и выходит как минимум типобезопасней, чем в Си);
— техника эта оправдана или когда кол-во таблиц виртуальных ф-ий и кол-во экземпляров близко к 1:1, или когда кол-во ф-ий в наборе мало (1-3 от силы);
— компилятор умывает руки в плане девиртуализации; поэтому, подобную технику стоит тщательно тестировать в реальной архитектуре и на реальных данных, бо она может дать замедление чаще, чем кажется в "теории" (что лично меня не перестаёт удивлять, положа руку на).