26.07.2020

Тестер. Первые результаты

Особенности работы протестированных процессоров в параллельном режиме я описал в предыдущем посте. Теперь посмотрим, на что способно одно ядро каждого процессора.

FPU32. СЛАУ из 3-х уравнений AMD FX-4350 решает 8.5 млн./сек. Это несколько отличается от значения, которое я приводил в самой первой статье. Объясняется просто: там я оценивал производительность, решая много раз одну и ту же СЛАУ, поэтому данные попали в кэш первого уровня и производительность была больше. В конечно же варианте теста производительность оценивается при решении разных систем.
i3-3227U обеспечивает 6.7 млн./сек, а i7-6700HQ - 13.6 млн./сек.

FPU64. СЛАУ из 3-х уравнений FX-4350 решает также 8.5 млн./сек., i3-3227U - 6.7 млн./сек., а вот i7-6700HQ настолько быстр, что в 32-разрядном режиме памяти под все системы не хватило, что бы время решения было примерно 1 секунду. И для система из 6 уравнений тоже.
Системы же из 12 уравнений этот процессор решает 792 тыс./сек., FX-4350 - 454 тыс./сек, i3-3227U - 407 тыс./сек.

FPU80. СЛАУ из 3-х уравнений FX-4350 решает также 3.5 млн./сек., а (сюрприз!) i3-3227U - 4.5 млн./сек. То есть маленький, крохотный процессор для нетбуков от Intel с тактовой частотой 1900 МГц кроет как бык овцу AMDшный процессор с частотой 4200МГц!  i7-6700HQ опять же оказался настолько быстр, что памяти под такие СЛАУ не хватило.
А вот системы из 6-ти уравнений процессоры решили так: FX-4350 - 603 тыс./сек, i3-3227U - 1 млн./сек., i7-6700HQ - 2 млн/сек.

Какой вывод из всего этого можно сделать? Архитектура Piledriver от AMD очень сильно проигрывала решениям Intel, несмотря на более высокие тактовые частоты, по крайней мере при операциях с плавающей точкой.
А что касается научных вычислений с максимально возможной точностью, то AMD находилась где-то ниже плинтуса.
Правда, в последних поколениях процессоров AMD исправилась и сейчас отстает как раз Intel. Но пока протестировать последние процессоры от AMD мне не довелось.

Общая оценка. Методика оценки очень проста. Решается набор СЛАУ разных размерностей от 3 до максимальной, на решение которой требуется не более 1 секунды. Увеличение размерности идет по геометрической прогрессии со знаменателем 2.
Процессоры сравниваются только на тех размерностях, которые у них совпадают. Разные типы данных имеют разный приоритет. Для текущей оценки я взял приоритет 0.5 для FPU32, 0.4 для FPU64 и 0.1 для FPU80.
Однопоточная производительность имеет приоритет 0.6, параллельная - 0.4. Если за единицу брать производительность процессора FX-4350, то получается вот такая картинка.
FX-4350 имеет базовую частоту 4200 МГц, частоту шины памяти 1600, i3-3227U - 1900 и 1333 МГц, i7-6700HQ - 2600 и 2133 МГц соответственно

В принципе, можно построить более подробные графики по каждому типы вычислений, но не уверен, нужно ли.

Что дальше? Дальше нужно решить пару проблем.
Первая состоит в том, что я генерирую уникальные СЛАУ для теста случайным образом. В некоторых, очень редких случаях система может получаться не совместной, т. е. либо не имеющей решение, либо требующей более высокой точности при решении, иначе возникает переполнение.
В принципе, при решении реальных СЛАУ такие ситуации нужно просто отслеживать и выдавать предупреждение в ходе решения. Но не в случае замера производительности!
Т. е. надо бы придумать, как сгенерировать случайную гарантированно совместную систему. Пока никаких идей у меня в этом направлении нет. Ну, за исключением того, что бы не генерировать уникальные системы, а сделать лишь одну гарантированно совместную, а потом ее просто скопировать нужно количество раз.

Вторая проблема состоит в том, что современные процессоры очень-очень быстры. А для точного замера производительности нужно решить достаточное количество систем. Я в текущем варианте беру такое количество, которое решается примерно за 1 секунду. В таком режиме погрешность измерения составляет примерно 5% (хотя точно научными методами я ее не оценивал), поэтому уменьшать время не хотелось бы.
Тем более, что производительность настолько высока, что для самых быстрых процессоров пришлось бы ее снижать не в 2 раза, а на порядок и более, что существенно увеличило бы погрешность.

Но в 32-разрядном режиме для маленьких систем у меня не хватает памяти для их размещения! То есть 2 гигабайт памяти, выделяемой программам Windows в 32-разрядном режиме не достаточно, так как время решения всех систем, помещающихся в эту память, меньше 1 секунды! Ситуация еще более усложняется при работе в параллельном режиме, так как там каждому ядру необходимо решить свой отдельный набор СЛАУ.
Кроме того, желая максимально использовать доступную мне память, я допустил где-то досадную ошибку, вызывающее ошибку нехватки памяти, которая возникает только на очень быстрых системах. Из-за этого не удалось оттестировать еще один доступный мне ноутбук.

