Dore: Ogre

Интересно только специалистам по рентгеновской кристаллографии

В программировании различают run time и compile time. То, что известно компилятору в compile time, следует тогда же и делать; тогда программа будет работать быстрее. Например, полиморфный вызов виртуальной функции происходит медленнее, чем вызов статической: ее адрес надо сначала найти в vtable.

Между тем, значительное количество проектов, формально являющихся объектно-ориентированными, используют полиморфизм в следующем урезанном виде. Пусть, например, мы моделируем солнечную систему. В начале работы программа читает конфигурационный файл и инстанциирует из него некоторое количество объектов, имплементирующих бизнес-логику Солнца, Земли, Луны, планет, американских разведывательных спутников и проч. Все они являются потомками абстрактного класса Небесное Тело. После этого новые объекты больше не создаются, а существующие не уничтожаются; движок только полиморфно вызывает у каждого из них по очереди функцию "сделать следующий ход", пока не будет достигнут конец вселенной или не будет нажата комбинация клавиш Ctrl-C.

Легко видеть, что полиморфизм тут используется только для удобства организации и расширения кода путем добавления все новых и новых небесных тел, а также потому, что людей, пишущих код именно таким образом, легче нанять. Конкретно, после того, как конфигурационный файл прочитан и все необходимые объекты инстанциированы, адреса виртуальных функций, специфичных для каждого конкретного указателя-на-небесное-тело, оказываются навсегда известными. В такой ситуации естественнее различать compile time, run time before reading config, и run time after reading config.

Соотвественно, пропатченный рантайм C++ должен, по окончании чтения конфига, пройтись по коду, найти в нем все поиски виртуальных функций в vtable, и хардкоднуть на их место известные к тому времени адреса в памяти.
  • Current Mood: creative
Я сомневаюсь что в задачах подобных солнечной системе вызов функций и есть то узкое место определяющее скорость вычислений
Заголовок хороший, такие приятно игнорировать.
Есть такой анекдот: "А ты умеешь играть на скрипке?" - "Не знаю, может и умею, ни разу не пробовал"

Также и здесь - вдруг случайно окажешься специалистом по рентгеновской кристаллографии.
Сложности с распараллеливанием - это та проблема, которую хочется иметь.
Что нам этот внешний цикл ? Это внутренний цикл, для каждого тела по остальным телам, что существеннен.
нет, внешний конечно есть, только все время программа проводит не в нем.
Бьерн Страуструп. Дизайн и эволюция языка C++, или Как я родил ёжика.
Андрей Александреску. Современное проектирование на С++, или Как я выебал ёжика.
Это тоже было бы весьма полезно. В ту же кучу - очень неудобно, что сейчас конструкция if (0) {...} исчезает из окончательного кода целиком, а if (x) {...}, где x прочитан из конфига и равен нулю - нет.
Дык я же написал, как сделать с минимальным импактом. Нужен новый модификатор типа: const_after_having_read_config.
Предлагаю простое решение, требующее лишь слегка изменить формат конфига.
Итак, для начала - файл конфига будет расширение cpp. (Дальше, надеюсь, и так понятно =))
Тогда нельзя будет никого нанять, или каждому нанятому надо будет по году объяснять, как делать. С точки зрения программиста и его существующих практик решение должно быть прозрачным.
Мне кажется любой C++ программист сделает именованную константу в коде вместо конфига, если он знает, что в рантайме оно не будет меняться. Но это в случае, если изменения в конфиг будет вносить C++ программист. Если же изменения будут вносить другие люди, то мы либо платим performance'ом за гибкость, либо вручную имплементируем оптимизацию. Вашу оптимизацию вполне можно имплементировать вручную для конкретного случая.

Вопрос - должен ли это делать компилятор?
Может быть. А может быть и нет. Надо weigh benefits against costs. SMC может вызвать разные CPU caching/parallelization issues, это надо делать аккуратно. Кроме того, ваш конкретный use case не кажется достаточно общим. Если бы runtime умел с точностью определять life span of objects, он мог бы временно заменять виртуальные вызовы на статические, или даже на jump'ы. Но это по сложности уже близко к тому, чтобы, скажем, уметь распознавать неэффективные алгоритмы, и на лету заменять их на более эффективные. Если мы столько работы отдаём компилятору, то это перестаёт пахнуть C++-ом :) Фишка C++-а в том, что на нём пишут люди, которые хорошо понимают, как оно будет работать, и даже если есть оптимизации, программист знает про них, и специально пишет код в рассчёте на них.
Я выше пишу чуть подробнее - нужен новый модификатор const и еще один проход опттимизатора с его учетом после чтения конфига.
Вспомнился prefetch из Windows. Windows собирает статистику, в какой последовательности обычно грузятся странички из EXE/DLL, и время от времени сортирует их на диске так, чтобы это было последовательное чтение. (Вы знали об этой фиче?).
Польностью согласен. С++ должен стать Явой с JIT.
Интересно, а C++ программисты слышали, что кроме наследования есть еще и агрегация как способ композиции?
Аггрегирование неизвестно чего не работает быстрее, чем вызов неизвестно какой функции.
Именно поэтому агрегацию нужно включить в это обсуждение.
Нет, это я слушаю (я же не программист): например, 1) pure virtual functions –– они требуют обращения к vtables? 2) может ли компилятор так соптимизировать, например if или case, чтобы это было быстрее чем обращение к vtable?
Фундаментально, проблема заключается в том, что неизвестно заранее, какую функцию вызывать. Это становится известным только после чтения конфига. К тому времени программа уже давно скомпилирована и должна оставаться неизменной до следующей перекомпиляции. В такой парадигме ничего поделать нельзя, хоть аггрегируй, хоть наследуй.