Про мнимые и реальные оптимизации в 10 раз, целительный SSE, и все такое

    По мотивам одного вчерашнего поста про оптимизацию условных переходов при расчете x=sign(a,b)*min(abs(a), abs(b)) якобы в 10 раз. Краткая сводка:

    • оптимизация налицо, но размер мнимый: не в 10 раз, а 2.5 раза;
    • бенчмарки надо делать правильно: не надо мерить CPU stalls, RAM bandwidth итп вместо исследуемой функции;
    • бенчмарки надо делать правильно: иначе могут дико дрожать;
    • выставлять только приоритет прикольно, но на коротких бенчмарках зря: +0.5% скорости, -15% дрожания;
    • нужно мерить исследуемую функцию и только ее, только так получаешь корректные данные;
    • нужно греть проц, нужно считать минимум из N прогонов/секунд, только так побеждаешь дрожание;
    • нужно пользовать SSE, с ним получилось 8.6 раз, причем код… читается.

    В общем, опять пачка классических методологических ошибок при бенчмарке. Кому интересно, как такие ошибки НЕ делать, подробности, детальный разбор полетов, оптимизация в еще несколько раз и, главное, исходники под катом.

    Прочитал вчера исходный пост, очень удивился ускорению в 10 раз за счет ликвидации переходов, даром, что на синтетике. Это слишком много, переходы дорогие, но не настолько. Попробовал повторить результаты, посмотрел внимательнее, и натурально: опять детсадовские ошибки в методике бенчмарка! Что же, пора их опять разобрать, пример хороший.

    Ошибка #1 заключается в том, что приведенный исходный код тупо не мерит вообще ничего вменяемого. Сумма результатов llr() считается, и это хорошо. Но компилятор видит, что она не используется никак, и поэтому имеет полное право соптимизировать. У меня как раз соптимизировал. Вдобавок исходно опубликованные (теперь разобрались и исправили) варианты оптимизаций считали вообще не тот результат, и это было незаметно. Ох…

    Мораль #1: пацаны, печатайте результаты, ВСЕГДА. И ошибки сразу поймаешь, и компилятор «ненужный» цикл не выкинет. Еще помогает объявить результат как volatile, но печатать его все равно надо.

    Ошибка #2 заключается в том, что автор мерит цикл rand()+llr(), затем мерит цикл rand()+llr2(), затем рукой и на глазок вычитает время исполнения. Это плохая идея по двум причинам. rand очень тормозной, бенчмарк получается неоправданно долгий. ;) Это раз. В эксперименте измеряется производительность фарша из двух функций, а этот фарш заведомо ведет себя НЕ так, как только нужная функция. Это два.

    В общем случае подход «давайте померим фарш из функций A+B» негоден потому, что компилятор того, может перемешать вычисления. Получится, что часть «измеряемой» функции B спряталась в функцию A, и на самом деле мы мерим неизвестную часть от B. Хорошо, когда A+B это боевая пара, использующаяся именно так. Плохо, когда вместо A тестовая синтетика типа rand().

    В данном случае такого перемешивания не происходит, в дизасме call _rand() без всяких попыток его инлайнить, но явно происходит некая другая беда. Какая именно, я не понимаю, но рабочая гипотеза про CPU stalls. Гипотеза оттого, что немного оттянув начало вычислений llr(), которое начинается с инструкции test esi, esi, где esi почти что только что вернули из rand(), удается измеримо ускорить исходный бенчмарк. При этом просто повторить цикл 2 раза, понятное дело, эффекта не дает, надо именно разнести вычисления:

    10.7 sec, rand()
    13.3 sec, rand() + llr()
    12.6 sec, rand() + llr() + 2x unroll

    // 2x unroll без ускорения, 13.3 sec
    int a = rand() - RAND_MAX / 2;
    int b = rand() - RAND_MAX / 2;
    x += LLR(a,b);
    a = rand() - RAND_MAX / 2;
    b = rand() - RAND_MAX / 2;
    x += LLR(a,b);
    
    // 2x unroll c ускорением, 12.6 sec
    int a = rand() - RAND_MAX / 2;
    int b = rand() - RAND_MAX / 2;
    int a2 = rand() - RAND_MAX / 2;
    int b2 = rand() - RAND_MAX / 2;
    x += LLR(a,b);
    x += LLR(a2,b2);
    


    В одном из экспериментов, кстати, ускорение до 12.8 sec вообще давала тупо вставка asm { nop } перед x += LLR(a,b), однако уверенно повторить такое чудо не удалось. Какая-то случайная флуктуация. Но в общем и целом, наглядно видно, что мерить фарш из тяжелого rand() и легкого llr() это занятие, хм, нетривальное. Я предполагаю, что где-то в паре инструкций test/jxx возникают stalls из-за того, что выражение подсчитали вот только что. Может, кто-нибудь, у кого есть под руками VTune, сможет посмотреть поточнее, отчего так.

    Мораль #2: пацаны, не мерьте фарш из исследуемой функции и синтетики, мерьте только нужную вам функцию. Как компилятор перемешает тот фарш и какие спецэффекты появятся на стыке, не угадать.

    Избавляемся от тормозного rand(), выносим предрасчет достаточно большого блока случайных чисел за цикл, внутри цикла оставляем только вычисление и суммирование llr(). Итого мерим вдобавок к функции еще и оверхед на доступ к памяти, но при линейном чтении он минимальный. ВНЕЗАПНО, вместо мнимого ускорения в 10 раз наблюдаем реальное ускорение в скромные 2.5 раза.

    Ок, это уже согласуется с ожиданиями, но теперь недостаточно быстро и хорошо ;)

    На помощь приходит SSE. У меня на десктопе неновый Core2Duo E8500, но даже он умеет SSE4. Разворачиваем цикл в 4 раза, считаем по 4 пары зараз. Прямо в лоб пользуемся специнструкциями для вычисления sign, abs.

    1.073 sec, llr() baseline
    0.438 sec, llr2() optimized, 2.5x
    0.125 sec, llr4() sse + 4x unroll, 8.6x

    Что интересно, код довольно читаем. Единственно что, надо аккуратно откомментировать _mm_sign_epi32(). Первое, он внезапно берет еще и 2й аргумент, и как бы умножает его на знак 1го аргумента. То, что надо. Второе, при этом _mm_sign(0)=0, а не 1, как для каких-то задач могло бы хотеться. Однако для наших целей разницы нет, тк. если abs(a) либо abs(b) равны 0, то sign*min(abs)=0, поэтому ошибки нет.

    static inline __m128i LLR4(const int * pa, const int * pb)
    {
    	__m128i a = *(__m128i*)pa;
    	__m128i b = *(__m128i*)pb;
    
    	// sign(a,b)*min(abs(a),abs(b))
    	__m128i absa = _mm_abs_epi32(a);
    	__m128i absb = _mm_abs_epi32(b);
    	__m128i absm = _mm_min_epi32(absa, absb);
    	__m128i ab = _mm_mullo_epi32(a, b);
    	__m128i rr = _mm_sign_epi32(absm, ab);
    	return rr;
    }
    


    Заметка на полях: что интересно, суммирование компонент регистра через _mm_hadd_epi32 вместо выгрузки регистра в память и суммирования затем 4 обычных int результатов не дает. Вот бы уже увидеть эффект от hadd наконец хоть где-нибудь, давно хочу.

    Еще заметка: что интересно, дальнейшее разворачивание цикла тоже результатов не дает. Видимо, уже упирается в скорость чтения памяти, там около 6.4 GB/sec выходит.

    Ошибка #3, значится, в том, что давно доступные SSE расширения не задействованы, все доблестные оптимизации зачем-то выполнены под платформу, по существу, i386.

    Это спорный вывод, долго думал, писать его или как. «Ошибка» это или нет? Я лично считаю, что да. Потому что если уж оптимизировать, так по-большому! Схоласты в каментах наверняка завоют, что таки нет. Ведь эта версия векторизована и работает по 4 пары чисел зараз, а это решительно несовместимое изменение исходной функции. Плюс оптимизации под i386 тоже очень ценны, вдруг программу запустят не на i7, а на памятнике IT археологии. Однако в любом случае, мораль уж точно одинакова.

    Мораль #3: пацаны, нынче 2013 год, SSE4 есть почти везде, SSE2 уаабще везде, поступайте соответственно, оптимизируйте (по возможности) под 99% пользователей, а не 1% fallback.

    Получившаяся SSE версия теперь уже достаточно бодра, чтобы наблюдать, как время на разных запусках пляшет от 0.125 до 0.145 секунд. Почти 20% разницы. Ох…

    Ошибка #4 только что появилась за счет оптимизации ;) Выставление высоких приоритета треда и процесса дает около 0.5% производительности, но никак спасает от дрожания измерений. Первое, простаивающий процессор сбрасывает частоту, а обратно ее набирает не сразу. За 10+ сек успевает, за 0.1 сек нет. Второе, даже если прогнать коротенький тест 100 раз, время каждой отдельной итерации все равно будет плясать. И в начале, и в конце этих 100 прогонов. Мало ли, что там в якобы простаивающей системе с 99% idle таки себе фоном работает и как влияет.

    Процессор надо «греть» и даже после этого результаты (достаточно коротеньких) прогонов надо фильтровать. Просто сделать чуть побольше итераций и усреднять недостаточно, общее время все равно заметно дрожит. Выкинуть первые «холодные» итерации недостаточно тоже, результат дрожит. Можно считать среднее и дисперсию, выкидывать outliers, где abs(value-mean)>=k*stddev, затем пересчитывать среднее, я попробовал, но и это дрожит!

    А менее всего дрожит такой нехитрый метод: делаем несколько прогонов (я делаю 10) и выбираем из них минимальное время одного прогона. Оказывается, что такой минимум стоит как вкопанный, отличия по нескольким запускам максимум на 0.5%.

    Обратите внимание: прогоны эти надо делать вдобавок к первоначальному разогреву в течение 3-10 сек, иначе 10*0.1 = 1 сек опять не хватит для набора эшелона и несмотря на фокус с минимумом, время все равно будет дрожать. Либо же прогонов надо делать (сильно) более 10. В нашем примере самая первая референсная реализация заодно работает как такой разогрев. Но если строчку Bench(Test1) закомментировать, опять начнутся пляски им.тов.св.Витта. Опа, throttling style!

    Мораль #4: пацаны, грейте проц не менее 3 сек, а лучше 10 сек и вдобавок считайте минимум по N прогонов.

    При бенчмарках можно допустить еще кучу ошибок, и наверняка в итоговом коде (кстати, вот он: gist.github.com/anonymous/5605201) еще что-нибудь пропущено, но сегодня мы разбирали вот эти. Надеюсь, не совершенно бесполезно.

    Будьте бдительны, мерьте правильно, оптимизируйте без пощады и до конца ;)
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 60

      +2
      На тему этой и предыдущей заметки: "Вечный вопрос измерения времени". Вдруг кому-то пригодится.
        –5
        Автор исходного поста писал про 10%, «в 10 раз» у него нет.
          +6
          «получаем десятикратное* увеличение скорости выполнения требуемой операции.»
            0
            Прогнав тестовую программу, получаем следующий результат — время выполнения составляет 9.4 секунды! Итого, сравнивая времена «нетто» (2.1 и 0.2 секунды соответственно) для двух вариантов расчета искомого значения, получаем десятикратное* увеличение скорости выполнения требуемой операции.


            Цитата из исходного поста.
              0
              Хм, а в начале статьи

              время выполнения составляет в среднем 9.2 секунды. Если раскомментировать вызов расчетной функции, пересобрать проект и повторить эксперимент — получаем примерно 11.5 секунд.


              И в конце

              Описанной оптимизацией только лишь одной функции удалось сократить время обработки единичного блока данных со 160 до 140 секунд (при выполнении на i7), что составляет более 10%
                0
                Десятикратное увеличение происходит в конкретном синтетическом тесте, а 10% — на базовом алгоритме, в котором помимо вычисления оптимизируемой функции очень много еще чего.
              0
              Мораль #5: используйте предсказуемый рандом, чтобы результаты консистентными были.
                +2
                там srand(0) вроде есть
                +13
                Во1х?
                  –8
                  привык так писать «во-первых»
                    +8
                    ну Вы же для остальных простых людей пишете :) в таких случаях в начале статьи нужно список терминов приводить
                      –4
                      Щаз (*) соберется критическая масса опечаток и я на утеху grammar nazi поправлю, может, пост

                      Но это сейчас у меня лично первый раз в жизни, когда кто-то не понял, что значит «во1х», «во2х», итп :)

                      (*) авторская транскрипция; автор в курсе, что в словаре пишут «сейчас»; заранее спасибо, неизвестный кэп ;)
                        +10
                        Я думал это либа какая-то, пока читал по диагонали.
                          +19
                          Я далек от граммар нацци, но «во1х» — это ужасно. Пожалуйста, не пишите так никогда! А за пост спасибо :)
                            –10
                            никогдааа?! 4 кнопки вместо 9, одна из которых чутка удаленный минус; в скайп-чатике быстрей-быстрей как же еще писать!? :)
                              +5
                              Настоящие мужики на это смотрят как удачную на возможность потренировать скоропись и гибкость пальцев! :)
                                +15
                                Если писать «1)» и «2)», то вообще по два символа получается. Следовательно:
                                1)пишется в 2 раза быстрее;
                                2)читается в 1000 раз быстрее, чем «бо-один-икс» и «бо-два-икс».
                                Оптимизации на лицо (если я не ошибся в бенчмарках).
                                  –7
                                  Ошибся в обоих пунктах, увы!
                                  1) пишется в 3 клика, а не 2, ибо скобки набираются с зажатым shift.
                                  2) чтение в 1000 раз быстрее пробенчмаркано только на одной платформе; на моей микроархитектуре различий нет, все исполняется за одинаковое время.

                                  А если серьезно, от всей этой ветки обсуждения у меня глаза слегка на лоб.

                                  Количество людей, которых, хм, легкое отклонение графематики от привычной им беспокоит достаточно для того, чтобы писать комментарии, активничать с плюсиками и минусиками и, может, еще как то проявлять активную гражданскую позицию, выходит неожиданно большое — отличается от нуля.

                                  Технический (вроде) ресурс, техническая (точно) статья, оформлена в целом, простите, не сказать, чтобы нечитаемо — а обсуждаем варианты написания «во-первых» — а еще 4 опечатки, которые я только что заодно с этим Чудовищным Отклонением от ХаброНормы исправил, не заметил почему-то никто.

                                  Привет, дивный новый Хабр — чую, еще пара лет эволюции в этом направлении и если хоть одну запятую не там поставишь, то берегись! Буря, пусть сильнее грянет буря!!! :)
                                    +7
                                    Развитие ветки обсуждения спровоцировало всё же то, что вы стали защищать свой очень-очень странный способ написания. Ошибки/опечатки, не мешающие пониманию, уже не замечают, зато такие, почти l337-speak, слова реально сильно затрудняют чтение.
                                    Хоть Хабр сейчас и модно ругать, но в этом случае дело не в нём.
                                      –9
                                      > Развитие ветки обсуждения спровоцировало всё же то, что

                                      «обсуждать» тут было сразу нечего после 2го моего комментария, а теперь уже абсолютно нечего

                                      > защищать свой… способ… Хабр… модно ругать,

                                      глаза на лоб еще сильнее

                                      лишний раз убеждаюсь, что чего и как не напиши — кто-нибудь обязательно все поймет настолько наоборот, что просто слов нет

                                      остается только надеяться, что большая часть посетителей Хабра — все еще способна без особых усилий понять, что такое «во1х», отличает пояснения от оправданий, иронию от ругани, итд итп

                                      и потому и не пишет ничего, слава Кришне, в эту замечательную подветку ;)
                                        +5
                                        > лишний раз убеждаюсь, что чего и как не напиши — кто-нибудь
                                        > обязательно все поймет настолько наоборот, что просто слов нет

                                        Попробуйте писать правильно, а не выдумывая правила для самого себя.
                                  +3
                                  Вы говорите о количестве нажатий так, словно вы каждый вводимый символ заново ищете на клавиатуре.
                        +1
                        отечески. спасибо андрей.
                          0
                          Нет, до сих пор есть активно использующиеся новые процессоры, которые ни сном ни духом про SSE. Vortex86SX хороший пример (инструкции 486ого на скоростях 200-400 МГц).
                            +6
                            активно использующиеся новые процессоры...Vortex86SX

                            Ну-ну. Много у вас в Селектеле стоек с Vortex86SX?
                              +1
                              В Селектеле нет. А вот на предыдущей работе два этажа было забито POS-оборудованием, которое x86, но SSE ни в зуб ногой.
                                +1
                                Embedded — это отдельный мир, и те, кто в нём живёт достаточно квалифицированы, чтобы адаптировать любой код к своим нуждам. SSE интринзиками их не испугаешь.
                                  +2
                                  Это не эмбеддед. Это обычные x86, на которые ставят винды/линукс и запускают обычный софт, написанный обычными программистами. Посмотрите на ближайший терминал оплаты — это оно.
                            +2
                            А вот это очень правильный подход, особенно с учётом того, что через две недели выйдет новая серия процессоров на архитектуре Haswell с дополненным набором инструкций AVX2 для обработки 256-битных целочисленных векторов (8 int32_t). Естественно, код с SSE intrinsics в AVX сам не переведётся, но вышеприведённый код элементарно переделать вручную: __m128i поменять на __m256i и _mm_xxx на _mm256_xxx, тем самым поднять производительность ещё примерно в 2 раза (с учётом новых Cycles per Instruction). Ну а для старых процессоров использовать линейную обработку.
                              +3
                              Мораль #6: найдите время запустить тест на разных компьютерах. Не в этом случае, но все же иногда алгоритм, работающий 250мс ± 200мс на настольном компьютере, может работать 200мс ± 10мс на сервере…
                                0
                                В идеале неплохо бы еще на разных архитектурах. Однако 250±200 это от 50 до 450, какой-то очень лихой разброс.
                                  0
                                  200 — это не радиус разброса, а стандартное отклонение. Там разброс на самом деле был от 150 до 600, только вот распределение далекое от нормального.
                                0
                                __m128i ab = _mm_mullo_epi32(a, b);
                                

                                А не случится ли тут переполнения?

                                P.S. А вот у меня на Core2 нет SSE4, посоветуйте фоллбак, интересно же потестировать.
                                  +1
                                  Случится. Такое же, как в исходной версии.

                                  __m128i tmp1 = _mm_mul_epu32(a,b); /* mul 2,0*/
                                  __m128i tmp2 = _mm_mul_epu32( _mm_srli_si128(a,4), _mm_srli_si128(b,4)); /* mul 3,1 */
                                  return _mm_unpacklo_epi32(_mm_shuffle_epi32(tmp1, _MM_SHUFFLE (0,0,2,0)), _mm_shuffle_epi32(tmp2, _MM_SHUFFLE (0,0,2,0))); /* shuffle results to [63..0] and pack */
                                  +7
                                  Геймдевелопера не скрыть за широкими штанами )) Люблю читать статьи shodan'а.
                                    +1
                                    Т.е. бывших геймдевелоперов не бывает? :)
                                      +1
                                      Разве геймдовом славен Андрей? Имхо, основная ассоциация — Sphinx.
                                      0
                                      С моралью три согласен полностью. В свое время использование вот этого варианта реализации декодера Витерби как раз дало где-то десятикратный прирост.
                                        0
                                        Результат от hadd надо искать в другом месте.
                                        Умножение матрицы 4x3 на вектор размером 4 можно делать через три штуки dpps (т.е. SSE4.2)
                                        а можно три mulps и три haddps (SSE3)
                                        По времени примерно одинаково (на текущих i7 haddps капельку быстрее), но не требует SSE4.2

                                        Вот тут обсуждали: blog.lexa.ru/2011/09/13/o_vektornom_umnozhenii_vtoroi_final.html
                                        (и там дальше по ссылкам)

                                        Вектор на матрицу — это всякие повороты и масштабирования (что color space, что координат, один хрен)
                                          0
                                          Кстати, посмотрел те цифири. На SandyBridge 3 mulps + 3 haddps — процентов на 20 быстрее трех dpps + два бленда.
                                        • НЛО прилетело и опубликовало эту надпись здесь
                                            0
                                            Дисперсия там и так маленькая (теоретически вообще 0, все полностью детерминировано же). Так что фокус про «нагрев» и минимум не дисперсию прячет, а решает вообще другую проблему: нестабильные результаты от запуска к запуску на коротких бенчмарках. Результаты хочется стабильные, а не плюс-минус 20%, иначе как сравнивать. Но и получать их хочется за 10 прогонов, а не 1000, иначе каждый запуск больно долго ждать. Получилось вот так.
                                              0
                                              Ну у процессора то подъем частоты до максимальной — это не десятки ж секунд. Вот 0.1, как мне кажется, больше на правду похоже
                                                0
                                                От настроек ОС зависит. Недавно была статья (название забыл), в которой объяснялось, почему иногда частота вообще не поднимается, несмотря на полную загрузку.
                                              0
                                              > Если мы хотим использовать этот код в продакшене,
                                              Не хотим. Речь идет о benchmark'ах.
                                              > Но очень странная идея — использовать минимум в погоне за маленькой дисперсией.
                                              Выбор наименьшего значения по результатам огромного повторения — это попытка определить время выполнения именно того участка кода, который нас интересует и нивелировать потери на кэш-промахи, переключения контекста и т.п.
                                              • НЛО прилетело и опубликовало эту надпись здесь
                                                  0
                                                  Не путайте замер времени выполнения кучки машинных инструкций и времени выполнения огромного алгоритма. Современное железо и ОС никто не оптимизировал для быстрого выполнения сверхмалых задач, отсюда и все погрешности в первом случае. Но, когда эта кучка инструкций войдет в другую программу как составная часть, все эти погрешности окажутся раскиданными по всей программе, и время выполнения этих инструкций станет близким к минимальному, что и измеряется.

                                                  Вот если измерять скорость работы некоторого модуля целиком — тогда да, надо брать среднюю.
                                                  • НЛО прилетело и опубликовало эту надпись здесь
                                                      0
                                                      > Вы путаете бенчмаркинг ради бенчмаркинга и бенчмаркинг ради практического результата.
                                                      Я не являюсь специалистом, но мне кажеться, что Вы путаете бенчмаркинг и профилирование.
                                                      • НЛО прилетело и опубликовало эту надпись здесь
                                                        0
                                                        Кеш-промахи — вещь не случайная, а закономерная, если только они не были вызваны переключением контекста, а потому даже после операция взятия минимума они будут учтены.

                                                        Переключения же контекста — вещь довольно редкая, но долгая. Если прогонять измеряемый участок кода не более сотни раз — есть риск измерить не время выполнения кода, а время переключения контекста, вместе со временем выполнения других потоков. Стоит же числу итераций подняться до пары миллионов — и все переключения контекста затеряются в основной массе полезной работы.

                                                        До конца время выполнения к минимальному не сведется, но надо же понимать разницу между 20мкс (+400мс на то самое переключение контекста) и 1 мс (+400мкс). (Цифры взяты с потолка)
                                                      0
                                                      Мне это знание за тем, что бы я мог сказать, что алгоритм (или реализация) «А» быстрее алгоритма «В».
                                                      Другое дело если мне нужен ответ на вопрос: «как долго выполняется алгоритм „А“ в реальном проекте и сколько это в процентах от общего времени (т.е. профилировка)?», — тогда я согласен, условия при которых производятся измерения должны быть максимально приближены к «боевым».
                                                      • НЛО прилетело и опубликовало эту надпись здесь
                                                        0
                                                        Знание затем, чтобы оперативно сравнивать две версии функции на микробенчмарке, в ходе работы над ними.

                                                        Еще раз: случай абсолютно одинаковый, но время одного исполнения сильно дрожит. По понятным вполне причинам, но с этим нельзя работать. Один (один) долбаный пик иногда сильно сбивает среднее, а он вдобавок не один. Представь ситуацию: в 70% повторов выходит 120+-1 мс, именно это значение (120 мс) и интересует. По существу, медианное. Еще в 30% повторов выходит что угодно от 130 до 150 мс, потому что наведенное (!) разными внешними (!) причинами дрожание. Внезапно, среднее колбасит от 123 до 129 мс, и внезапно, все отдельные изменения в ходе работы, которые реально убирают по 1 мс, бенчмаркать невозможно. А с минимумом возможно, и при этом итерация НЕ должна длиться 1000 повторов.

                                                        Вот можно, кстати, попробовать медиану считать. Этим разом я не попробовал. Но минимум считать еще проще, результат отличается незначительно, и главное: выходит крайне стабильным и эксперименты БЫСТРЫЕ, те. с ним можно ежеминутно работать. А для окончательного мега-бенчмарка, разумеется, нужно и кучу прогонов, и дисперсию, и вообще распределение лучше бы глянуть. Вот и все.
                                                        • НЛО прилетело и опубликовало эту надпись здесь
                                                            +1
                                                            Уже жалею, что снова ввязался в обсуждение. Мне кажется, ты споришь сам с собой, подразумевая какой-то свой прошлый опыт (неизвестный более никому), но зато чуть менее, чем полностью игнорируя то, что я тебе пытаюсь рассказывать про этот вполне конкретный случай. После мощного передергивания про «надо изгаляться над данными и выбрать другую метрику» (а этот бред придумал ты, но зачем-то немедленно приписал его мне) продолжать общение вообще, честно говоря, сразу неохота. Так что повторюсь последний раз, чисто для протокола, и хватит.

                                                            Да, вообще в произвольном общем случае с неизвестным распределением нижний outlier как показатель брать нельзя, так как иногда может подвести. Да, нужно как-нибудь фильтровать. Да, для финального мега-бенчмарка необходимо и качественно фильтровать, и вообще смотреть и анализировать распределение, и данные мерить не случайные, а несколько наоборов реальных плюс несколько специально сконструированных, итд итп. Все так, отличные верные общие выводы. Никто с ними как бы не спорит и обратного нигде никогда не писал.

                                                            Но мы говорим про конкретный и нередкий частный случай!

                                                            Нет, и в этом конкретном и в куче подобных частных случаев можно для ежеминутных итераций над фиксированными данными взять таки минимум. Это потому, что распределение скошено, нижних outliers нету (я типа смотрел свои данные), и минимум практически равен медиане. Алле, распределение известно, условия идеализированы (длинное линейное чтение из памяти), время исполнения тут вообще должно быть константой, те. наблюдаемое время t = T + random_noise, где нас интересует фактическое время T. Очевидно, что min(t) = T + min(random_noise) >= T, ситуация «min(t) маленький, но фактическое T большое» невозможна. Подчеркиваю: в данном конкретном классе случаев.

                                                            Есть красивые примеры, что в других классах бенчмарков все бывает сильно не так? Есть убедительные доказательства, что даже в данном классе бенчмарков min(random_noise) бывает сравним по порядку величины с T? Вперед, пиши статью, будет очень интересно почитать.
                                                            • НЛО прилетело и опубликовало эту надпись здесь
                                                                –1
                                                                > с потолка взять утверждение

                                                                «нижних outliers нету (я типа смотрел свои данные)»

                                                                комментарий не читай @ собеседника сразу обсирай
                                                    +1
                                                    Возвращаясь к оригинальному коду функции LLR() хотел бы сказать, что в зависимости от архитектуры может быть разное кол-во условных переходов. Например на ARM, где есть предикация, число переходов может быть меньше, чем на x86 и результаты оптимизации на ARM могут быть скромнее.
                                                    • НЛО прилетело и опубликовало эту надпись здесь

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

                                                      Самое читаемое