Иногда, скорость - это все. В таких случаях приходится избавляться от всего замедляющего работу. Бывает, что приходится отказываться и от элегантности архитектуры. Именно так бывает если приходишь к выводу, что для ускорения необходимо избавиться от таблицы виртуальных функций.
Предлагаю Вашему вниманию способ "виртуальных функций на этапе компиляции". Этот подход не перекрывает всю функциональность виртуальных функций, но часто его возможностей достаточно.
Итак код:
template <class T> class B
{
public:
void Hello()
{
T* pT = static_cast<T*>(this);
pT->MyName();
}
void MyName() { cout << "This is B"; }
};
class D1 : public B<D1>
{
};
class D2 : public B<D2>
{
void MyName() { cout << "This is D2"; }
};
int main()
{
D1 d1;
D2 d2;
d1.Hello(); //"This is B"
d2.Hello(); //"This is D2"
return 0;
}
Тут "виртуальной" является функция MyName. Весь фокус состоит в том, что при объявлении производного класса базовому классу передается тип нового класса в качестве параметра шаблона. А базовый класс при вызове "виртуальных" функций преобразовывает this к необходимому типу.
Думаю многих смущает конструкция вида "class D2 : public B {...};". Но люди, вкусившие элегантность С++, сразу вспомнять что идентификатор класса считается объявленным сразу после "class D2", а следовательно может быть использован в списке наследования.
Удачной оптимизации ![]()



