[C++] Насколько большой может быть потеря в скорости при использовании

Переформулирую вопрос: ее умешьшить можно каким-нибудь способом? Если да, то каким?
Код покажи.
и что у тебя за свич-конструкции, и какие вирт.функции? из твоих слов это (мне) непонятно
{
public:
...
int type;
};
void draw(const X& x)
{
switch(x.type)
{
case 1: ...; break;
case 2: ...; break;
case N: ...; break;
default: ...;
}
}
Заменяется на
class X
{
public:
virtual void draw = 0;
};
class X1 : public X
{
public:
virtual void draw { ... }
};
class XN : public X
{
public:
virtual void draw { ... }
};
и что тебя удивляет? одно дело, сделать сравнение, джамп и call по адресу, другое дело - залезть в таблицу вирт функций, сделать поиск по ней, и уже оттуда call
какой еще нафиг поиск по таблице виртуальных функций?
Код рисования немаленький там, а скорость раза в два наверное упала.
прастой. Познее, знаете ли, связывание
Там поиск не производится, смещение уже заранее известно.
конечно, если скорость падает значительно на фоне долгих вычислений, мое объяснение явно неудачно. А так, на холостой работе, по идее, вирт.функции работают медленнее
и какое отношение позднее связывание имеет к виртуальным функциям?
навреное, объекты в памяти создаваться будут дольше во втором случае
ну блин
скока же у тебя классов? в смылсе чему N равно?
думается мне что дажа миллион классов будут создаваться меньше секунды
Не важно сколько классов, draw в цикле вызывается по всем объектам этих классов (которые уже созданы).
хм, оказывается "позднее связывание" - по разным источникам обозначает два совсем разных связывания.
Ну, от прыжка ты не избавишься, так что может от виртуальных функций придется отказаться.
не тупи
Чего-то не хочется, да и странно вообще, что вызов функции так резко программу замедляет.
1) Как я понимаю вызов виртуального метода всегда сбрасывает кэш.
2) Возможность инлайниться.
Все.
А так про switch vs virtual call просто -- чем больше свитчей, тем эта конструкция медленнее, чем виртуальный вызов. Еще по идее, если методы простые, и на кэш нагрузка небольшая, то виртуальная таблица будет сидеть в кэше, в этом случае и несколько свитчей будет медленнее. Если же метод сложный, то тогда вообще не так важно как быстро делается вызов.
Потому что у нас там прыжок по значению из памяти. Как ты предлагаешь предсказать переход по такому прыжку?
ИМХО в общем случае и для свитчей предсказание ветвления не очень работает.

короче все равно, в конце концов switch в такой ситуации это просто тупо по многим другим причинам
Ну если мы прыгаем по адресу который не был предсказан, сбрасываются всякие кешиПри чем тут кеши?


Открываю страшную тайну: в 80х86 ассемблере есть инструкция jmp ax (есть ещё и jmp al, конечно, но мы не о ней=) так вот, если у тебя кейзы в свитче идут хорошо подряд (0..4096, например а компилятор грамотный, то прыжок займёт те же два такта.
Однако возможно получить линейное замедление производительности, конечно. Ещё можно забыть поставить break; (КСТАТИ! или даже запретить компайлеру реарранжить кейзы (есть такая опцыя).
Они всё равно сбрасываются...
Кэшы ынструкцый?адно не хочу я с вами спорить, никто нихрена не знает, все только обобщают свои бесценные знания архитекутуры 8086 на всё подряд. Кароче в сад.

В 8086 был кэш инструкций?
в 80х86 ассемблере есть инструкция jmp ax, ... то прыжок займёт те же два такта.какие еще в жопу 2 такта?


Опять же возвращаемся к
Кэшы ынструкцый?Куда и когда
Они всё равно сбрасываются...В этом заявлении тоже надо искать смысл, или все таки его нет?

Всем спасибо, проблема похоже в другом.
вообще, вопрос был почему виртуальные медленнее
а обсуждать стали почему свичи медленнее
прикольно

Открываю страшную тайну: в 80х86 ассемблере есть инструкция jmp ax (есть ещё и jmp al, конечно, но мы не о ней=) так вот, если у тебя кейзы в свитче идут хорошо подряд (0..4096, например а компилятор грамотный, то прыжок займёт те же два такта.Про это подробней. Как код будет выглядеть? Ты сам видел или это в теории?
int xxxxx(int n) {
switch(n) {
case 0: return 1;
case 1: return 0;
case 2: return 3;
case 3: return 2;
default: return 4;
}
}
gcc -O3 как всегда облажался:
.globl _Z5xxxxxi
.type _Z5xxxxxi, @function
_Z5xxxxxi:
.LFB1744:
pushl %ebp
.LCFI14:
xorl %eax, %eax
movl %esp, %ebp
.LCFI15:
movl 8(%ebp %edx
cmpl $1, %edx
je .L274
jle .L284
cmpl $2, %edx
movl $3, %eax
je .L274
movl $2, %eax
cmpl $3, %edx
.L283:
je .L274
movl $4, %eax
.L274:
popl %ebp
ret
.p2align 47
.L284:
movl $1, %eax
testl %edx, %edx
jmp .L283
А студия как всегда рулит:
int __declspec(noinline) xxxxx(int n) {
switch(n) {
00401870 mov eax,dword ptr [esp+4]
00401874 cmp eax,3
00401877 ja $L66450+6 (401895h)
00401879 jmp dword ptr [eax*4+40189Ch]
case 0: return 1;
00401880 mov eax,1
}
}
00401885 ret
case 1: return 0;
00401886 xor eax,eax
}
}
00401888 ret
case 2: return 3;
00401889 mov eax,3
}
}
0040188E ret
case 3: return 2;
0040188F mov eax,2
}
}
00401894 ret
default: return 4;
00401895 mov eax,4
}
}
0040189A ret
Да здраствует open source

