LINUX.ORG.RU

GCC генерит говнокод при использованьь case X ... Z

 ,


0

1

Использовал case X ... Z: https://s26.postimg.org/o78wqrs8p/2017-09-24_12-41-54.png
Переписал на case X : case Y: case Z: https://s26.postimg.org/t76cypxvd/2017-09-24_12-49-08.png

Отчего все так плохо? Я думал что он тупо при компиляции виртуально раскроет X ... Z в case X: case Y: case Z, а он просто судя по всему там вместо switch/case if'ы мутит.

Еще конструкция:

    case C_MOVE_LT               : command_move(ant, command); break;
    case C_MOVE_T                : command_move(ant, command); break;
    case C_MOVE_RT               : command_move(ant, command); break;
    case C_MOVE_L                : command_move(ant, command); break;
    case C_MOVE_R                : command_move(ant, command); break;
    case C_MOVE_LB               : command_move(ant, command); break;
    case C_MOVE_B                : command_move(ant, command); break;
    case C_MOVE_RB               : command_move(ant, command); break;

    case C_PICK_LT               : command_pick(ant, command); break;
    case C_PICK_T                : command_pick(ant, command); break;
    case C_PICK_RT               : command_pick(ant, command); break;
    case C_PICK_L                : command_pick(ant, command); break;
    case C_PICK_R                : command_pick(ant, command); break;
    case C_PICK_LB               : command_pick(ant, command); break;
    case C_PICK_B                : command_pick(ant, command); break;
    case C_PICK_RB               : command_pick(ant, command); break;

Выполняется в два раза быстрее чем

    case C_MOVE_LT               : 
    case C_MOVE_T                : 
    case C_MOVE_RT               : 
    case C_MOVE_L                : 
    case C_MOVE_R                : 
    case C_MOVE_LB               : 
    case C_MOVE_B                : 
    case C_MOVE_RB               : command_move(ant, command); break;

    case C_PICK_LT               : 
    case C_PICK_T                : 
    case C_PICK_RT               : 
    case C_PICK_L                : 
    case C_PICK_R                : 
    case C_PICK_LB               : 
    case C_PICK_B                : 
    case C_PICK_RB               : command_pick(ant, command); break;

★★★★★

Последнее исправление: PPP328 (всего исправлений: 1)

По снимкам не пойму что ты там сфоткал. Покажи ассемблерный код.

u0atgKIRznY5
()
Ответ на: комментарий от PPP328

На godbolt выглядит вот так
-O0

Мань, выйди вон из профессии, позязя :3

anonymous
()

«снимки» не смотрел (тут это непринято), НО

в первом случае компилер может построить двоичный поиск, а во втором нет.

MKuznetsov ★★★★★
()
Ответ на: комментарий от PPP328

Пример на godbolt показывает что второй вариант как раз быстрее. Вообще для таких вопросов неплохо бы приводить оригинальный код, а не делать неэквивалентных примеров.

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

Я рискну предположить что в оригинальном примере либо ant, либо command было селектором для switch. Тогда в первом случае компилятор мог для каждого случая сделать inline с частью вычислений в compile-time. А во втором случае, т.к. значение аргумента не известно заранее, пришлось делать вызов оригинальной функции, что и послужило причиной просадки.

alexanius ★★
()
Ответ на: комментарий от andreyu

Вы серьезно рассуждаете о качестве сгенеренного кода с -O0?

А что -O0 - не режим или на нём оптимизаций нет? :) Тут скорее вопрос почему код, который вроде как должен работать быстрее работает медленнее. Вопрос действительно интересный.

alexanius ★★
()
Ответ на: комментарий от alexanius

А что -O0 - не режим или на нём оптимизаций нет? :)

И много проектов в продакшене используют -O0?

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

Так не подтверждения, что код работает медленнее.

andreyu ★★★★★
()
Ответ на: комментарий от alexanius

А что -O0 - не режим или на нём оптимизаций нет?

Ты не поверишь, но на нём действительно вообще нет оптимизаций. Более того, все выражения компилятор рассматривает независимо. Т.е. при -O0 код

case A: func(); break;
case B: func(); break;

будет быстрее
case A:
case B: func(); break;

потому что во втором случае всегда _будет_ проведено сравнение с B, даже если было успешное сравнение с A

Пруф из мана gcc:


Statements are independent: if you stop the program with a breakpoint between statements, you can then assign a new value to any variable or change the program counter to any other statement in the function and get exactly the results you expect from the source code.

redgremlin ★★★★★
()
Ответ на: комментарий от redgremlin

Ты не поверишь, но на нём действительно вообще нет оптимизаций.

Это вопрос не веры а фактов :P

$ gcc t.c -std=c11 -fdump-passes -O0 2>&1 | grep ON | wc -l
119

Там в основном всякая технологическая лажа, но суть в том что сейчас даже на O0 обязан отрабатывать минимальный inline.

потому что во втором случае всегда _будет_ проведено сравнение с B

Конечно же нет. В обоих случаях будет построен lookup table (см. выше ссылку на godbolt). Просто в первом случае есть лишний mov, кода получается больше и есть лишний переход, т.е. в целом оно должно быть медленнее. Но на тривиальных примерах из треда погрешность будет больше чем изменение скорости работы, так что замерить не получится

Я пока что всё ещё ставлю на то что в оригинальном случае повлиял inline.

alexanius ★★
()

Опции компиляции мы за тебя угадывать будем?

LongLiveUbuntu ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.