2015-05-18 5 views
1

Существуют ли методы/библиотеки, которые позволяют гибко иметь иерархию классов (которая имеет функции virtual), но, как только были определены типы объектов во время выполнения, разрешена девиртуализация вызовов функций?C++ devirtualization во время выполнения?

Для простого примера предположим, что у меня есть программа, которая считывает типы формы (круг, прямоугольник, треугольник и т.д.) из некоторого файла конфигурации, чтобы построить структуру некоторых данных указанных форм (например, vector<shape*>):

class shape { 
public: 
    virtual void draw() const = 0; 
    // ... 
}; 

class circle : public shape { 
public: 
    void draw() const; 
    // ... 
}; 

// ... 
vector<shape*> shapes; 

Очевидно, что если я хочу, чтобы нарисовать все фигуры, я могу сделать:

for (auto&& s : shapes) 
    s->draw(); 

Каждый раз такое делается итерация над shapes, вызовом virtual функции сделан для вызова draw() для каждый форма.

Но предположим, что один раз shapes создан, это never собирается снова меняться на всю жизнь программы; и далее предположим, что draw() будет называться раз. Было бы хорошо, если после того, как известны фигуры, был способ «девиртуализировать» вызовы draw()во время выполнения.

Я знаю о методах оптимизации для devirtualize virtual вызовов функций в время компиляции, но я не с просьбой об этом.

Я был бы очень удивлен, если бы был умный взлом, чтобы сделать это на C++ напрямую, поскольку одним из способов сделать это было бы изменение машинного кода в памяти во время выполнения. Но есть ли там библиотека C++, которая позволяет такое?

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

Примечание: раствор должен быть кросс-платформенный, то есть, по меньшей мере, с помощью GCC/лязг и VC++.

+2

Как бы эта девиртуализация работала, если в списке были разные фигуры? Будет ли это (концептуально) заменить вызов s-> draw() внутри цикла for с помощью (if type == Circle) s-> circle :: draw(); else if (type == Square) s-> square :: draw(); тип инструкций? Если это так, я не уверен, что это обязательно будет более эффективным. –

+0

Если дальнейших подклассов формы нет, вы можете сами «девиртуализировать» себя, наложив друг на друга. Но, пока есть два или более подкласса, если компилятор не может каким-то образом доказать себе, что конкретный кусок кода будет использовать только определенный подкласс, он должен использовать вызов виртуальной функции. Это маловероятно, но возможно. После «auto x = новый круг»; компилятор знает, что, поскольку у него есть «x» под рукой, «x-> draw()» можно безопасно девиртуализировать, и многие компиляторы, вероятно, уже делают это. К сожалению, масштабы таких оптимизаций, как правило, довольно малы. –

+1

Выполняли ли вы профилирование, указывающее на то, что прыжок vtable имеет измеримое влияние на производительность? vtable звонки по-прежнему * очень * дешево. По словам Дональда Кнута, преждевременная оптимизация - это корень всего зла. – Dai

ответ

1

Я вполне уверен, что нет такой вещи, как магия, «Здесь компилятор, больше нет подклассов, которые я собираюсь определить, и этот список не изменится, поэтому исключить служебные вызовы виртуальной функции " вид.

Одна вещь, которую вы можете сделать, которая может помочь с виртуальными вызовами в чрезвычайно критических ситуациях, - это отсортировать список ваших форм по их подтипам. Например, вместо спорадического шаблона подтипов, таких как круг, прямоугольник, треугольник, прямоугольник, треугольник, квадрат и т. Д., Вы хотите изменить эти типы так, чтобы они образовывались как круг, круг, круг, круг, ..., квадрат, квадрат, квадрат, ... и т. д. Это эффективно оптимизирует предсказание ветвления. Я не знаю, подходит ли этот метод или дает много пробега с новейшими архитектурами и оптимизаторами, но было не так давно, когда я был жив, когда это было очень полезно.

О JIT, я немного изучал эту область. Я бы не стал рекомендовать попытку найти решение JIT, чтобы волшебным образом сделать ваш код на C++ быстрее.

Вместо этого, я исследовал его, поскольку моя программа уже имеет предметно-ориентированный язык, визуальный вид узловой языка программирования GUI, где вы рисуете соединения между узлами (функцией), вместо того, чтобы писать код, чтобы сделать новые вещи, как шейдеры и фильтры изображений (аналогичные BluePrint от Unreal Engine 4). В настоящее время это не так быстро, как рукописный собственный код, поэтому я был заинтересован в изучении маршрута генерации кода/JIT. В настоящее время он больше похож на интерпретатора.

Я пробовал Tiny C и LCC для них, но одна вещь, которую я нашел довольно разочаровывающей в них, заключается в том, что их оптимизаторы не так сложны, как ваши коммерческие компиляторы. Я часто получал результаты в среднем в 3-4 раза медленнее, чем MSVC или GCC. Они в противном случае замечательны, так как они настолько легки и легки вставлять.

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

+0

«Вот компилятор, ...» Уже неправильно. Мой вопрос явно включал «во время выполнения». Компилятор явно компилируется. –

Смежные вопросы