PS. Прыжок НЕ занимает 2 такта. К современному процессору вообще такое понятие не применимо "инструкция X занимает Y тактов"... Вон у gcc например зацените, ему показалось выгоднее пихать в eax все подряд и в нужный момент вернуться, чем пихнуть в него то, что нужно и вернуться.



А если код в кейзах неравномерный по длине, то как будет: выравнивание по самому длинному, джампы-редиректы или забъет и скомпилирует нормально?
Ну то что gcc делает elsif elsif это в любом случае ненормально. Бинарный поиск я думаю сделает, щас попробуем...

int __declspec(noinline) xxxxx(int n) {
switch(n) {
00401870 mov eax,dword ptr [esp+4]
00401874 cmp eax,3E8h
00401879 jg xxxxx+32h (4018A2h)
0040187B je xxxxx+2Ch (40189Ch)
0040187D dec eax
0040187E je xxxxx+26h (401896h)
00401880 sub eax,9
00401883 je xxxxx+20h (401890h)
00401885 sub eax,5Ah
00401888 jne xxxxx+47h (4018B7h)
case 100: return 3;
0040188A mov eax,3
}
}
0040188F ret
case 10: return 2;
00401890 mov eax,2
}
}
00401895 ret
case 1: return 1;
00401896 mov eax,1
}
}
0040189B ret
case 1000: return 4;
0040189C mov eax,4
}
}
004018A1 ret
int __declspec(noinline) xxxxx(int n) {
switch(n) {
004018A2 cmp eax,2710h
004018A7 je xxxxx+59h (4018C9h)
004018A9 cmp eax,186A0h
004018AE je xxxxx+53h (4018C3h)
004018B0 cmp eax,0F4240h
004018B5 je xxxxx+4Dh (4018BDh)
default: return 8;
004018B7 mov eax,8
}
}
004018BC ret
case 1000000: return 7;
004018BD mov eax,7
}
}
004018C2 ret
case 100000: return 6;
004018C3 mov eax,6
}
}
004018C8 ret
case 10000: return 5;
004018C9 mov eax,5
}
}
004018CE ret
Ну да, по идее это и есть бинарный поиск.
А было по началу вот так:
int __declspec(noinline) xxxxx(int n) {
switch(n) {
case 1: return 1;
case 10: return 2;
case 100: return 3;
case 1000: return 4;
case 10000: return 5;
case 100000: return 6;
case 1000000: return 7;
default: return 8;
}
}
я имел в виду, что длина кода в кейзах будет разной
Компилеры, как всегда, тормозят, человек, как всегда, рулит:
int xxxxx(int n) {
switch(n) {
case 0: return 1;
case 1: return 0;
case 2: return 3;
case 3: return 2;
default: return 4;
}
}
movl 4(%ebp %eax
xorl $1, %eax
movl $4, %ebx
cmpl $3, %eax
cmoval %ebx, %eax
ret

Это третий вариант

gcc таки побыстрее работает, да и в любое время
gcc таки побыстрее работает, да и в любое времяКто б сомневался

Только за штучную и качественную ручную работу лучше платят

Как научат компилеры четыре кейза в одну инструкцию xorl $1, %eax превращать, на пенсию пойду

А что такое 4(%ebp $2 ? Это вообще какой язык?
А что такое 4(%ebp $2 ? Это вообще какой язык?Это вообще-то ассемблер, AT&T синтаксис.
$2 -- константа, равная 2.
%ebp -- регистр EBP (в данном случае содержит адрес в стеке, по которому начинается стековый кадр функции xxxxx (int i)).
4(%ebp) -- ячейка памяти со смещением 4 от значения %ebp, в данном случае содержит значение переменной i.
А суффикс l (long) у инструкций работы с регистрами и памятью, означает работу с 4-х байтовыми величинами.
Понял. Просто ни разу не видел такого синтаксиса.




gcc -O3 как всегда облажался:
А студия как всегда рулит:с чего это ты решил, что GCC облажался?
производительность сравнил?
собственными глазами видел как GCC для бОльшего количества вариантов в switch точно так же прыгает по адресу из таблицы, может просто в этом случае (только 4х вариантов) не целесообразно так делать или просто версия GCC старая.
Оставить комментарий
Zoulla
виртуальных функций вместо switch-конструкции?А то прогу одну так подправил, притормаживать стала