октября 12, 2006 в 9:45 pm
Насколько данная идиома применима в диамантовым иерархиям, например:
class Base {};
class ChildLeft : virtual public Base {};
class ChildRight: virtual public Base {};
class Child: public ChildLeft, public ChildRight {};
Как себя она ведет при различных вариантах множественного наследования? Каким образом можно вынести данную идиому до уровня паттерна и можно ли вообще?
октября 13, 2006 в 12:45 pm
Я использовал данный подход для неперекрестного множественного наследования.
Не забывай о первоначальном понятии слова паттерн - это не обязательно базовый класс, добавляющий функциональность.
Как использовать для приведенного примера? Незнаю, поэкспериментируй.
Насчет, паттерна. Если для тебя, Дима, так важно обозвать подход паттерном - называй его так
А вообще приятно, что хоть кто-то прочитал эту статью. Я уж подумал, что зря писал.
октября 13, 2006 в 1:16 pm
я читал.
понял мало
как говорится, мне б ваши проблемы
октября 15, 2006 в 5:34 pm
Решил добавить.
Если кто решит как применить подобный подход для указанной Димой иерархии, милости просим написать в комментариях. Было бы интересно.
октября 18, 2006 в 6:56 pm
ох. а что это за случаи, когда скорость доступа к методам настолько существенна?
октября 20, 2006 в 10:49 am
2usix:
например, нейронная сеть с миллионами элементов и нетривиальной структурой (т.е. обязательна реализация "каждому нейрону свой объект"). И для каждого из нейронов вызывается набор методов.
Вот и посчитай
октября 20, 2006 в 4:09 pm
Ну вот, заговорили о высоком
Да, плюсы рулят, по ходу зе бест 
октября 27, 2006 в 12:49 am
Имхо дешевле купить Deep Blue, чем писать что-то серьозное в таком стиле.
октября 27, 2006 в 12:43 pm
doc, одно к другому не относиться.
Если бы все так мыслили, то нафиг вообще алгоритмы. Ведь все можно перебором решить
ноября 4, 2006 в 2:22 pm
2seagull:
миллион объектов? кошмар! остается только догадываться, какие мысли зародятся в нейронной сети, реализованной таким образом
ЗЫ: может использовать по flyweight'у на каждый поток? можно с кодогенерацией...
ноября 5, 2006 в 5:51 pm
да уж это мыслящий монстер :)))
А можно поподробней по поводу flyweight?
ноября 6, 2006 в 9:43 am
наверное имелся ввиду паттерн проектирования
http://en.wikipedia.org/wiki/Flyweight_pattern
ноября 6, 2006 в 10:39 am
Паттерн полезный, но не в данном случае. Но, все равно спасибо за то что немного образовали меня, добавив новое модное слово на старое понятие.
Хотя хотелось бы заметить, что "песня совсем не о том" - обсуждаем мы не реализацию НС, а предложенный подход оптимизации.
декабря 13, 2006 в 11:31 pm
Я тоже довольно много задаюсь вопросом о ускорении работы своих программ, поэтому я избегаю использования виртуальных функций. Но один вопрос меня действительно сильно волнует: влияет ли на скорость наследование ( без виртуальных функций )? Если влияет, то насколько?
декабря 14, 2006 в 12:50 am
Я вот о чем подумал. Может нам ради производительности вообще отказаться от ООП и писать на ASM? А то пишем на каких-то тормознутых Java, Ruby, Perl, Python, PHP, C, C++, .NET, SmallTalk (нужное подчеркнуть). Что нам дают более абстаркные языки? Какие, например, преимущества у SmallTalk против ASM(C, C++)?
декабря 14, 2006 в 12:52 am
Вопрос к коду. Насколько я понял мы виртуальные функции перегружаем на этапе компиляции. Правильно?
декабря 14, 2006 в 3:36 pm
2 Руслан:
Как по мне языки высокого уровня дают нам скорость разработки, а это уже много. Но бывают таки участки кода, где стоит "спуститься", пожертвовав "высоким и красивым" в пользу эффективности.
>Вопрос к коду. Насколько я понял мы виртуальные
>функции перегружаем на этапе компиляции. Правильно?
По сути правильно, но не совсем. Немного остается работы и на run-time.
2 kolobok:
>влияет ли на скорость наследование ( без виртуальных
>функций )? Если влияет, то насколько?
Сложный вопрос. Не совсем ясно, что подразумевается под влиянием наследствения на скорость.
Я так понимаю у вас есть код который можно написать в рамках ООП или без ООП. так? Тогда напишите 2 таких кода и замеряйте время выполнения. Будет интересно почитать вашу статью по этому поводу.
декабря 14, 2006 в 5:15 pm
>> kolobok
Если тебе настолько важна скорость, отпрофилируй код и перепиши critical sections на ASM вставки. В пограничных случаях можно использовать замещение, вместо более громоздкого наследования с RTTI. Хотя на практике, проблеммы недостаточной производилельности системы -- это следствие недостроенной архитектуры. Как вариант можно смоделировать наследование функторами.
декабря 14, 2006 в 5:28 pm
>Вопрос к коду. Насколько я понял мы виртуальные
>функции перегружаем на этапе компиляции. Правильно?
По сути правильно, но не совсем. Немного остается работы и на run-time.
тут Виталик схитрил
на самом деле действительно все делается на этапе компиляции: при инстранцировании шаблона в фукнции Hello будет происходить передача управления на замещенную фукнцию MyHello в результирующем классе T. В runtime это будет выглядеть просто как вызов обычной фукнции (как раз это наверное и имел ввиду Виталик
).
декабря 14, 2006 в 9:02 pm
Где-то попадала интересная ссылка на преимущества лисп. Сколько операций нужно(можно) оптимизировать в компиляторе С++, что бы получить выиграш по скорости?
Сколько операций нужно(можно) в Self, для увеличения скорости?
И еще на закуску. Интересный факт. Извесно, что узкое место многих сложных бизнес систем - вызов полиморфного метода. Второй интересный факт: эта операция работает в Ява быстрее, чем в С++. Поразительно, но факт. К сожалению не могу привести ссылки на первоисточник. (Статья про SmallTalk и Self) Про рефлексию, AOP и DynamicProxy я вообще молчу.
декабря 15, 2006 в 2:07 am
2 Дима:
- все это тоже займет процессорное время, хотя возможно современные компиляторы это дело и прооптимизируют.
Ничего я не хитрил :))) Просто в runtime выполняется излишний static_cast + выделение места под временный указатель + присвоение
Но остается очевидным что такие растраты по сравнению с работой виртуальной функции - мизерны. Видимо, Дима решил их не принимать во внимание. Что же эти моментами можно пренебресь вполне обоснованно
2 Руслан:
Все что ты написал очень заинтересовало. Большая просьба найти первоисточники.
и еще раз к Диме:
>Как вариант можно смоделировать наследование
>функторами.
Звучить сюрреалистично для нас незнающих, хотя и радует слух человека любящего математику
Можно поподробней.
декабря 15, 2006 в 10:57 am
Виталик, что бы не быть голословным по поводу того, что static_cast никаких действий в runtime не влечет: http://www.cppreference.com/keywords/static_cast.html, http://www.codeproject.com/cpp/static_cast.asp.
декабря 15, 2006 в 11:29 am
Но еще остается:
>+ выделение места под временный указатель + присвоение
Хотя это можно перебороть инлайн функцией:
T* getthis()
{
return static_cast<T*>(this);
}
А потом использовать так:
getthis()->MyName();
Вот теперь мы точно перенесли все на время компиляции.
И еще раз повторюсь: Дима просвети по поводу функторов.
декабря 15, 2006 в 11:54 am
http://ckdev.org.ua/2006/12/15/modelirovanie-virtualnyih-funktsiy-funktorami/