Лишние переходы - зачем они?

Тема в разделе "WASM.A&O", создана пользователем Y_Mur, 23 ноя 2006.

  1. Y_Mur

    Y_Mur Active Member

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    2.494
    Собственно технические детали темы неоднократно обсуждались на форуме, но хочу понять не "как исправить", а "зачем вообще задумано"?
    С точки зрения оптимизации по скорости следует уменьшать количество переходов, а уж двойных (переход на переход) и вовсе избегать, но и Масм и Дельфя упорно вызывают API через call на jmp. Как лечить Масм знаю, но хочу понять что это - просто глупость? или есть хоть какие нибудь плюсы?
    Попадалось мнение, что это типа экономия одного байта в инструкции call, за счёт добавления ещё шести :) - сомнительная надо сказать экономия :))

    Еще Fasm, Дельфя, и многие асм программеры обожают пихать данные в сегмент кода, а потом через них джимпать или калать (если нужно совместить переход с занесением в стек ссылки на данные).
    Если для Fasm это хотя бы понятно - ну не умеет он раскидывать смешанные в исходнике данные и код по соответствующим сегментам в готовой проге, то зачем так поступают программеры, имеющие возможность выбора? - что тоже есть какие нибудь плюсы?
     
  2. cpp_and_wasm

    cpp_and_wasm Владимир

    Публикаций:
    0
    Регистрация:
    27 июл 2006
    Сообщения:
    128
    Програмеры не забывают о собратах-крэкерах! :)
     
  3. Y_Mur

    Y_Mur Active Member

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    2.494
    cpp_and_wasm
    Эта версия мне тоже кажется правдоподобной :)))
     
  4. Pavia

    Pavia Well-Known Member

    Публикаций:
    0
    Регистрация:
    17 июн 2003
    Сообщения:
    2.409
    Адрес:
    Fryazino
    Y_Mur
    Мои домыслы.
    Для переносимости кода. Такой код можно перенести на другой адресс в памяти и он будет работать. Это преднозначено в первую очередь для библеотек, если у них базовый адресса совподут то их можно будет разместить по разным адресам.
    Про фасм не скажу, а вот про дельфи. Это реализация ООП для вызова методов класса. Также такая вещь как процедурная переменная. Удобно если нужно делать вызов различных процедур по назночению но одинаковых по типу вызова.
     
  5. IceStudent

    IceStudent Active Member

    Публикаций:
    0
    Регистрация:
    2 окт 2003
    Сообщения:
    4.300
    Адрес:
    Ukraine
    Pavia
    Сомнительная переносимость - несколько джампов и куча "непереносимых" данных. Вообще, говорили, что это для экономии размера кода.
     
  6. Pavia

    Pavia Well-Known Member

    Публикаций:
    0
    Регистрация:
    17 июн 2003
    Сообщения:
    2.409
    Адрес:
    Fryazino
    IceStudent
    Тогда уж не для экономии кода. А для уменьшения exe файла. В таком случаи таблица экспорта будет маленькой по одному адрессу на jmp в замен по адрессу на вызов через call.
     
  7. Quantum

    Quantum Паладин дзена

    Публикаций:
    0
    Регистрация:
    6 янв 2003
    Сообщения:
    3.143
    Адрес:
    Ukraine
    Pavia
    Кому-то нужно повторить структуру PE файла...

    Y_Mur
    6 байт добаляются всего 1 раз, а 1 байт экономится при каждом вызове. Следовательно, если вызовов много, получаем небольшую экономию. Тем не менее, thunk'и придумали не для этого. К примеру, если поубирать thunk'и в либах импорта, ms link.exe перестаёт работать в режиме /OPT:REF. Polink работает нормально.
     
  8. Y_Mur

    Y_Mur Active Member

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    2.494
    Опять не понятно:
    Переносимость кода
    И jmpы сваленные в кучу в конце программы, и непосредственный вызов API через call используют косвенные переходы, т.е. реальные адреса API всё равно хранятся в таблице в сегменте данных. А как раз call насколько я помню в отличие от jmp не может быть относительным, и потому если в проге все косвенные call заменены на прямые, то при переносе кода их нужно корректировать так, что где выигрыш? Наоборот тут придётся корректировать и адреса call и ссылки на данные в jmp. Или я чего-то недопонимаю?

    Экономия размеров кода \ exe файла
    Дабы сэкономить на этом нужно соблюсти условие: "вызовов API более чем в шесть раз больше чем ссылок на внешние функции". Сравните на маленьких файлах (где каждая функция вызывается по одному разу) и убедитесь, что размер exe с call на jmp больше, а не меньше :)
    И даже если соблюсти это условие, то получить выигрыш более 3% крайне сложно, неужели из-за этого терять скорость? Имхо на выравнивании exe секций портери больше :)

    Pavia
    Ничего не понял про ООП и процедурные переменные...
    Какая им разница куда ведёт переданная через стек ссылка к примеру на выводимую на экран текстовую строку - в сегмент кода или в сегмент данных? А проигрыш очевиден - лишний переход...
     
  9. Y_Mur

    Y_Mur Active Member

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    2.494
    Quantum
    Ещё раз заглянул в масм справку и не понял... /OPT:REF это зачем? - ни разу эту фичу не использовал.
     
  10. Quantum

    Quantum Паладин дзена

    Публикаций:
    0
    Регистрация:
    6 янв 2003
    Сообщения:
    3.143
    Адрес:
    Ukraine
    Y_Mur
    Я же написал, что выйгрыш в размере - не основная фишка данного метода.

    Тут пишут, что thunk'и сокращают кол-во используемых релоков, но это не так. Кстати, в вики тоже самое написано. Странно... Вообще, если учесть, что thunk'и используют недокументированный формат comdat'ов в либах импорта, возможно, у мс когда-то имелись секретные поводы использовать эти самые thunk'и.

    Данная фича включена по умолчанию. Зачем - см. msdn.
     
  11. Proteus

    Proteus Member

    Публикаций:
    0
    Регистрация:
    19 июн 2004
    Сообщения:
    344
    Адрес:
    Russia
    1) Безусловный переход (если память не изменяет) конвеер не срывает.
    2) Сами по себе API функции, как правило не очень быстро работают. И вызывать их слишком часто толку нет.

    И какой спрашивается смысл тут что-то экономить? Ну сделаешь что-то на один такт быстрее. Этого никто не заметит.
    Да и потом Дельфя и Масм задуманы совсем не для генерации оптимального кода, а для облегчения работы.
     
  12. Y_Mur

    Y_Mur Active Member

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    2.494
    На счёт "вызовов API более чем в шесть раз больше чем ссылок на внешние функции", эт я погорячился - нужно добавить ещё 4 байта в таблицу настраиваемых адресов, итого в 10 раз :)), т.е. получить выигрыш в размере трудно, а вот проигрыш - запросто :)))

    Quantum
    Ну положим в масм по умолчанию /OPT:NOREF, но всё равно разберусь - заинтересовал :)

    Proteus
    1. Как писал Leo, ещё как срывает, и ещё где то он писал, что переход на переход это ещё хуже :)
    Вопрос не про копеешную экономию, а про стиль программирования - зачем делать откровенно антииоптимизирующие телодвижения, если ничего не мешает их не делать? Большая глупость складывается из множества маленьких :)))

    Итого: попутно выяснилось удвоение затрат на настройку перемещаемых адресов, а из плюсов пока только версия cpp_and_wasm - проще API перехватывать :)))
     
  13. Miller Rabin

    Miller Rabin New Member

    Публикаций:
    0
    Регистрация:
    4 янв 2006
    Сообщения:
    185
    Только не пинайте сильно :)
    Я не такой великий спец по оптимизации как, например, leo или Black Mirror. Поэтому я могу ошибаться

    Но как и вы я неоднократно задумывался над вопросом почему call на jmp вместо call [...]

    Мне кажется это связано с тем, что ранние процессоры, не помню точно 286 или 386 не поддерживали косвенные переходы. И эта фича используется для, блин, совместимости.

    К тому же помниться мне в Delphi 7 до сих пор есть опция коррекции при делении.
    Это когда Intel выпустила Pentium который делил неправильно в некоторых случаях.
    К счастью она отключается :)

    Видимо, все это связано с политикой больших корпораций Microsoft и Borland.
    Код должен работать всегда и везде
     
  14. Quantum

    Quantum Паладин дзена

    Публикаций:
    0
    Регистрация:
    6 янв 2003
    Сообщения:
    3.143
    Адрес:
    Ukraine
    Y_Mur
    По умолчанию именно REF. При включенном REF линкер пытается отследить модули, к которым есть символьные обращения через extern, но фактически не используются. Такие модули линкером исключаются в плане оптимизации образа по размеру. Это не касается comdat'ов, которые оптимизируются в любом случае (и в REF и в NOREF). Более подробно написано в msdn. Тем не менее, на практике наблюдаются некоторые противоречия в логике линкера, если поубирать из импорта thunk'и, т.е. те тупые вызовы переходников. Можете запустить линкер в режиме /verbose для наглядности.

    Вижуал давно перестал их делать.

    Miller Rabin
    Ы???
     
  15. IceStudent

    IceStudent Active Member

    Публикаций:
    0
    Регистрация:
    2 окт 2003
    Сообщения:
    4.300
    Адрес:
    Ukraine
    Quantum
    А что такое comdat?
     
  16. Quantum

    Quantum Паладин дзена

    Публикаций:
    0
    Регистрация:
    6 янв 2003
    Сообщения:
    3.143
    Адрес:
    Ukraine
    IceStudent
    Это запись в обьектнике, которая попадает в образ только при явном обращении. Весь импорт строится из comdat'ов. Получается как будто обьектник внутри другого обьектника. comdat'ы встречаются не только в импорте, но и в обычных обьектниках.
     
  17. Y_Mur

    Y_Mur Active Member

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    2.494
    После медитации с дебуггером дошло - masm генерит относительные call (эт в #8 у меня просто остались воспоминания о реалмоде, а в протект относительные call есть) - значит в таблицу перемещений попадают только jmpы, которых меньше и за котрыми не нужно гоняться по всему файлу - соответственно кеширование рулит, и время подготовки проги к запуску немного уменьшается.
    Значит некоторая польза для больших прог интесивно использующих API всё таки есть :)

    Quantum
    Попробовал /OPT:REF, /OPT:NOREF линкер из Masm32 v9 линкует без проблем в обоих вариантаx :)
     
  18. Quantum

    Quantum Паладин дзена

    Публикаций:
    0
    Регистрация:
    6 янв 2003
    Сообщения:
    3.143
    Адрес:
    Ukraine
    Y_Mur
    А разве не должен?
     
  19. Y_Mur

    Y_Mur Active Member

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    2.494
    Quantum
    All
    И теперь остался за кадром только пункт 2 вопроса:
    Все согласны, что:
    Код (Text):
    1. push MB_OK
    2. call a1
    3.   db 'Ура заработало ! ', 0
    4. a1:
    5. call a2
    6.   db 'Всем привет', 0
    7. a2:
    8. push 0
    9. call MessageBoxA
    вместо:
    Код (Text):
    1. push MB_OK
    2. push ссылка на строку 'Ура заработало ! ', 0
    3. push ссылка на строку 'Всем привет', 0
    4. push 0
    5. call MessageBoxA
    не есть хорошо?
     
  20. Quantum

    Quantum Паладин дзена

    Публикаций:
    0
    Регистрация:
    6 янв 2003
    Сообщения:
    3.143
    Адрес:
    Ukraine
    Y_Mur
    Ты уже убрал thunk'и из либ импорта?

    Конечно, это нарушает цепочки call/ret в кеше.