Ну вот, вроде бы оттестировал все процессоры на решение систем линейных уравнений методом Гаусса-Жордана, самое время привести результаты.
Здесь я не стал приводить результаты AMD FX-4350, так как он является базой для сравнения и его производительность принимается за единицу.
Как видно, рост ускорения на вещественных операциях за несколько лет составил даже больше, чем в два раза.
Безусловный лидер среди оттестированных – это AMD Ryzen 5 3600, он уверенно обгоняет оттестированные процессоры Intel поколения SkyLake.
Складывается впечатление, что основное ускорение в будущем в однопоточной производительности будет именно на вещественных операциях, так как, похоже, потенциал серьезного ускорения целочисленных операций на текущий момент полностью исчерпан. Хорошо, если будет один-два процента за год.
Впрочем, посмотрим, что даст в отношении целочисленных операций M1 от Apple. Вдруг случится неожиданный сюрприз. 😀
Ну и напоследок традиционная загадка, на которую пока у меня нет ответа. При переходе от однопоточных к многопоточным скалярным вычислениям наблюдается серьезный провал производительности на минимальных размерах.
У второй же группы провал отсутствует или минимален.
В первой группе все процессоры, поддерживающие HyperThreading, кроме Intel i3-3227U. Этот процессор, хоть и имеет данную технологии, не демонстрирует просадку производительности на минимальном размере.
Как одну из причин такого странного поведения я изначально рассматривал нехватку ОЗУ, что при переходе на многопоточный тест, из-за высокой производительности процессоров, не давало возможности сгенерировать достаточное количество систем для теста, что снижало точность тестирования.
Так, Intel i7-6700HQ имеет 12 ГиБ ОЗУ, i5-8300H вообще 8, а Ryzen 5 3600 16, что с учетом его производительности может быть недостаточно.
Но с другой стороны, на типе double провал существенно меньше, хотя производительность уменьшается незначительно (буквально на несколько процентов), в то время как памяти требуется в два раза больше. Если бы дело было в нехватке ОЗУ, это должно было бы привести к еще большему провалу, но это не так.
Видимо, все же дело в HyperThreading? Надо подумать, как можно было бы это легко проверить.
Да, еще: Scalar SSE всегда быстрее FPU. Ну, почти всегда. На трех переменных у Intel таки быстрее FPU. Но такое преимущество только на простых, арифметических операциях.
Сохранится ли оно на более сложных, например, тригонометрических, степенных или логарифмических операциях? Данный вопрос требует отдельного исследования, но у меня пока ни одной интересной задачки на эту тему не вырисовывается.
Ну и жертвуется точность, конечно. В редких случаях это может быть важно. Впрочем, пока что FPU присутствует на всех современных процессорах, так что при необходимости всегда можно его использовать.





> в однопоточном режиме, в отличие от целочисленных операций, рост производительности за несколько лет весьма значителен
ОтветитьУдалитьСогласен! Но как я понимаю это еще потому что решение линейных уравнений более удобная задача для современных процессоров. Выполнять ворох однотипных операций достаточно просто, и наращивание 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 потоками вместо максимума, опять же чтобы уменьшить память.
"больше чем 4 ядер" - имел ввиду "4-х потоков"
Удалить> А может быть на 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 решался только до этой размерности.
> то на Double практически при равной производительности я генерирует в два раза меньше систем
УдалитьА, ок, я чуть-чуть по другому у себя тестировал: старался сгенерировать столько систем, чтобы тест выполнялся приблизительно 1 сек, соответственно при многопоточном тесте упирался в физический размер памяти, но у Вас такой проблемы нет как я понимаю, так как Вы опираетесь на полный размер систем в памяти. Что выглядит проще :)
А как СЛАУ распределяются по потокам в многопоточном тесте? Если через остаток от деления индекса на число потоков, то теоретически замедление может быть из-за False Sharing: на маленьких системах несколько потоков обновляют одну и туже линию кэша, что приводит к постоянной синхронизации кэшей. Чем меньше размер системы в памяти и больше потоков, тем чаще происходит синхронизация кэша и возможно при системе из 3-х float, синхронизация упирается в пропускную способность. Если тест устроен таким образом, я бы попробовал протестировать когда 1-е N СЛАУ выполняются 1-м потоком, следующие N СЛАУ 2-м и т.д, где N=ЧислоСистем/ЧислоПотоков. А еще возможно оптимальнее: выровнять входные данные изначально по 64-бит границе, а дальше распределять по 16 СЛАУ за раз: 1-е 16 СЛАУ на 1-й поток, 2-е на 2-й поток и т.д. Размер 16-ти СЛАУ как для float, так и для double всегда будет кратен 64-бит вне зависимости от числа уравнений, плюс тест будет проходить по СЛАУ практически линейно, что должно быть более оптимально для подсистемы памяти.
Но это так гадание на кофейной гуще :)
"64-бит границе", "кратен 64-бит" - хотел написать не бит, а 64-байт
Удалить> так как Вы опираетесь на полный размер систем в памяти.
УдалитьНу, почти... Вообще, я выделяю именно под системы половину от всей физической памяти, если есть файл подкачки, и половину от доступной физической памяти, если его нет.
Дальше, если хватает памяти, то создаю столько систем, сколько их нужно для решения за 1 сек. одним потоком. Если же памяти маловато, то использую всю, что выделил, тогда получается, что в этом случае один поток решит быстрее, чем за секунду.
Беру всего половину, так как в худшем случае, для систем из трех переменных, еще примерно 25% памяти уйдет на указатели.
> А как СЛАУ распределяются по потокам в многопоточном тесте?
Я использую второй описанный тобою вариант.
> Если через остаток от деления индекса на число потоков
Я сразу такой вариант не рассматривал из-за синхронизации кэшей.
Более того, я посчитал, что при решении маленьких систем погрешность может быть сильно высокой даже из-за самого цикла for, даже без деления. Поэтому обернул решение N систем в ассемблерную процедуру, что бы снизить погрешность от неоптимальности Delphi, и самое главное, избавиться от операций сохранения регистров в стек на каждом решении.
Эх, опять мое предположение не оправдалось :) Даже и не знаю тогда, надо будет у себя проверить.
Удалить