исключения fpu

Тема в разделе "WASM.ASSEMBLER", создана пользователем cresta, 12 май 2005.

  1. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    Как известно,при обработке обычных (не связанных с fpu) исключений ставится процедура - фильтр, что очень даже экономит время на проверке диапазонов входных данных для операций, например деления : mov eax,1 div ecx - нет необходимости проверять (и терять на это время) содержимого ecx. Случится исключение деления на 0 - тогда и будем разбираться внутри обработчика, не случилось - времени не теряется.

    А можно ли как-то организовать подобный ход событий при работе с числами с плавающей запятой? Или проверка состояния fpu на предмет деления на ноль, переполнения и т.д. после каждого действия и связанные с этим потери времени - это неизбежно?
     
  2. NoName

    NoName New Member

    Публикаций:
    0
    Регистрация:
    1 авг 2004
    Сообщения:
    1.229
    Можно проверять флаги состояния, и также там есть исключения.
     
  3. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    NoName

    Я наверное несколько запутано выразился.

    Проверить флаги - это понятно, но не хотелось бы без нужды их постоянно проверять. Сделать проверку флагов только по факту случившегося исключения и тогда разбираться, что конкретно произошло. Т.е. есть ли механизм контроля, который следил бы за исключениями fpu, и который информировал бы меня только в случае, если исключение имело место быть, а если исключений не было, то он бы меня не дергал и я не отвлекался бы без необходимости на проверку.
     
  4. LocTb

    LocTb New Member

    Публикаций:
    0
    Регистрация:
    11 окт 2004
    Сообщения:
    54
    В драйвере: перехватывай #MF, устанавливай NE в CR0, отмаскируй в Control Word исключения + почитай мануалы Intel'a, будут тебе исключения.
     
  5. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    cresta

    Почитай IA-32 volume 1, chapter 8: Programming with x87 FPU.

    Там (почти) все расписано. По крайней мере "включить\отключить" генерацию нужных исключений очень легко: fclex+fldcw.



    > "очень даже экономит время на проверке диапазонов входных данных для операций, например деления: mov eax,1 div ecx"

    Так то оно так, но при этом следует учитывать что генерация и обработка исключения в ОС занимает очень много времени, т.е. это действительно - исключительная ситуация. Поэтому выбор между проверкой диапазона и исключением зависит от логики работы программы: если это исключительная ситуация (типа "капут - сливай вода"), то ес-но рулит SEH, а если это просто особая ситуация (хотя возможно и редкая) и для нее предусмотрена особая обработка, то логично делать обычную проверку. И не стоит преувеличивать "траты времени", по крайней мере для целочисленных проверок - ну что такое test ecx,ecx по сравнению c div ecx - капля в море :))
     
  6. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    leo

    Допустим такой случай: расчёт арктангенса. Предварительная проверка, которая направит код в одно из трех возможных ветвлений, занимает довольно таки немало :dntknw:


    Код (Text):
    1.     fld qword ptr [asin]
    2.     fabs
    3.     fld1
    4.     fcompp
    5.     fstsw ax
    6.     fwait
    7.     shr al,1
    8.     sahf
    9.     ja @valid
    10.     jc @error
    11.     @equ_1:
    12.         fld qword ptr [asin]
    13.         fmul qword ptr [pidiv4]
    14.         fstp qword ptr [angle]
    15.         ......
    16.         ret
    17.     @valid: ;Atn(x / Sqr(-x * x + 1))
    18.         ......
    19.         ret
    20.     @error:
    21.         ......
    22.         ret




    Как тут выгодней?
     
  7. NoName

    NoName New Member

    Публикаций:
    0
    Регистрация:
    1 авг 2004
    Сообщения:
    1.229
    Забыл сказать важную вещь. Большинство математических выражений fpu выполняет правильно. Даже 1 делить на ноль будет бесконечноть и тому подобное, так что имеет ли смысл обрабатывать деление на ноль? Другой случай если появляются нечисла, которые плодят огромное количество багов, про них было в статье эдмонда хорошо написано.
     
  8. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    cresta

    > "Как тут выгодней?"



    Пример конечно не очень удачный, т.к. FPATAN работает с лююбыми операндами и ошибок деления на 0 и переполнений не возникает. Единственная неопределенность для FPATAN, когда оба операнда = 0 (ес-но NaN'ы не в счет).



    С другой стороны, это пример того что вариант asin = 1 является не ошибкой, а просто особой ситуацией. В таких случаях нужно смотреть по тактам - сколько у нас скушает проверка по сравнению с общем временем вычисления. Все трансцендентные функции кушают сотню-другую тактов, fdiv и fsqrt около полусотни. Сравнения fcom и fcomi мы вроде как-то рассматривали в одной из тем - это что-то около десятка тактов (для P6 поменьше, для P4 около или чуть больше). Перехода боятся тоже не стоит если он предсказуем, поэтому для редкой особой ситуации должен быть прыжок "вниз", а основная ветка должна идти сразу после jcc. А в твоем примере, кстати не очень хорошо - первой идет редкая ситуация, а для общего случая приходится прыгать. Ну а обработка исключения в винде съест у тебя неcколько тысяч тактов (реальные цифры см. в теме IsDebuggerPresent). Вот и думай :))
     
  9. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    NoName

    понятие бесконечность существует, вроде логично, а что потом делать с этой бесконечностью, если она - выходной результат некой ф-ции, как с ней дальше работать и как на неё реагировать :dntknw:



    leo

    Значит не стоит?. Просто свести к минимуму затраты времени на неизбежные проверки на входе.



    ОК. Спасибо.
     
  10. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    cresta



    Значит не стоит :)

    Если хочешь, вот еще парочка аргументов.



    Говоря о "потерях времени" мы часто забываем о внеочередном \ параллельном \ спекулятивном исполнении. Вот и я упомянул о ~10 тактов латентности сравнения (т.е. готовности jcc) и забыл уточнить, что в потоке команд это в итоге даст потери не более нескольких тактов.

    Если правильно расположить ветки условных переходов с точки зрения статического предсказания (т.е. основную ветку расположить без прыжка - сразу за jcc), то с учетом спекулятивного исполнения главная ветка начнет выполняться еще до того как будут проанализированы все условия, т.к. операции в главной ветке по идее не должны зависить от цепочки fld1(или fldz)+fcomp+fstsw+ALU+jcc (если конечно по глупости не создать ложной зависимости по флагам типа inc, clc, cld и т.п.). Поэтому максимум потерь здесь это 2-3 такта на запуск операций в исполнительные порты. Можешь протестировать на wintest - с проверкой условия и без проверки - разницы скорее всего не заметишь (даже на fdiv и fsqrt, не говоря о трансцендентых тормозах). Ну а в случае выполнения условия получаем непредсказанный переход с откатом спекулятивного исполнения и автосбросом возможного исключения - а непредсказанный переход это всего 10-20 тактов и уж никак не тысяч, как в случае эксепшена.



    А что мы будем иметь если обрабатывать особую ситуацию в SEH'е ? Установить обработчик - надо ? Надо. Отключить на выходе - надо ? Обязательно. В итоге как минимум те же несколько тактов скушаем. Да и сам обработчик менее прозрачный, если не сказать более сложный получается. В твоем примере ты одним сравнением обошел две возможные ошибки - отрицательный операнд fsqrt и деление на 0. А в обработчике придется анализировать флаги исключений и\или EIP чтобы разобраться в каком месте произошла ошибка, да еще учесть разное сосотяние стека FPU для этих случаев. По моему - морока, хотя если есть желание - можно поупражняться :))



    А вывод я бы сделал такой: 1) основные исключения (инвалидная операция, деление на 0 и переполнение) лучше не маскировать, иначе в непредвиденной ситуации хлопот не оберешься - ошибки могут плодиться одна за другой и не известно когда они проявятся и в итоге трудно будет найти причину, 2) а проверочки все-таки делать явным образом - потери не велики, зато проще и понятнее.
     
  11. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    Убедил, чёрт красноречивый

    (с) не помню кто.
     
  12. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    cresta



    А если я тебе скажу, что в простейшем цикле fdiv+dec+jnz добавление проверки условия не увеличивает, а уменьшает время обработки на P4, ты поверишь или нет :)) Сам не поверил - видимо какие-то тонкие эффекты (без проверки идет какое-то проскальзывание одного такта из трех => среднее увеличение на 0.3 такта на цикл, а при добавлении проверки все исчезает). Вот и подсчитывай такты по мануалам :))
     
  13. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    Может это проскальзывание - это пенальти по поводу спешки, и пенальти больше чем дополнительная проверка как раз на величину 0,3 ?
     
  14. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    Да в цикле такие вещи вообще бесполезно смотреть, т.к. задержка на сравнение проявляется только в первом проходе, а при последующих за счет спекулятивного исполнения следующее сравнение идет во время предыдущего fdiv и задержка "съедается".



    А без цикла получается все "правильно" с поправкой на дискретность rdtsc на P4, т.е. разница либо 0 либо 4, т.е. как и предполагалась меньше 4-х.

    Кстати и эту задержку можно "скрыть" если перед сравнением выполнить полезную независимую fpu-операцию. В в твоем примере это может быть x*x, тогда запуск в порты fcomp и fstsw пройдет параллельно с fmul.



    Ну да ладно, вроде все ясно - пора завязывать с чертовским красноречием :))