09.01.2021

Результаты вещественного теста

Ну вот, вроде бы оттестировал все процессоры на решение систем линейных уравнений методом Гаусса-Жордана, самое время привести результаты.

Здесь я не стал приводить результаты AMD FX-4350, так как он является базой для сравнения и его производительность принимается за единицу.
Как видно, рост
ускорения на вещественных операциях  за несколько лет составил даже больше, чем в два раза.
Безусловный лидер среди оттестированных – это AMD Ryzen 5 3600, он уверенно обгоняет оттестированные процессоры Intel поколения SkyLake.

 
Самое интересное, что и в однопоточном режиме, в отличие от целочисленных операций (см. предыдущий пост), рост производительности за несколько лет весьма значителен: 60% скалярной производительности и более 150% векторной.
Складывается впечатление, что основное ускорение в будущем в однопоточной производительности будет именно на вещественных операциях, так как, похоже, потенциал серьезного ускорения целочисленных операций на текущий момент полностью исчерпан. Хорошо, если будет один-два процента за год.
Впрочем, посмотрим, что даст в отношении целочисленных операций M1 от Apple. Вдруг случится неожиданный сюрприз. 😀

 
При одновременной работе нескольких потоков тупо всё решает их количество. Так что "щедрый" на ядра подход AMD, похоже, себя оправдывает.

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

 
Как видно, процессоры делятся на две группы: у одной серьезный провал на минимальном размере, у некоторых  процессоров Intel получается даже медленнее, чем решать на одном ядре.
У второй же группы провал отсутствует или минимален.

В первой группе все процессоры, поддерживающие HyperThreading, кроме Intel i3-3227U. Этот процессор, хоть и имеет данную технологии, не демонстрирует просадку производительности на минимальном размере.

Как одну из причин такого странного поведения я изначально рассматривал нехватку ОЗУ, что при переходе на многопоточный тест, из-за высокой производительности процессоров, не давало возможности сгенерировать достаточное количество систем для теста, что снижало точность тестирования.
Так, Intel i7-6700HQ имеет 12 ГиБ ОЗУ, i5-8300H вообще 8, а Ryzen 5 3600 16, что с учетом его производительности может быть недостаточно.

Но с другой стороны, на типе double провал существенно меньше, хотя производительность уменьшается незначительно (буквально на несколько процентов), в то время как памяти требуется в два раза больше. Если бы дело было в нехватке ОЗУ, это должно было бы привести к еще большему провалу, но это не так.


Видимо, все же дело в HyperThreading? Надо подумать, как можно было бы это легко проверить.

Да, еще: Scalar SSE всегда быстрее FPU. Ну, почти всегда. На трех переменных у Intel таки быстрее FPU. Но такое преимущество только на простых, арифметических операциях.
Сохранится ли оно на более сложных, например, тригонометрических, степенных или логарифмических операциях? Данный вопрос требует отдельного исследования, но у меня пока ни одной интересной задачки на эту тему не вырисовывается.
Ну и жертвуется точность, конечно. В редких случаях это может быть важно. Впрочем, пока что FPU присутствует на всех современных процессорах, так что при необходимости всегда можно его использовать.

