На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: Qraizer, Hsilgos
Страницы: (4) 1 2 [3] 4  все  ( Перейти к последнему сообщению )  
> насколько тормозной dynamic_cast?
    Ну просто как бы вызывать в цикле 10 миллионов раз виртуальный метод, или юзать туда dynamic_cast - так а чего он ожидал то? :D Чем то приходится платить за удобство. Пусть переписывает цикл, выносит все виртуальные вызовы за цикл или пусть архитектуру переделывает. Может у него в архитектуре косяк, раз он собрался вызывать 10 миллионов раз динамик каст. Я конечно понимаю что это всего лишь синтетический тест на замерку времени работы той или иной плюшки. Но ведь тогда это остается в рамках теста! И к реальному миру имеет слабое отношение. Разве что с академической стороны вопроса.

    Добавлено
    Цитата Eric-S @
    Тест... По-моему немного надуманный и не совсем адекватный. Я сильно засомневался в его корректности. Но может я ошибаюсь или чего-то не понимаю. Поэтому не стал судить и учитывать.

    Я если честно - положа руку на сердце, признаюсь тебе - что статью я совсем не читал. Прочитал заголовок, и первые два предложения. И тут же переместился к коментариям. Поэтому не могу судить на сколько адекватный там тест, но судя по коментам - ты прав. Он не совсем адекватный.
      Цитата KILLER @
      Как по мне, так участник под ником dyadyaSerezha вполне доступно распедаливает оппоненту где он не прав. Плюс еще что то там даже переписал, замерил и получил результаты.

      Правота кого-то становится очевидной, если заранее известен ответ. Ну или несколько раз штудировать весь текст.
      Да, действительно, dyadyaSerezha адекватно и обосновано отвечает.
      И твоя позиция, очень схожа с его позицией.

      Но я, просто так, вставать на чью-то сторону, не захотел. Сначала я усомнился во всё написанном и пожелал более вразумительных пояснений.
        Цитата Eric-S @
        Но я, просто так, вставать на чью-то сторону, не захотел. Сначала я усомнился во всё написанном и пожелал более вразумительных пояснений.

        Ясненько. Ну и правильно сделал. Тема таки интересная все же. :)
          Цитата KILLER @
          И к реальному миру имеет слабое отношение. Разве что с академической стороны вопроса.

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

          Дёргать миллион раз? Ха-эм... У меня количество вызовов зависит от входных данных. Может быть ноль, а может и миллион. Но вероятно, будет не слишком много. Хотя виртуальные функции дёргаются очень злосно.

          Добавлено
          Цитата KILLER @
          Ясненько. Ну и правильно сделал. Тема таки интересная все же. :)

          А это у меня вылезает постоянно. Вот как заклинит на чём-то, как начну сомневаться.... Даже порой над очевидными вещами. Иногда вылезает профит, иногда нет. Но всегда интересно поковыряться.
            Цитата Eric-S @
            Ха-эм... У меня количество вызовов зависит от входных данных. Может быть ноль, а может и миллион.

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

            Добавлено
            Если писать какой то виндовз примитивный калькулятор, то там хоть как не извращайся, все равно на производительности это не особо скажется. Ну если все извращения в рамках разумного конечно .
            Сообщение отредактировано: KILLER -
              Цитата Eric-S @
              Хэх. На самом деле мне понятны лишь отдельные куски. А вот общего понимания не возникло.
              Что там вообще происходит?
              Зачем это нужно?
              Почему именно так, а не иначе?

              Ну вот например со списками типов, я кое-как разобрался, да ито с третьего захода, после того как колупал реализацию типа variant.
              Так и не надо колупать-то. Реализация там приведена, чтобы её можно было использовать. Чтобы понять, как использовать, достаточно прочитать пример использования.
              Там всё просто: объявляем динамически связываемые параметры (списки типов полиморфных иерархий); определяем параметры и тип их связывания (динамический или статический); объявляем сигнатуру мультиметода (список типов его параметров); определяем класс-контейнер с реализациями первичного мультиметода и всех его перекрытий (это которые apply). Макросом интегрируем visit-метод в классы, создаём диспетчер и всё – в нужных местах вызываем его и получаем управление в одном из apply.
              Вообще, с этим лучше туда.

              Добавлено
              Цитата Eric-S @
              IObject это множество классов описывающих объект.
              IProvider это другое множество классов выполняющих действие с объектом.

              Правда, один конкретный IProvider может выполнять действие лишь с одним лини нисколькими типами объектов. Так же для одного типа IObject подходит один или несколько провайдеров. Но всё равно, разнообразных сочетаний очень много.
              Это нестрашно. Относись к мультиметодам как перегрузке, фактически оно так и есть. Только перегрузка разрешается в run-time диспетчером, а не в compile-time компилятором. Ты ж когда используешь перегрузку, не особо задумываешься о том, что не каждая комбинация параметров имеет точно однозначное соответствие с прототипом одной из перегруженных функций.

              Добавлено
              Цитата Eric-S @
              Я ведь почему создал тему? Полистал инет, почитал всякие фразочки на счёт dynamic_cast и захотел понять, столь ли страшен зверь, как его малюют?
              Знаешь, те мультиметоды писаны под C++03. Я уже с полгода как в свободное время потихонечку портирую... э-э-э, начиняю их новостандартными фичами, и они становятся куда удобнее. Только делается это довольно медленно, т.к. меня ничто не гонит, а свободного времени не так много (честно? я лучше к прохождению Wolfenstein 2 на Mein leben подготовлюсь, благо Episode 1 на ютуб уже выложил, и отдохну от Freescale-овых приколов, чем буду отдыхать в мультиметодах с приколами от Visual Studio), и в частности я решил сделать сразу две реализации: ту самую, и ещё без интрузивного visitor-а.
              Естественно, вторая реализация далеко не так производительна. Если первая стоит два всего лишь полиморфных вызова на каждый динамически связываемый параметр, то вторая выполняет бинарный поиск в сортированном контейнере на каждый параметр. Там, правда, нет dynamic_cast<>, но много typeid().before(). И к моему удивлению скорость упала далеко не так сильно, как я думал: после всех оптимизаций всего-то раза в три-пять.
              Так что слушать пессимистов полезно, но надо понимать, всё познаётся в сравнении. Та же задача, решаемая велосипедом вместо стандартных средств, будет менее надёжной и более дорогой в создании, и вряд ли окупится в одноразовом применении. Но после доработки напильником в плане оптимизации ресурсоёмких операций стандартные средства зачастую оказываются не так плохи.

              Добавлено
              Цитата KILLER @
              Каждый вызов виртуального метода - сродни вызову dynamic_cast по сути. Так как при вызове вирт. метода, происходит такой же обход VMT, как и при вызове dynamic_cast, ну возможно он немножко шустрее чем каст.
              Это ты сейчас описал не полиморфный вызов, а динамический. В Плюсах такого нет, но есть в ОбджектПаскале, а также во всякоразных фреймворковых библиотеках, типа Qt. Виртуальный вызов по факту просто косвенный: в конструкторе в экземпляр пишется указатель на vmt, в vmt при компиляции/сборке записаны указатели на методы, так что в рантайм происходит вычитка поинтера на vmt, его индексация в массиве поинтеров на методы, вычитка целевого поинтера и вызов по нему (с возможным кастом this, для чего компилер делает ещё един маленький thunk, но уже с прямым вызовом). По сравнению с dynamic_cast<> тут нет ничего ресурсоёмкого.
              Сообщение отредактировано: Qraizer -
                Цитата Qraizer @
                Это ты сейчас описал не полиморфный вызов, а динамический. В Плюсах такого нет, но есть в ОбджектПаскале, а также во всякоразных фреймворковых библиотеках, типа Qt. Виртуальный вызов по факту просто косвенный: в конструкторе в экземпляр пишется указатель на vmt, в vmt при компиляции/сборке записаны указатели на методы, так что в рантайм происходит вычитка поинтера на vmt, его индексация в массиве поинтеров на методы, вычитка целевого поинтера и вызов по нему (с возможным кастом this, для чего компилер делает ещё един маленький thunk, но уже с прямым вызовом). По сравнению с dynamic_cast<> тут нет ничего ресурсоёмкого.

                Да, согласен, я тут немного наврал ты прав. Там же указатель как раз и перескакивает на нужныые методы по таблице во время присваивания, когда не нужно кастить или когда явно скастовали.
                  Цитата KILLER @
                  Может тогда стоит пересмотреть архитектуру и использовать массив каких нибудь легковесных объектов, взамен вызова виртуального(ых) метода(ов). Хотя опять же, это имеет смысл тогда, когда тебе действительно это мешает, и серьезно искажает ожидаемый результат.

                  Пока особо не мешает. Впрочем, часть операций я закэшировал.
                  Например, у меня фабрика изначально создавала множество объектов.
                  Причём одинаковых объектов.
                  Там не только вызов виртуальных методов, но выделение и освобождение блоков памяти из кучи.
                  Сейчас я закэшировал. Объект создаётся один раз и указатель сохраняется. В следующий раз, отдаётся именно указатель на старый объект. Виртуальный методо и память не выделяются.

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

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

                  Добавлено
                  Цитата KILLER @
                  использовать массив каких нибудь легковесных объектов, взамен вызова виртуального(ых) метода(ов).

                  Не знаю... Там достаточно сложно и разветвлено. Как-то проще с виртуальными методами. Например довольно часто надо класть объекты в коллекции, чтобы единообразно их хранить и обрабатывать. Причём объекты разные, с разными свойствами.
                  В некоторых, отдельных случаях, что-то можно оптимизировать. Но в остальных случаях, надо долго думать и не факт, что чего-то придумается.

                  Добавлено
                  Цитата Qraizer @
                  Реализация там приведена, чтобы её можно было использовать.

                  В чистом виде пока не заинтересовала. Реализацию изучал, чтобы подсмотреть чего-нибудь интересненькое, прокачать свой левел.

                  Добавлено
                  Цитата Qraizer @
                  Так что слушать пессимистов полезно, но надо понимать, всё познаётся в сравнении. Та же задача, решаемая велосипедом вместо стандартных средств, будет менее надёжной и более дорогой в создании, и вряд ли окупится в одноразовом применении. Но после доработки напильником в плане оптимизации ресурсоёмких операций стандартные средства зачастую оказываются не так плохи.

                  Ну да. Это верно. Только вот надо эти стандартные средства правильно использовать.
                  Я как-то одну свою прогу ускорил, просто навтыкав в неё std::move() и string_veiw... Субьъективно раз в десять. А по приблизительным замерам, где-то на 30%. Ну много у меня там std::string туда-сюда копировались!
                    Цитата KILLER @
                    причем строится она конкретно в момент создания объекта динамически полиморфного типа, т.е. в рантайме, да еще и в куче
                    Не совсем так. Точнее, совсем не так.
                    Таблица виртуальных методов фиксирована для всего класса, поэтому нет необходимости строить её заново при создании объектов Все таблицы виртуальных методов компилятор создаёт статически на этапе компиляции. Тогда же строит дерево наследования, необходимое для работы dynamic_cast.
                    При создании объекта в него просто добавляется ссылка на соответствующую таблицу виртуальных методов.

                    Вызов такого виртуального метода стоит достаточно мало. Если вызовы невиртуальных методов выполняются простой командой вызова подпрограммы, то для виртуальных сперва из объекта извлекается адрес VMT, а потом происходит вызов функции по заданному смещению. То есть прямой вызов функции заменяется на одну выборку из памяти и косвенный вызов.

                    dinamic_cast тоже не всегда сильно тормозит. В случае простой иерархии, когда граф наследования имеет вид дерева, dynamic_cast всего лишь проверяет находится ли таблица переданного ему указателя в поддереве наследников затребованного класса (или является корнем). Это делает короткий цикл из трёх команд. Плюс пара команд, нужных в случаях нулевого указателя и невозможности приведения. В таком случае время его работы пропорционально высоте дерева наследования от затребованного класса до его "самой базовой" базы.

                    Реально тормозить он начинает в случае множественного наследования при попытке скастить указатель в иерархию другого базового класса. Поскольку в этом случае просто пробежаться по дереву наследования не получается, и приходится производить полноценный поиск.
                      Цитата amk @
                      Реально тормозить он начинает в случае множественного наследования при попытке скастить указатель в иерархию другого базового класса.

                      Вот мне стало интересно забыл уже про такие проблемы ;)
                      Реально тормозит это сколько? ( секунда, минута, год или счет на микро если не нано?)
                      и в сравнении с чем, когда у тебя в апликации есть скажем сокет до двух секунд,
                      то это фигня, случае множественного наследования если наследовать от 10классов,
                      то наверно затормозит, Я не слышал чтоб бывало больше двух, ну и меня еще в 96году
                      научили , там где критична скорость не используй VF, хорошая темя для вопроса на интервью ,
                      проверить понимает чел тему или нет, для реальной задачи похоже на холивар,
                      IMHO.
                        Я всё что нужно для подсчёта написал. при восходящем приведении сканирование идёт по прямому пути. Нарисуй граф наследования и посчитай полное число ветвей от него к переданному указателю. Получишь число переходов.
                        Сообщение отредактировано: amk -
                          Как уже писал, всё же решил попробовать избавиться от приведения, чисто ради сравнения результата.
                          Так вот, результат.... Я разницы не заметил.
                          Скорость такая же... Если где-то увеличилась, то незначительно. А ведь кроме dynamic_cast, я раздухарившись, выпилил shared_ptr из той части кода, использовав простые указатели. Возможно ннебольшое ускорение.
                          Объём... Я предполагал уменьшение, за счёт удаления функций-обёрток приведения. Но были добавлены шаблонные классы, которые исполнили роль промежуточного виртуального интерфейса. Видимо из-за них, бинарный код даже немного вырос.
                          Но все результаты, можно считать статестической погрешностью. Поскольку, часть архитектуры пришлось перебирать и переделывать.
                          А вот взрывного улучшения или ухудшения мною вообще не замечены. Разве что исходный код, стал немного строже в плане типов и запутаннее в плане подключения заголовочных файлов.
                            Цитата Eric-S @
                            Разве что исходный код, стал немного строже в плане типов
                            Вот это правильно.
                            Цитата Eric-S @
                            и запутаннее в плане подключения заголовочных файлов.
                            А вот это скорее из-за того, что по живому резал.
                              Цитата amk @
                              Цитата Eric-S @
                              и запутаннее в плане подключения заголовочных файлов.
                              А вот это скорее из-за того, что по живому резал.

                              Там обратное связывание.

                              Группа файлов, в которых описываются классы "*object.h", требуют подключение заголовочного файла "manager.h".
                              Он же в свою очередь требует подключение шаблона "iprovider.h", а затем всех "*object.h".

                              И таких обратных зависимостей, несколько штук.

                              Разрулил это предобъявлением некоторых классов и подключением хидеров полных объявлений, в *.cpp файлы.

                              Резал конечно по живому, но как не кручу, иных вариантов не нахожу. Собственно в начальном варианте приведение использовал как раз для снижения обратной связнности.
                                Цитата amk @
                                Не совсем так. Точнее, совсем не так.
                                ...
                                В случае простой иерархии, когда граф наследования имеет вид дерева, dynamic_cast всего лишь проверяет находится ли таблица переданного ему указателя в поддереве наследников затребованного класса (или является корнем)
                                Цитата amk @
                                при восходящем приведении сканирование идёт по прямому пути. Нарисуй граф наследования и посчитай полное число ветвей от него к переданному указателю. Получишь число переходов

                                Как ты говоришь - "Не совсем так. Точнее, совсем не так". ;)
                                Для реализации dynamic_cast (и прочих задач rtti) достаточно и практически удобно\целесообразно хранить только "обратную" ссылку наследника на его непосредственного предка (или нескольких предков в случае множественного наследования). Хранить же "прямые" ссылки от предка на всех его возможных наследников (коих может быть туева хуча в случае базовых классов библиотек типа MFC, VCL и т.п.) практически нецелесообразно и нереально.
                                Поэтому dynamic_cast независимо от вида приведения типа ("восходящее" от наследника к предку, или "нисходящее" от предка к наследнику) начинает проверку от реального (а не номинального) типа кастуемого объекта и далее идет вверх по иерархии (перебирая предков) пока не встретит требуемый тип или не дойдет до корневого типа иерархии.
                                Поэтому в примере #1, когда dynamic_cast выполняется не "от балды", а в соответствии с доп.идентификатором класса ptr->Category (или же по typeid), производится всего лишь одна проверка - на соответствие реального типа объекта ptr* (его поля VMT) требуемому типу Beta (указателю на VMT этого класса) независимо от того, является ли Beta прямым наследником номинального типа Alpha или же его пра-пра-внуком в десятом поколении. Отсюда и вывод
                                Цитата Eric-S @
                                Так вот, результат.... Я разницы не заметил.
                                0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                0 пользователей:
                                Страницы: (4) 1 2 [3] 4  все


                                Рейтинг@Mail.ru
                                [ Script execution time: 0,0594 ]   [ 17 queries used ]   [ Generated: 29.03.24, 10:07 GMT ]