Таким образом, крайне актуальной задачей является переход на 64-разрядную версию программы. Там есть свои нюансы, но об этом в следующий раз.

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

  1. Мой мобильный i5 на FPU32 как раз где-то между Вашими i3 и i7 с 10 млн./сек. систем из 3-х уравнений.

    FPU80 - кстати очень не стандартный тип, у того же clang при компиляции в 64-bit, extended precision (long double в C) - 128 bit, а не 80, я даже не уверен он вообще поддерживает 80-bit, может поэтому AMD решили его реализовать абы как?

    А если генерировать систему уравнений x * M = y следующим образом:
    1. Генерируем случайные x и M
    2. Считаем y
    3. Выбрасываем x и используем M и y (в моем случае объединяем вместе в прямоугольную матрицу)

    Вроде в таком случае система должна быть всегда совместной, за исключением случаев когда в M есть зависимые строки, но по-моему вероятность этого близка к нулю. Я попробовал и ошибка решения стала существенно ниже, но все равно не 0, так как как не крути, ошибка накапливается.

    ОтветитьУдалить
  2. Я так и генерирую. )

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

    > как как не крути, ошибка накапливается.
    Да,особенно на больших системах. Возможно, стоит использовать целочисленные коэффициенты, или коэффициенты с небольшим числом знаков после запятой, да еще и дробная часть что бы была суммой обратных величин от целых степеней двоек? )

    > FPU80 - кстати очень не стандартный тип
    Ну, может и не совсем стандартный, но очень распространенный - всегда реализован на FPU процессоров x86-64.
    В то время как более стандартный 128-битный гораздо реже реализуется аппаратно. Думаю, поддержку FPU80 можно включить какими-то опциями компилятора.
    Но вообще почему-то разработчики компиляторов активно рекомендуют использовать именно SSE в 64-разрядном режиме вместо x87. Правда, SSE поддерживает только арифметические команды на аппаратном уровне. Как выполнять более сложные вычисления, не очень понятно.

    ОтветитьУдалить
    Ответы
    1. А я изначально просто случайные числа использовал и игнорировал ошибку :)

      Посмотрел ассемблер для FPU80: хоть в памяти тип и занимает 128 бит, но это все тот же FPU80, просто выравненный и clang использует обычные x87 инструкции для него.

      Как я понимаю x87 все еще существует только для обратной совместимости, мало того что не развивается, но еще и иногда замедляется. Современный подход, на сколько я знаю, это простые быстрые векторные инструкции, а транслировать тригонометрические и другие сложные операции в более простые может и компилятор, это намного проще писать, понимать, отлаживать и исправлять чем микрокод. Также это позволяет легко иметь несколько реализаций: точная, но медленная, быстрая, но менее точная, и легко переключаться между ними. В прикладной программе такие операции выполняются через вызов функции из стандартной библиотеке, например вот одна из реализаций синуса в linux: https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/x86_64/fpu/multiarch/svml_d_sin2_core_sse4.S

      Удалить
    2. Тут не понятно. На первый взгляд организация FPU в виде регистрового стека без свободного доступа к произвольному регистру делает программирование FPU более сложным. Но на самом деле, похоже, его создатели сделали это не просто так: это позволяет достаточно легко программировать вычисления сложных арифметических выражений, если понять суть и вспомнить про обратную польскую запись.

      Насчет замедления не знаю. Раньше, по крайней мере, АЛУ для вещественных чисел совместно использовалось FPU и SSE. На некоторых топовых процессорах предыдущих поколений было 4 АЛУ, для того, что бы SSE работало максимально эффективно. Но это же позволило выполнять 3-4 команды FPU параллельно, если они были независимы. То есть потенциально можно было достичь производительности SSE на FPU, если грамотно развернуть циклы.

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

      Синусом полюбовался, прикольно. Но это, насколько я понял, "быстрая", но не точная реализация? На самом деле, похоже, очень медленная.Даже на стареньком 487 синус и одновременно косинус с полной точностью вычислялся за 300 тактов. Я вот не уверен, что этот ассемблерный будет с такой же скоростью считаться.

      Удалить
    3. Стек возможно удобен для написания ассемблера человеком, но вот развернуть цикл так чтобы процессор загружал конвейер и выполнял инструкции параллельно - совсем не тривиально, так же как я понимаю то что внутри регистр 80 битный, тоже вставляет пальцы в колеса.

      Я попытался найти сравнение производительности для синуса на x87 и из glibc, но что-то не нашел, а также не смог мой компилятор заставить использовать x87 (правда долго не пытался). Зато наткнулся на статью о баге в процессорной реализации: https://randomascii.wordpress.com/2014/10/09/intel-underestimates-error-bounds-by-1-3-quintillion


      Ага это была быстрая, но не точная, вот эта как я понимаю точная, но не векторизованная: https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/ieee754/dbl-64/s_sin.c;hb=HEAD#l194

      Удалить