7 комментариев:

  1. > в однопоточном режиме, в отличие от целочисленных операций, рост производительности за несколько лет весьма значителен

    Согласен! Но как я понимаю это еще потому что решение линейных уравнений более удобная задача для современных процессоров. Выполнять ворох однотипных операций достаточно просто, и наращивание FPU и ALU блоков приводит к заметному ускорению даже на одном ядре в следствии спекулятивного выполнение инструкций. Это кстати хорошо видно по Вашим графикам: при 192 уравнений польза от HT пропадает, так как один поток в состоянии загрузить все FPU блоки. Вроде AMD в Zen-3 еще FPU блоков докинул :) Алгоритм Евклида же, совсем не удобен процессору, конвейер простаивает, далеко вперед не убежишь, остается ускорять отдельные блоки, что AMD и сделала ускорив деление.

    > я изначально рассматривал нехватку ОЗУ

    Согласен, все же очень похоже именно на это :) Я бы в эту сторону еще покапал.

    > Но с другой стороны, на типе double провал существенно меньше

    А может быть на double даже при однопоточном варианте свопится? Если сравнивать однопоточную производительность для float и double для разных размерностей, она одинаковая?

    Еще интересно что при 24 уравнений многопоточное ускорение для float и double практически равно, а при 12 на double Ryzen и i5-8300h проседают, хотя на float у них все хорошо. Это тоже очень похоже на нехватку памяти.

    > В первой группе все процессоры, поддерживающие HyperThreading, кроме Intel i3-3227U

    Возможно HT не при чем, и группа с провалом - это все процессоры с больше чем 4 ядер, вследствии нехватки памяти? Извините, но очень на это похоже :)

    Я бы попробовал следующие тесты/графики:

    1. Сравнил однопоточную производительность для float и double.
    2. Попробовал запустить тест на 100-200 мс вместо секунды, чтобы уменьшить потребление памяти
    3. Запустил бы с 2-4 потоками вместо максимума, опять же чтобы уменьшить память.

    ОтветитьУдалить
    Ответы
    1. "больше чем 4 ядер" - имел ввиду "4-х потоков"

      Удалить
    2. > А может быть на double даже при однопоточном варианте свопится?
      Не очень понял, про что ты здесь спрашиваешь. На 3 переменных на одном потоке Float быстрее Double максимум на 9% на AMD A10-4600M, дальше идет FX-4350 c 6.5%. На Intel преимущество Float еще меньше, максимум у i7-6700HQ - 4.6%, у остальных еще меньше.
      Соответственно, так как объем памяти одинаков, то на Double практически при равной производительности я генерирует в два раза меньше систем, чем на Float, а значит, если дело в погрешности, то на Double ситуация должна быть еще хуже. А она существенно лучше!

      > Если сравнивать однопоточную производительность для float и double для разных размерностей, она одинаковая?
      Нет. Float дает максимальное преимущество на больших размерностях, что в общем-то ожидаемо.
      Но разные процессоры ведут себя немного по-разному. У кого-то есть максимум, у кого-то полка начиная с определенной размерности, у кого-то два пика.
      Нетипично ведет себя только Ryzen - у него стабильно почти одинаковая производительность для всех размерностей для обоих типов, кроме пика в 9% на 24 переменных.
      Максимальное преимущество у FX-4350 в 25% на 192 переменных. У i7-6700HQ ~16% на 384 переменных, у i5-8300H два пика в 10% на 24 и 384 переменных, так же и у i5-6500: 10% на 24 и 14 на 384.
      Размерности выше 384 сравнить не могу, так как float решался только до этой размерности.

      Удалить
    3. > то на Double практически при равной производительности я генерирует в два раза меньше систем

      А, ок, я чуть-чуть по другому у себя тестировал: старался сгенерировать столько систем, чтобы тест выполнялся приблизительно 1 сек, соответственно при многопоточном тесте упирался в физический размер памяти, но у Вас такой проблемы нет как я понимаю, так как Вы опираетесь на полный размер систем в памяти. Что выглядит проще :)

      А как СЛАУ распределяются по потокам в многопоточном тесте? Если через остаток от деления индекса на число потоков, то теоретически замедление может быть из-за False Sharing: на маленьких системах несколько потоков обновляют одну и туже линию кэша, что приводит к постоянной синхронизации кэшей. Чем меньше размер системы в памяти и больше потоков, тем чаще происходит синхронизация кэша и возможно при системе из 3-х float, синхронизация упирается в пропускную способность. Если тест устроен таким образом, я бы попробовал протестировать когда 1-е N СЛАУ выполняются 1-м потоком, следующие N СЛАУ 2-м и т.д, где N=ЧислоСистем/ЧислоПотоков. А еще возможно оптимальнее: выровнять входные данные изначально по 64-бит границе, а дальше распределять по 16 СЛАУ за раз: 1-е 16 СЛАУ на 1-й поток, 2-е на 2-й поток и т.д. Размер 16-ти СЛАУ как для float, так и для double всегда будет кратен 64-бит вне зависимости от числа уравнений, плюс тест будет проходить по СЛАУ практически линейно, что должно быть более оптимально для подсистемы памяти.

      Но это так гадание на кофейной гуще :)

      Удалить
    4. "64-бит границе", "кратен 64-бит" - хотел написать не бит, а 64-байт

      Удалить
    5. > так как Вы опираетесь на полный размер систем в памяти.
      Ну, почти... Вообще, я выделяю именно под системы половину от всей физической памяти, если есть файл подкачки, и половину от доступной физической памяти, если его нет.
      Дальше, если хватает памяти, то создаю столько систем, сколько их нужно для решения за 1 сек. одним потоком. Если же памяти маловато, то использую всю, что выделил, тогда получается, что в этом случае один поток решит быстрее, чем за секунду.
      Беру всего половину, так как в худшем случае, для систем из трех переменных, еще примерно 25% памяти уйдет на указатели.

      > А как СЛАУ распределяются по потокам в многопоточном тесте?
      Я использую второй описанный тобою вариант.

      > Если через остаток от деления индекса на число потоков
      Я сразу такой вариант не рассматривал из-за синхронизации кэшей.
      Более того, я посчитал, что при решении маленьких систем погрешность может быть сильно высокой даже из-за самого цикла for, даже без деления. Поэтому обернул решение N систем в ассемблерную процедуру, что бы снизить погрешность от неоптимальности Delphi, и самое главное, избавиться от операций сохранения регистров в стек на каждом решении.

      Удалить
    6. Эх, опять мое предположение не оправдалось :) Даже и не знаю тогда, надо будет у себя проверить.

      Удалить