- 01
- 02
- 03
- 04
- 05
- 06
- 07
- 08
- 09
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
#include <iostream>
#include <stdlib.h>
#include <typeinfo>
using namespace std;
#define ololo for(volatile register int i=0;i<10;++i);
struct VB
{
virtual void f() const =0;
};
class V1: public VB
{
void f() const {ololo}
};
class V2: public VB
{
void f() const {ololo}
};
struct TU1
{
inline void f() const {ololo}
};
struct TU2
{
inline void f() const {ololo}
};
struct TUB
{
const type_info* type;
union
{
TU1 tu1;
TU2 tu2;
};
template<class T>
void ctor()
{
this->type=&typeid(T);
}
template<class T>
inline void call(const T& f)
{
if(this->type==&typeid(TU1))
f(this->tu1);
else
f(this->tu2);
}
};
enum {N=1000, N2=N*50};
int main() {
cout<<"ok"<<endl;
{
VB*v[N];
for(int i=0;i<N;++i)
if(rand()%2)
v[i]=new V1;
else
v[i]=new V2;
volatile clock_t a=clock();
for(int j=0;j<N2;++j)
for(int i=0;i<N;++i)
v[i]->f();
volatile clock_t b=clock();
cout<< (double)(b - a) / CLOCKS_PER_SEC<<endl;
}
cout<<"ok"<<endl;
{
TUB v[N];
for(int i=0;i<N;++i)
if(rand()%2)
v[i].ctor<TU1>();
else
v[i].ctor<TU2>();
struct Continuation
{
inline void operator()(const TU1& a) const {a.f();}
inline void operator()(const TU2& a) const {a.f();}
} cps;
volatile clock_t a=clock();
for(int j=0;j<N2;++j)
for(int i=0;i<N;++i)
v[i].call(cps);
volatile clock_t b=clock();
cout<< (double)(b - a) / CLOCKS_PER_SEC<<endl;
}
cout<<"ok"<<endl;
return 0;
}
LispGovno 31.10.2012 18:27 # +1
Интересно, что это означает в контексте стандарта С++?
vse_govno 31.10.2012 19:07 # +3
LispGovno 31.10.2012 18:30 # 0
3.14159265 31.10.2012 18:39 # +3
Ты обманываешь рандомом предсказатель переходов. В реальных ситуациях там не 50/50 а какой-то метод дергается чаще.
А в той же жабе instanceof прекрасно им оптимизируется.
Ну и сделай штук 5-6 полиморфных классов - интересно посмотреть. Плюс не стоит забывать про кейс. Хотя можно сказать заведомо что сишный кейс - кал.
Бенчи в ideone - это жестоко.
LispGovno 31.10.2012 18:42 # 0
bormand 31.10.2012 18:44 # +4
Не в 100%. Для косвенных переходов тоже есть предсказатель, правда, если мне не изменяет память, помнит он только самый последний вызов. Т.е. если дрючить одну и ту же виртуальную функцию - угадает, если разные по очереди - уже нет.
LispGovno 31.10.2012 18:49 # 0
Да полиморфизм виртуальных функций должен ещё больше выиграть. Мне это почему то кажется очевидным, поэтому я ничего не делал. Даже если заюзать деление пополам у if в tagget union. Результаты могут пошатнутся в сторону tagget union только если мало полиморфных классов (типа 2) и притом одна ветка if всегда выполняется много чаще других (например первый полиморфный класс содержится в массиве v много чаще других).
Ещё наверное сравнение if(this->type==&typeid(TU1)) можно оптимизировать, ибо &typeid(TU1) неплохо бы заменить на константу. Оно константно, но компилятор может превратить это в вызов функции.
3.14159265 31.10.2012 18:54 # +3
Да мне тоже много чего казалось очевидным, пока не начал креститься проверять лично.
PS> Внезапно на хабре сделали хороший, добротный тест:http://habrahabr.ru/post/118087
absolut 31.10.2012 19:02 # +1
это к Тарасу
LispGovno 31.10.2012 19:04 # 0
3.14159265 31.10.2012 19:12 # +1
И он соснул.
LispGovno 31.10.2012 19:38 # 0
LispGovno 31.10.2012 18:56 # +1
virtual functions: 1.94 сек
tagget union: 1.6 сек
Видимо зависит от проца.
Мы кажется выяснили, что на IDEONE - AMD.
Возможно на liveworkspace.org - Intel или более древний\более новый AMD.
3.14159265 31.10.2012 19:01 # +5
И повторюсь - делать бенчи в online ide - это конечно хардкор.
LispGovno 31.10.2012 19:33 # −1
absolut 31.10.2012 20:11 # +8
LispGovno 31.10.2012 20:18 # −1
bormand 31.10.2012 21:12 # +7
LispGovno
bazhenovc 31.10.2012 23:40 # +4
Fai 01.11.2012 00:10 # +4
roman-kashitsyn 01.11.2012 00:15 # +7
Fai 01.11.2012 02:04 # +5
LispGovno 01.11.2012 02:17 # 0
http://ideone.com/ICydFM
roman-kashitsyn 01.11.2012 11:59 # +4
LispGovno 01.11.2012 12:22 # 0
roman-kashitsyn 01.11.2012 12:36 # 0
defecate-plusplus 01.11.2012 12:44 # +3
потому то микрософт мерзкий с99 никогда не внедрит
LispGovno 01.11.2012 12:53 # 0
defecate-plusplus 01.11.2012 12:55 # +2
микрософт, как обычно, поддерживает только все самое последнее
roman-kashitsyn 01.11.2012 12:57 # +3
> поддерживает только все самое последнее
т.е. c89?
roman-kashitsyn 01.11.2012 12:56 # +4
LispGovno 01.11.2012 13:01 # −2
А в чем особенности этих?
LispGovno 01.11.2012 22:26 # 0
UncleAli 02.11.2012 00:24 # +2
LispGovno 02.11.2012 00:56 # −1
LispGovno 01.11.2012 00:29 # −1
"Как программировать на C++ без извращений?".
В конце книги вывод: "Это очень просто. Не программировать на С++".
В главе "Благодарности и пожелания автору от рецензентов": "Как можно проще программируйте на С++ и не будьте таким развратным."
LispGovno 01.11.2012 00:32 # −2
bazhenovc 01.11.2012 12:23 # +1
absolut 01.11.2012 12:40 # 0
bazhenovc 01.11.2012 15:30 # 0
TarasB 01.11.2012 15:51 # +2
bazhenovc 01.11.2012 16:35 # 0
wvxvw 01.11.2012 16:39 # 0
bazhenovc 01.11.2012 17:01 # 0
TarasB 01.11.2012 17:12 # +7
Не, я тоже нихуя не понял, что wvxvw имел в виду.
wvxvw 01.11.2012 17:34 # −1
Like ordinary unions, tagged unions can save storage by overlapping storage areas for each type, since only one is in use at a time.
TarasB 01.11.2012 20:34 # +2
Полиморфные объекты тоже это умеют.
А вот структуры, которые могут одновременно хранить и Derived1, и Derived2, жрут лишнюю память, и про "использовать память под полезные нужны" имелось в виду именно в сравнении с простыми структурами.
И да, если разные ветки имеют ооочен разные размер, то полиморфные объекты могут дать выигрыш по памяти, потому что они всегда жрут сколько надо, и тегоюнион жрёт память в соответствии с размером самой длинной ветки.
LispGovno 01.11.2012 17:18 # +2
1)Убирания долгой аллокации объекта по new из кучи.
2)Добавление кешфрендли объекту, тк нет отделения большим диапазоном адресов таблицы виртуальных методов от RTTI от самого объекта и от указателя на него.
3)Частичное убирание stall в конвейере команд процессора.
Пункт 1) также можно реализовать и через placement new в объекте подобном TUB (там внутри подобный унион или массив с размером максимального объекта, который можно разместить в TUB).
Может ещё кто-то знает назвначение подобной техники. Хотя конечно это бы оформить в библиотеку с кодогенерицией, а то сейчас выглядит паршиво как говнокод.
defecate-plusplus 01.11.2012 17:54 # +1
http://www.boost.org/doc/libs/1_51_0/doc/html/variant.html
LispGovno 01.11.2012 18:35 # −1
Инстанцирование шаблона класса - создание кода класса на основе шаблона (кодогенерация).
>boost.org/doc/libs/1_51_0/doc/html/variant
больше 12 байт объекты не размещаются в boost::variant. Он выделяет объекты в куче с соответствующими последствиями.
LispGovno 01.11.2012 18:42 # −1
template<class T>
inline void call(const T& f)
LispGovno 01.11.2012 18:43 # −1
defecate-plusplus 01.11.2012 21:52 # 0
однако мне тоже интересно откуда дровишки
1) про 12 байт
2) O(log n) vs O(n) если там вообще compile-time
ну а про сложность (которая также compile-time) тоже интересно
ну разве что компилятору сложно - ну а кому в наше время легко
может я чего упустил?
bormand 01.11.2012 22:00 # +1
Пора уже показывать над минусами тех, кто их поставил.
Сложность, если конечно не ошибаюсь, там действительно O(1) т.к. get() это проверка на соответствие запрашиваемого и реального типа да тупой каст.
defecate-plusplus 01.11.2012 22:03 # 0
в variant там все решается через перегрузку
типа такой:
bormand 01.11.2012 22:13 # 0
> в variant там все решается через перегрузку
типа такой:
The get function allows run-time checked, type-safe retrieval of the content of the given variant.
Все-таки рантайм проверка там есть.
defecate-plusplus 01.11.2012 22:22 # 0
ран-тайм - имеется в виду, что при несоответствии типов будет в рантайме кинуто bad_get, а не ошибка компиляции студия в Release сгенерила вот такой код: если это O(1) выбор в компайл-тайм, то я не знаю
bormand 01.11.2012 22:28 # 0
А это походу потому, что она видит что туда засунули. Попробуйте сделать тип зависящим от чего-нибудь типа argc - если 1 то foo() если 2 то bar(). Тогда, мне кажется, она вставит проверочку с вбросом bad_get.
http://liveworkspace.org/code/468230da8ae63feab65e99bede607fbd
LispGovno 01.11.2012 22:29 # 0
Лол. У тебя же n классов в иерархии. И если ты не знаешь, какой там лежит класс в boost::variant, то у тебя n * O(1). То есть О(n).
А так я думаю очевидно, что boost::get работает за О(1).
defecate-plusplus 01.11.2012 22:38 # 0
> Тогда, мне кажется, она вставит проверочку с вбросом bad_get.
да, одну проверочку вставит, а не O(n)
@LispGovno
> И если ты не знаешь, какой там лежит класс в boost::variant, то у тебя n * O(1) утверждается, что для такого variant должно произойти 8 проверок, прежде чем мы убедимся, что тип == baz
однако:
LispGovno 01.11.2012 23:08 # 0
утверждается, что для такого variant должно произойти 8 проверок, прежде чем мы убедимся, что тип == baz
Ну конечно. Ты сначала проверяешь через boost::get на int, потом на long, потом на ... и так до конца. Если ты что-то знаешь, то ты естественно часть проверок через boost::get опускаешь. В данном коде выше ты знал, что у тебя там baz лежит и сделал лишь одну проверку:
boost::get<baz>(v).print();
Другие варианте тебе были просто не нужны.
Фактически про О(n) я говорил, когда ты через boost::get моделируешь полиморфизм.
defecate-plusplus 01.11.2012 23:36 # 0
ладно
закрывая этот вопрос окончательно - ты имеешь в виду visitor для объекта variant - будем эмулировать полиморфизм
http://liveworkspace.org/code/374baab33e4f8e969fec16f3119f5345
вот этот код, как ни крути, у меня в релизе без оверхеда сразу выбирает нужный operator ()
LispGovno 01.11.2012 23:41 # 0
Наконецто. Я о нем уже несколько раз в этом треде писал и о его проблемах.
defecate-plusplus 01.11.2012 23:43 # +1
всё что ты написал - ты не разобрался как он работает
LispGovno 01.11.2012 23:50 # 0
Я сказал, что стратегию под оптимизацию другую выбрать нельзя визиторе, если профлаер указал на несостоятельность текущей стратегии поиска реального типа класса.
И ещё проблема, что не известно какая стратегия там вообще по умолчанию используется, если в исходники не заглядывать, тк в документации это не указано.
LispGovno 01.11.2012 23:42 # 0
LispGovno 01.11.2012 23:45 # 0
С чего ты взял что без оверхеда? Что там? O(1), O(log n) или всеже O(n)?
На счет O(1) я уверен, что оно там не используется, если смотреть на размер boost::variant. Значит там или O(log n) или О(n)
defecate-plusplus 01.11.2012 23:50 # +2
запустил в Release, но с /Zi /DEBUG
поставил бряку на apply_visitor и по асм коду (alt+8) прошелся f11
на любых исходных данных компилятор переходит в нужный обработчик за одну проверку
вникать в шаблонодебри сорцов варианта в первом часу ночи уже не хочется
edit
> за одну проверку
да там и нет в асм коде вообще ни таблицы сравнений, ни последовательности сравнений по списку
сразу куда нужно переходит - оверлоад, мать его
defecate-plusplus 02.11.2012 00:02 # +1
а, может быть, виноват хороший оптимизатор
так что надо будет еще потестировать завтра
LispGovno 02.11.2012 00:03 # +1
сразу куда нужно переходит - оверлоад, мать его
Может это компилятор соптимизировал, раз знает, какой реально тип там лежит во время компиляции? К параметрам командной строки привезал тип, как ты делал выше?
Если исключил воможность оптимизации со стороны компилятора, то это довольно странно. Почему за О(1)?..
defecate-plusplus 02.11.2012 00:16 # +1
внутри variant хранится which - число - навроде "индекс" типа из шаблонного списка
apply_visitor в итоге строит конструкцию switch для вызова хендлера по конкретному индексу среди всех типов внутри variant - так что уж тут как компилятор отработает этот свич, такой O и будет (да, O(n) в худшем случае, то там return на каждую ветвь, так что оптимизатору есть где поработать)
ну а на /O2 микрософтовский компилер всю эту байду сокращает до банальщины - заранее кладет в стек нужные адреса при инициализации для каждого возможного argc (выбор из например 4 типов-то для 2 переменных не очень велик) и затем по конкретному argc где надо сразу достает нужные смещения и делает вид, что O(1) и так и задумано
LispGovno 02.11.2012 01:03 # 0
буст:вариант
молодцы. Хоть стратегию поменять нельзя, но зато есть где развернутся оптимизатору. Уважуха.
defecate-plusplus 01.11.2012 22:06 # 0
http://liveworkspace.org/code/195391c0a3ddf544bf621ce16b0a316b
cstdio включен чтобы было легче разбираться в асме
LispGovno 01.11.2012 23:10 # 0
Или этого больше нет в новых версиях, или это было в некоторых реализациях, либо так было реализовано в boost::any. Я склоняюсь к последнему.
LispGovno 01.11.2012 22:47 # 0
> ну а про сложность (которая также compile-time) тоже интересно
Уж и не знаю что там со сложностью компилетайм, а вот сложность поиска находящегося в варианте класса из иерархии О(n), если он не известен через boost::get. В своей же реализации (например как в говнокоде выше) можно организовать поиск нужного класса в иерархии за О(log n), O(1) и О(n) с переборам классов в соответствии с вероятностью их появления. Через boost::get конечно тоже можно перебрать за О(n) с учетом вероятности, но это не удобно делать каждый раз, ибо перебор приходится харкодить везде по коду и если профлаер скажет поменять данный перебор, то придется менять вообще везде вручную возможно с ошибками. А так выбрал реализацию call нужную (в том числе и сгенерировал её код на крестах через метапрограммирование) и если профлайер скажет, мол медленно - ты сможешь поменять стратегию сгенерированного кода для call из говнокода выше.
А boost::apply_visitor не известно как работает и менять стратегию в угоду оптимизации не позволяет.
defecate-plusplus 01.11.2012 23:05 # 0
поясни мысль
когда variant инициализируется, он уже в курсе какой тип он хранит
поэтому в чем проблема для него сделать одну единственную проверку "если тип тот, то на тебе его, если тип не тот - лови эксепшен"
где тут O(n)?
разницу между any и variant улавливаешь?
из мануала:
> Boost.Variant provides compile-time checked visitation of its content.
какая еще стратегия? O(1)
LispGovno 01.11.2012 23:23 # 0
А вот с твоим boost::get каши не сваришь. Он потребует проверку O(n) для перебора всей иерархии классов в любом случае, если ты моделируешь полиморфизм и не знаешь какой реальный тип из списка дозволеных, лежит в boost::variant.
TarasB 01.11.2012 17:13 # +2
LispGovno 01.11.2012 17:22 # −1
3.14159265 01.11.2012 19:48 # +2
Intel Core Architecture?
LispGovno 01.11.2012 20:10 # −2
LispGovno 01.11.2012 20:11 # −2
3.14159265 01.11.2012 20:22 # +2
Давно по незнанию я также писал такой код, несмотря на его определенную громоздкость, считая что где-где, а в JVM там-то всё оптимизировано.
Ну и по ходу дела старался избегать instanceofов и прочих ifов с кейсами даже там где они были необходимы (правда кейсы - не очень, обычно их можно заменить мапом или обычным массивом).
Казалось бы взять адрес класса, найти метод в таблице, сделать call. Что может быть проще?
Наверное это таки быстрее чем проверять бранч на 10 условий.
Но вконец я в душе ненавидя ооп-парадигму, т.н. паттерны и громоздкость порождаемого ими кода, решил проверить "а велик ли прирост скорости от полиморфизма".
3.14159265 01.11.2012 20:29 # +5
И выглядят они примерно таким вот образом:
Как мы видим это тот же кейс, от которго мы избавились с помощью полиморфизма, но только вот он всплыл в другом месте! Его перенесли в фабрику.
Еще есть способ: поднятие объектов по рефлексии, где из мапа достается класс объекта, но это в какой-то мере хак, причем тормозной.
roman-kashitsyn 01.11.2012 23:57 # +9
Не, в правильной фабрике ещё воткнута регистрация с привязкой идентификатора типа к прототипу.
> долбоебов-оопешников с мозгом засраном под завязку паттернами
Паттерны по сути весьма забавная штука: они позволяют заменить 20 строк копипасты 40 строками разного кода
TarasB 02.11.2012 09:36 # +5
Ох ты ж сукин сын, да это то, что меня всё время мучало на интуитивном уровне и я долго пытался как-то выразить, но не мог подобрать слова. Это надо вынести в крылатые фразы говнокода.
Ща ещё виртуалами плюсану ^_^
3.14159265 02.11.2012 15:00 # +2
Я только хотел писать коммент про подсознательное, интуитивное восприятие того что Роман выразил словами.
И про то что надо это где-нибудь на видном месте выбить в граните.
3.14159265 01.11.2012 20:38 # +4
По факту выяснилось что Тарас был глубоко прав, постоянно кормя говном фанатов ооп.
Хоть большинство и считают это зеленью, но он-то прав.
vistefan 16.11.2012 16:30 # +1
LispGovno 01.11.2012 17:24 # −1
TarasB 01.11.2012 20:40 # +1
if foo.tag /= tag_bar then throw Discriminant_Error; end if;
foo.bar;
В выпуске проверку можно убрать в бутленьке, если он хорошо проверен. Проверку можно убрать и в кулхацкерских целях.
Ещё в них разрешено пихать неподы. Потому что легко определить, какой из неподов, находящихся в разных ветках по одному адресу, надо убрать - посмотреть в тэг
LispGovno 01.11.2012 23:14 # −2
TarasB 02.11.2012 09:34 # +1
Что за хуйню ты мелешь?
Там всё за O(1), что там может быть за O(n)?
TarasB 16.11.2012 16:36 # 0
Я тут узнал недавно, что typeid считается в рантайме.
Поэтому сделай, пожалуйста, нормальный свич и явный тэг.