Комментарии 121
Самые большие относительные ошибки внезапно лезут не при сложении, а при вычитании близких значений.
3.1415 — 3.1414 -> В результате этой операции в мантиссе младшие 4 знака заполнены нулями, т.е. мусором.
На импортном языке называется catastrophic cancellation.
В общем и среднем по архитектурам, можно ожидать, что векторизованный код должен отличатся float vs. double по производительности в 2x для арифметических операций ±/*. С делилкой уже не так, с корнями, экспонентами и прочими синусами разрыв больше.
В выводах написано — много складываете, то складывайте в даблах. А что делать, если я умножаю много — float нормально?
С одной стороны, при умножении разных по значению чисел такой потери точности, как при сложении, не происходит. С другой стороны, ошибки округления накапливаются ничуть не хуже чем при сложении. Поэтому общая рекомендация — использовать повышенную точность для промежуточных расчетов — остается в силе.
С третьей стороны, вполне возможно, что компилятор все равно все оптимизирует так, чтобы промежуточных результатов использовались регистры FPU (80 бит).
Кроме того всегда нужно оценивать необходимую разрядность мантиссы вместо тупого использования double вместо float.
Если в вашем числе с плавающей запятой P разрядов (7 для float, 16 для double) точности, а в ваших данных S разрядов значимости, то у вас остаётся P-S разрядов для манёвра и можно сложить 10^(P-S) значений без проблем с точностью. Так, если бы мы использовали 16 разрядов точности вместо 7, то могли бы сложить 10^(16-3) = 10 000 000 000 000 значений без проблем с точностью.
(Существуют численно стабильные способы сложения большого количества значений. Однако простое переключение с float на double гораздо проще и, вероятно, быстрее).
Проблемы с точностью у double могут возникнуть уже после сложения 2 чисел при разности порядков более 13, разве нет?
Не нужно поселять необоснованную уверенность универсальности double в мозги юных подаванов.
И это не частность, достаточно не мало задач, где нас интересует потом SAD в данных или именно младшие разряды.
Вместо разрядности чисел можно выбирать другие алгоритмы расчёта (с улучшениями в сохранении точности).
Также: округлением чисел можно управлять.
Насколько понимаю, результат расчётов может зависеть от использования обычного FPU с обычными числами или использования SIMD (SSE, AVX): в первом случае внутреннее представление будет 80 бит, во втором — 64 или 32.
И
https://habrahabr.ru/post/112953/#comment_3623058
https://habrahabr.ru/post/112953/#comment_8730233
Если даблов не хватает, а производительность не интересует, то есть библитеки для вычислений произвольной точность, типа GNU MPFR.
Если даблов не хватает, но производительность важна — меняйте алгоритм.
На десктопных и серверных процессорах это пока что не так. (Коллега khim@ говорил про проблемы на Atom и Bobcat, надо уточнять.) Я рядом показывал результаты от его теста после устранения проблем передачи параметров, разницы вообще не было.
Для сравнения стащил очень прямолинейный расчёт СЛАУ по Гауссу, матрица 1000*1000, случайные коэффициенты; i3-4170 (Haswell).
FPU+double: 4.6сек; FPU+long double: 5.5 сек — соответствует оценке влияния кэширования; SSE: 3.6 сек — с тем, что в выхлопе clang есть лёгкая векторизация.
Это ближе к тому, что времена собственно вычисления не отличаются.
> В документации описаны ситуации, когда младший бит мантиссы результата принимает непредсказуемое значение. К сожалению, прямо сейчас я не смогу предоставить ссылку на конкретный раздел документации, но если интересно, завтра могу уточнить.
Прошу таки уточнить, и заодно к каким версиям это относится. Где-то до 387-го у них было ещё много странностей, но потом вроде всё вычистили?
P.S. Есть один момент, в котором я полностью согласен, что x87 это legacy: это его неуместный стек регистров. Кому и зачем это стукнуло в голову — ХЗ, но это не единственная, мягко говоря, странность Intel. Но сейчас и его помеху снизили до минимума.
…
The accuracy of these instructions is measured in terms of units in the last place (ulp).
…
With the Pentium processor and later IA-32 processors, the worst case error on transcendental functions is less
than 1 ulp when rounding to the nearest (even) and less than 1.5 ulps when rounding in other modes.
Если это интерпретировать, получается, что младший бит в половине случаев будет неправильным. Чтобы он был правильным, необходима точность 0.5 ulp или лучше.
Понятно что получались два примерно разных результата. Понятно что «в природе» белок мог свернуться туда и сюда в таких случаях. Но когда на Intel'е он сворачивается в одну сторону, а на AMD — в другую… это просто усложняет отладку, вот и всё… но всё равно выливается в серьёзные затраты.
Если на разных FPU белок сворачивается в разные стороны, значит, вычисления очень чувствительны к погрешностям такого порядка (~1ulp), а это уже признак, что им надо или повышать точность значений (double недостаточно), или менять модель на более устойчивую.А теперь, внимание, вопрос: как вы собрались «менять модель на более устойчивую», если оригинальное явление обладает неустойчивостью? Которую мы, собственно, и исследуем? Как я уже сказал: если этот белок реально синтезировать и посмотреть — куда он сворачивается, то там возможны несколько вариантов. Причём это явление настолько распространено, что в клетке бывают даже специальные «помощники», помогающие определённым белкам выбрать правильное направление сворачивания. В совсем клинических случаях один и тот же белок, свёрнутый по разному, используется клеткой для разных целей (но, понятно, в лекарствах такого ужаса стараются не допускать).
При устойчивой модели такие различия могут сбить младшие разряды (нарушение духа IEEE754 — результаты должны быть воспроизводимы на любом железе), но не привести к принципиально различным результатам.Вот только не всё в этом мире описывается устройчивыми моделями…
Понятно, что когда у вас явление неустойчиво малые модификации кода могут приводить к тому, что результаты будут меняться — тут ничего страшного нет, ещё раз повторюсь, мы исследуем явление, которое само по себе неустойчиво. Но когда результаты меняются без модификации кода — это просто неприятно и неудобно…
Для неустойчивых явлений должна быть устойчивая модель анализа собственно неустойчивости. Методы для этого в общем известны, хотя там много уникальной теоретической работы в каждом случае.
> Понятно, что когда у вас явление неустойчиво малые модификации кода могут приводить к тому, что результаты будут меняться — тут ничего страшного нет, ещё раз повторюсь, мы исследуем явление, которое само по себе неустойчиво. Но когда результаты меняются без модификации кода — это просто неприятно и неудобно…
Может, не кода, а исходных данных? Хотя в задаче о свёртке белка входные данные самого белка дискретны, малые модификации не получатся — их надо вводить по ходу процесса свёртки.
Но когда результаты радикально меняются от сверхмалых изменений входных параметров или промежуточных значений — это признак того, что надо менять модель, и тут проблемы процессоров как раз пошли на пользу — явление опознано.
Вообще, тут надо подобные микроошибки как раз вводить в расчётные программы, для детекта неустойчивости.
> The functions are guaranteed to be monotonic, with respect to the input operands, through the domain supported by the instruction.
Всё вместе наводит на несколько иное понимание — результат конкретной функции для конкретного аргумента всегда один и тот же, но все эти результаты вместе — могут показывать систематическое смещение относительно идеального значения.
Кроме того, это всё явно описано для максимальной точности (64 бита мантиссы). Уже для double (53 бита) эти ошибки становятся несущественными, потому что менее 1/2048 от ulp.
Для совсем глубокой раскопки можно было бы поискать, как именно эти систематические ошибки отличаются для разных моделей FPU. Но важно ли это, с учётом предыдущего абзаца?
Непредсказуемым от этого результат в стиле FPU не становится, но нестандартным — вполне может.
С этим безусловно согласен. Но термин «непредсказуемый» мне тут откровенно не нравится, потому что слишком сильно намекает на варианты типа «сразу повторили ту же операцию, а результат другой».
Считаю тогда, что разногласий тут нет.
А еще не стоит забывать, что на самом деле бинарная арифметика плохо подходит для многих видов вычислений из реальной жизни. Например для финансовых данных. Так как все вычисления должны воплняться по правилам десятичной арифметики, а не двоичной.
Бинарная арифметика хорошо подходит там, где основание системы счисления не имеет значения и не требуется контроль округления младших разрядов. Например почти все виды данных связанных с измерением объектов реального физического мира (цаще всего это научные и инженерные данные). Кстати, все примеры в статье — это как раз такие данные ("Размер комнаты", "Окружность Земли" и т.д.).
Увы, поддержка IEEE 754 decimal32/decimal64 очень слабая и на сколько мне известно, на поддержку десятичной арифметики в новом железе все производители забили (поправьте если это не так). Плюс дополнительно проблема осложнаяется тем, что тут требуется некоторая поддержка со стороны языков программирования. А её нет (за редким исключением).
А где нет inexactʼа (как .NET Decimal) — нет даже этого преимущества.
Второе преимущество уже не в качестве операций, но в устранении проблем понимания. Когда вопрос «Floating point is broken? 0.1+0.2 != 0.3» задаётся на Stackoverflow каждые пару дней — это показывает реальный барьер: людям сложно понимать двоичную плавучку. И десятичная тут помогает не отпугнуть широкие массы тех, кого нельзя по-честному называть программистами, но без кого на нынешнем безрыбье не обойтись ;( да, я тут чуть снобствую, для ясности объяснения.
Всё остальное у десятичной арифметики хуже. Точность операций хуже, за счёт размера младшего разряда. Реализации сильно сложнее, и аппаратно, и программно.
Если есть возможность делать финансовую арифметику на целых числах (копейки, сотые доли копейки — где как надо) — лучше делать так, несмотря на присутствие десятичной плавучки. (Разумеется, при наличии контроля переполнений. Это другая проблема, местами тяжёлая.)
Так как все вычисления должны воплняться по правилам десятичной арифметики, а не двоичной.А чисел с фиксированной точкой (то бишь, на самом деле, целых) — не хватает? Просто интересно — что за задача должна быть, чтобы нельзя было всё посчитать, условно говоря, «в копейках» и требовались числа с плавающей десятичной точкой…
Двоичной арифметики с любой точностью не хватает если вам нужны точные (или контролируемо огругляемые) вычисления.
Канонический пример:
if (0.1+0.2==0.3) {
// This will NOT be shown
printf("Literals are decimal\n");
}
Больше примеров здесь:
http://speleotrove.com/decimal/decifaq1.html
Кстати, там же говорится, что по проводившемуся IBM по анализу данных от порядка 50 крупных организаций только порядка 2% от общего количества данных использовали двоичное представление (и 55% у десятичных).
А вот (частично повторю соседний комментарий) 99999.99 + 0.02 при вычислении в decfloat32 окажутся 100000 вместо верного, но уже не представимого, 100000.01. И только inexact exception может свистнуть вам о проблеме, но кто на него обращает внимание?
В таких случаях хорошо бы использовать что-то типа numeric(n,m) используемых в БД.
Я заметил, что в последнее время стали всё чаще появляться такие вот статьи в стиле "я вам тут щас всё опясню, пацаны". Видимо, теория "в XXI веке можно нагуглить всё" работает не полностью, потому что поверхностные знания не успевают осесть, отфильтроваться и получить практическое подкрепление.
точность примерно 24 бита
Ну да, где-то +- бит, в зависимости от температуры процессора.
Итак, если вы измеряете размер квартиры, то достаточно float. Но если хотите представить координаты GPS с точностью менее метра, то понадобится double.
Тут я не уверен, но что-то мне подсказывает, что координаты GPS имеют какой-то фиксированные формат. Или автор решил, что как можно сделать, как в той на картинке из CSI?
Если у вас много оперативной памяти, а скорость выполнения и расход аккумулятора не являются проблемой — вы можете прямо сейчас прекратить чтение и использовать double.
Смешались в кучу кони, люди. Быстродействие, размер памяти и расход батареи сильно зависят от модели процессора, архитектуры компюьтера, алгоритма и вообще от множества параметров. На некоторых процессорах double вообще не поддерживается аппаратно.
Если у вас хорошо подогнанный конвейер с использованием SIMD, то вы сможете удвоить производительность FLOPS, заменив double на float. Если нет, то разница может быть гораздо меньше, но сильно зависит от вашего CPU. На процессоре Intel Haswell разница между float и double маленькая, а на ARM Cortex-A9 разница большая.
Снова кони и люди. Ни слова о том, что, например, у Intel в FPU всегда используется 80-bit precision, а результат потом просто округляется до нужного.
Исчерпывающие результаты тестов см. здесь.
Цикл вида C = А [operator] B
. Супер-исчерпывающе. Давно известные тесты производительности? Ерунда, возьмём цикл и нарисуем умные графики. Видимо, автору намекнули про это, потому что вверху статьи он пишет: "IMPORTANT: Useful feedback revealed that some of these measures are seriously flawed. A major update is on the way."
Причина в том, что точность теряется при сложении больших и маленьких чисел
Если требуется складывать много больших и маленьких чисел, то это большая проблема алгоритма, а не выбора между форматами чисел. Маленькие надо складывать с маленькими, а большие с большими.
Скорее всего, этот код будет работать так же быстро, как и первый, но при этом не будет теряться точность
Можно подобрать числа так, что и этот код будет терять точность. Опят же — надо смотреть алгоримт.
Что в сухом остатке? Сомнительная статья с ошибками и неопределённостями. Читайте лучше классику.
Я что-то говорил про SSE? SIMD это отдельная тема для разговоров. Выше уже упомянули, что на GPU вообще всё по-другому. В этом-то как раз и проблема статьи — всё в кучу, без привязки к конкретным алгоритмам и устройствам.
UPD: А, я понял. Вас смутил "хорошо подогнанный конвейер с использованием SIMD". Я же хотел сказать, что нельзя в кучу смешивать FPU, SIMD и разные процессорные архитектуры.
Поэтому, собственно, нормальные компиляторы и не генерируют кода с инструкциями x87, если без этого можно обойтись.Только в 64-битном режиме. В x86-64 SSE2 (как минимум) присутствует всегда и используется, в частности, для передачи параметров (не
long double
) в функции. В 32-битных легаси системах по прежнему используется в разы более медленный FPU — но кому они сейчас интересны?Я же хотел сказать, что нельзя в кучу смешивать FPU, SIMD и разные процессорные архитектуры.Разные архитектуры смешиваете вы.
Рассмотрим простейшую программу:
#include <iostream>
FLOAT __attribute__((noinline)) incld(FLOAT x) {
return x + 1;
}
int main() {
FLOAT x = 0;
for (long int i=0;i<1000000000;i++) {
x = incld(x);
}
std::cout << x << std::endl;
}
И запустим её:
$ g++ -O3 -DFLOAT="float" test.cc $ ./test 1.67772e+07 0m02.25s real 0m02.19s user 0m00.00s system $ g++ -O3 -DFLOAT=double test.cc -o test $ ./test 1e+09 0m02.22s real 0m02.19s user 0m00.00s system $ g++ -O3 -DFLOAT="long double" test.cc -o test $ ./test 1e+09 0m10.60s real 0m10.41s user 0m00.00s system $ cat /proc/cpuinfo | grep model model : 90 model name : Intel(R) Atom(TM) CPU Z3560 @ 1.00GHz model : 90 model name : Intel(R) Atom(TM) CPU Z3560 @ 1.00GHz model : 90 model name : Intel(R) Atom(TM) CPU Z3560 @ 1.00GHz model : 90 model name : Intel(R) Atom(TM) CPU Z3560 @ 1.00GHz $ gcc --version gcc (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4 Copyright (C) 2013 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Как видим 80-битная арифметика сасем-сасем чут-чут процесс замедляет. В четыре-пять раз всего-навсего. Если для вас это неважно — можете использовать 80-битную арифметику, кто ж вам запретит?
P.S. Для справки: в современных операционках FPU уже давно никто не использует и реализовано оно во многих современных процессорах «на отвяжись». Отсюда такие результаты. Если уж вы хотите кого-то критиковать — то стоило бы самому «изучить матчасть».
Во-первых, такими циклами производительности никто не меряет. Во-вторых, я действительно не знал, что x87 не используется сейчас, но моя критика почти никак не затрагивает этот факт. Я просто привёл конкретный пример, где выбор между float и double никак не влияет на производительность. Можно привести пример другой полярности, и моя позиция будет ровно такая же.
Во-первых, такими циклами производительности никто не меряет.А какими меряет?
Я просто привёл конкретный пример, где выбор между float и double никак не влияет на производительность.А давайте на ваш комментарий посмотрим, а?
Так что же авторы статьи напутали? Скрыли от читателей «необычайно важный факт», который так же интересен в современном мире, как какая-нибудь PDP-10 с 36-битными и 72-битными числами?Если у вас хорошо подогнанный конвейер с использованием SIMD, то вы сможете удвоить производительность FLOPS, заменив double на float. Если нет, то разница может быть гораздо меньше, но сильно зависит от вашего CPU. На процессоре Intel Haswell разница между float и double маленькая, а на ARM Cortex-A9 разница большая.Снова кони и люди. Ни слова о том, что, например, у Intel в FPU всегда используется 80-bit precision, а результат потом просто округляется до нужного.
Можно привести пример другой полярности, и моя позиция будет ровно такая же.Ваша позиция, как я понял, заключается в том, что статьи, позволяющие за полчаса сделать выбор в 90% случаев «никому не нужны», а нужны фундаментальные статьи, которые позволяют сделать такой выбор правильно в 99% случаев, но требуют на изучение месяц.
Но это — не очень конструктивно. Разработчику какой-нибудь MMORPG просто никто не даст изучать месяц фундаментальную литературу — ему нужно принять решение «здесь и сейчас»… ну хорошо — может до завтра есть ещё время подумать…
Насколько синтетичность теста влияет — вопрос более серьёзный — тут надо таки взять что-то образцовое (ну, linpack, наверно, overkill, но что-то в эту сторону).
Насколько синтетичность теста влияет — вопрос более серьёзный — тут надо таки взять что-то образцовое (ну, linpack, наверно, overkill, но что-то в эту сторону).Как раз изучать явление лучше на простых, синтетических тестах. Вот оценивать насколько ваше ускорение какой-нибудь мелочи влияет на реальную программу — вот тут да, linpack, SPEC CPU и прочие.
Здесь ускорение достигнуто исключительно за счёт параллелизации.Вы это сейчас серьёзно?
В этом легко убедиться, если посмотреть ассемблерный код.Ну если бы там был векторизованный цикл, то да. Но у вас тут, извините, Аристотелева муха получилась. Вы реально это сделать не пробовали? Попробуйте! Hint:
__attribute__((noinline))
там не зря стоит, ох не зря.В тех алгоритмах, где автоматическое распараллеливание затруднено или невозможно, такого выигрыша даже и близко не будет.Как раз будет. Может и больше будет, та как тут на каждое вычисление вызов функции всунут.
Ну я его тоже понял так, что SIMD это включая GPU.
> Как видим 80-битная арифметика сасем-сасем чут-чут процесс замедляет. В четыре-пять раз всего-навсего.
В вашем тесте основная проблема в конвенции вызова. Компиляторы (gcc, clang) отказываются инлайнить эту incld(). single, double идут только через xmm регистры, а long double — передачи fp в обе стороны через стек. Это и даёт основное замедление. Ваше явное noinline тут резко нечестно — портит результаты именно на вызовах.
Вот FreeBSD 10.3, процессор Intel i3-4170 (Haswell), компилятор clang 4.2; noinline я явно убрал: во всех 9 комбинациях ('float', 'double', 'long double') * ('-m32 -mfpmath=387', '-m32 -msse -msse2 -mfpmath=sse', '-m64') — время одинаково с точностью до лёгких плаваний — 0.82 секунды. Это означает, что в плотных вычислениях разницы между FPU и SSE разницы нет.
Что именно тормозит процессор тут в случае передачи через стек (при вашем noinline) — надо копать глубже. Но в варианте с SSE в 32 битах, тест с long double быстрее в 2 раза чем с double (!) — видимо, настолько дорогие у него движения типа «записать из FPU, прочитать для SSE». Что показывает ещё одну сторону некорректности теста.
Для полезного теста таки надо брать реальную задачу (какие-нибудь СЛАУ или Рунге-Кутты) и сравнивать на ней. Вся эта синтетика не годится ни к чёрту. Вернусь в город — попробую.
1) иногда параллелятся в конвейерах (команды FPU не параллелится из-за стековой архитектуры),
2) чуть более эффективный код из-за возможности произвольного доступа к регистрам.
Также где-то в документации Intel встречалось упоминание, что и SSE, и FPU используют один и тоже мат.процессор (архитектурно).
Я в своё время делал кучу таких сравнений непосредственно на ассемблере, и результат тот же — идентичны.«В своё время» — это как раз ключевое. «В своё время» программы были 32-битными и использовали FPU и в хвост и в гриву. Потому скорость его работы была такая же (и иногда и быстрее), чем у SSE.
А вот на Atom'ах и Bobcat'ах — да, FPU уже начали резать. Интересно узнать что там на Ryzen'е — не удивлюсь, что у него FPU уже тоже работает в режиме «заглушки».
Также где-то в документации Intel встречалось упоминание, что и SSE, и FPU используют один и тоже мат.процессор (архитектурно).В каком-нибудь Pentium4 — возможно, в современных процессорах — нет. Именно вследствии векторизации. AVX требует кучи ALU — и их глупо делать 80-битными, если только один из восьми будет использоваться в таком режиме. Потому на процессорах с поддержкой AVX FPU — это уже отдельный модуль, а дальше возникает вопрос: а нафига ему быть таким большим и сложным, если он, по факту, никем и ни для чего не используется?
Если это «уже» относится к тому, что они они планшетные — ok, соглашусь (таких зверей под рукой удобно нет). Если к развитию архитектуры чего-то уровня хотя бы настольника — не пойдёт, в тех объёмах FPU уже ничего существенно не значит.
> Интересно узнать что там на Ryzen'е — не удивлюсь, что у него FPU уже тоже работает в режиме «заглушки».
Скоро у нас, похоже, такие будут, проверю. Но — заранее полагаю, что в нём не будут делать такую глупость. AMD всегда заботилась об FPU лучше, чем Intel.
Например, для SandyBridge там сказано, что все умножения (кроме чисто целочисленных) и деления в SSE поступают в порт 0, а сложения плавучих и целочисленные умножения — в порт 1 (таблица 2-14).
Видимо, Intel имеет несколько отличное от Вашего мнение, что можно публиковать, они не боятся давать такие подробности ;)
И если посмотреть на что-нибудь свежее, типа Skylake (схема 2-1 приведённого Вами документа), то там вообще не говорится про x87 и MMX. Что может говорить о том, что обработка этих инструкций вытащена из общего пайплайна и лежит где-то сбоку. Это, как вы понимаете, не способствует производительности этих инструкций.
В общем случае — да. Но с учётом обычной прямой логики (насколько она применима к Intel) и сложности такого блока — это уже ближе к конспирологии.
Не буду дальше вглубляться — у нас ни у кого точных данных нет — закроем на этом.
> И если посмотреть на что-нибудь свежее, типа Skylake (схема 2-1 приведённого Вами документа), то там вообще не говорится про x87 и MMX.
Skylake у меня есть, мерил — в соседнем комментарии. Максимум найденной разницы — около 1.4 раза. Могли усложнить их выборку — факт. Но это ещё даже не разы. Посмотрим на следующие версии…
Assembly/Compiler Coding Rule 63. (M impact, M generality) Use Streaming SIMD Extensions 2 or Streaming SIMD Extensions unless you need an x87 feature. Most SSE2 arithmetic operations have shorter latency then their X87 counterpart and they eliminate the overhead associated with the management of the X87 register stack.
Latency — да, там в таблицах видно, что обычно на такт-два больше.
Ещё они могли тут иметь в виду проблемы с денормализованными. SSE тормозит на заметно меньшем количестве случаев с денормализованными, чем FPU; вообще же это дикий позор, что за 30+ лет с KCS реализации это не исправили.
Но в среднем это всё-таки даже не разы.
Денормализованные — это не ужас, и не повод тратить тысячи тактов на ерунду. Алгоритмы для этого отработаны уже годами, максимум потерь в аппаратной реализации — два такта на результат. И если у SSE, сделанного через 20 лет после FPU, есть проблема с денормализованными — это значит, что внутри там взята всё та же тупая реализация времён судорожного клепания K-C-S.
А то, что Вы видите флаг справляться с ними в случае SSE, и не видите в случае FPU — это уже фирменный стиль Intel, который просто отказался помочь пользователям FPU, с мотивацией типа «нефиг, пусть быстрее сбегают на SSE».
И да, я нигде не говорил о том, что x87 в разы медленнее, не надо мне приписывать чужих слов. Я говорил, что он просто медленнее, вы и сами это признали.
OK, у Вас было «весьма медленнее». Это эмоциональная оценка, но по-моему таки «весьма» это «не менее чем в разы» (иначе это несущественно по сравнению с прочими обстоятельствами).
> Я говорил, что он просто медленнее, вы и сами это признали.
Более точно — что уже начался период, что он хоть и немного, но медленнее.
Далее khim@ настаивает, что это будет нарастать, а я неизбежно соглашаюсь ;(
x87 был отдельным сопроцессором ( физически отдельная микросхема ) без стека там, вероятно, сложно было…
Тут всё-таки идеологические соображения. Но непонятно, какие именно.
Не стану настаивать, но насколько помню была проблема в т.ч. с количеством ног. 8086 у меня не было но на z80 их уже не хватало, и в т.ч. поэтому было мультиплекирование (И ИМХО существовал 8088, по крайней мере это было бы логичным поводом для его выпуска)
В итоге, т.к. два чипа висели на одном потоке команд нужно было как-то данные сохранять в сопроцессоре для обработке (там же ещё операции долгие и основной поток команд мог выполнятся параллельно на основном процессоре).
После выхода 486 такое поведение стало ненужным но осталось, а потом intel решил постепенно заменять сопроцессор на наборы инструкций для основного ядра mmx и т.п. в порчдке их востребованности и возможностей по реализации.
Передача в фиксированный регистр и обратно всё равно быстрее, и это делалось хотя бы для управляющего регистра.
Всё равно непонятно…
Не пойму, почему быстрее для разных чипов?
Смотрю control и status они грузятся не из cpu а по адресу, т.е. никакой прямой загрузки из cpu?
А вот прямая загрузка в fpu данных без использования cpu вроде как вполне возможна. Пока их не сделали на одном кристалле, такое решение как раз повышало производительность на мой взгляд.
Вы не о том. Я всё время говорю о преимуществе прямой адресации fp регистров, а не через стек, а вы — о проблемах двух чипов и общения с памятью.
FPU и так читает из памяти и команды, и данные, и пишет в основном в память (не в CPU). Ну читали бы в fp2 напрямую, а не на вершину стека — то же самое, но проще.
А вот лучше ли давать сопроцессору общаться с памятью напрямую или через CPU — вопрос уже другой. В MIPS, ARM и т.п. общаются только через CPU. Но я не знаю их мотивации в этом случае, да и делалось это всё на 10+ лет позже.
1. Так называемая польская нотация вроде (последовательное вычисление выражений)
2. Простота реализации (в операции участвуют два регистра, схематически не нужно городить выбор нужных регистров, такжĥе упрощается набор команд).
На тот момент времени думаю данное решение было оптимальным, это потом внутренняя частота процессора стала выше частоты памяти, операции стали выполнятся за один такт и в процессорах появились конвейерные cache-ы
Я склоняюсь к идее, что они пытались сделать разработку с точки зрения удобства компиляции. В то время (70-е годы) превращать код в польскую запись и исполнять её такой было типичным решением (см. P-code Паскаля, язык Forth, машины вроде Эльбрус-1,2, и тому подобное). Они знали стоимость подобного решения — заметное замедление по сравнению с чисто регистровым решением — и считали, что могут себе его позволить в случае FP операций. Гугление времён работы 8087 показывает, например, времена от 70 тактов на несчастый FADD (там что, внутри микрокомандный исполнитель был???). По сравнению с этим, цена манипуляций со стеком ничтожна.
А вот в следующем десятилетии наука заметно продвинулась, дав Static Single Assignment — который сейчас. Это было дороже (транслятор с ним мог не влезть в возможности 8086), но перевело всю логику на прямую работу с регистрами; с ней, наоборот, со стеком стало значительно сложнее работать. Поэтому последующие разработки (MMX, SSE) уходили от стека, и сейчас его сохраняют только в коде для разных VM, где удобно раскладывать из него на регистры без регенерации SSA. Ну а когда логику FPU ускорили до такой степени, что она стала чуть ли не быстрее целочисленной (как у AMD времён K5), а с OoO пришло переименование регистров, это дало окончательный приговор стеку…
Но основной 8086 всё-таки был сделан в классическом регистровом стиле.Основной 8086 был сделан так, чтобы на него можно было переносить ассемблерные программы с 8080 и Z80. Тут стековая архитектура — никаким боком. Хотите узнать что тогда Intel воротил, разрабатывая «совершенно новую» архитектуру — почитайте про iAPX 432.
там что, внутри микрокомандный исполнитель был???микрокомандный исполнитель в 45000 транизисторов не впихнёшь. Но и много чего другого не впихнешь тоже…
А в чем, собственно, проблема впихнуть микрокомандный исполнитель в 45000 транзисторов? Он же экономит транзисторы ценой потери времени, а не наоборот.
Вы с прямым углом не перепутали? Микрокомандный исполнитель как раз можно даже в несколько сотен транзисторов впихнуть, в зависимости от размера логического блока и самой команды. И это практически везде считается методом упрощения дизайна, ценой замедления.
> Хотите узнать что тогда Intel воротил, разрабатывая «совершенно новую» архитектуру — почитайте про iAPX 432.
Спасибо, в курсе. У Intel было несколько таких суперзакидонов, и притом очень интересных. А почему в кавычках?
Вот FreeBSD 10.3, процессор Intel i3-4170 (Haswell)Выкидываем. На Haswell'е ещё приличный FPU.
Но в варианте с SSE в 32 битах, тест с long double быстрее в 2 раза чем с double (!) — видимо, настолько дорогие у него движения типа «записать из FPU, прочитать для SSE».Да, пересылки между FPU и SSE весьма и весьма дороги. Это отдельные модули. Но мой тест был не об этом.
Для полезного теста таки надо брать реальную задачу (какие-нибудь СЛАУ или Рунге-Кутты) и сравнивать на ней.В реальной задаче как раз «затыки» сложнее увидеть. Хотя для «оценки масштабов бедствия» — да, нужна реальная задача…
По сравнению с чем?
Если с супермелкими изделиями вроде Atom — ok, может быть (приму пока сам не померю).
Если с более новыми поколениями — «меня терзают смутные сомнения» (tm)
Вот есть лаптоп с «Intel® Pentium® CPU 4405U @ 2.10GHz» (Skylake, но урезанный до мобильной версии, SSE весь есть, AVX отсутсвует).
Решение СЛАУ по Гауссу, прямолинейное; матрица 1000*1000 случайных чисел; Ubuntu 16.04; gcc5; -O3.
32 бита, FPU, double: 3.6 сек.
32 бита, FPU, long double: 5.5 сек (явно, затраты на cache misses)
32 бита, SSE, double: 3.2 сек. (код показывает частичную векторизацию)
64 бита, SSE, double: 2.3 сек.
64 бита, FPU, long double: 4.6 сек.
Как-то всё равно не укладывается в Вашу концепцию «FPU стал в разы медленнее», максимум подтверждаемого именно для отношения SSE/FPU это где-то 1.4 раза, при тщательном исследовании наверняка будет ещё меньше.
И я уверен, что для всех уровней процессоров от хорошего лаптопа и выше это сохранится ещё долго.
Я уверен, имелись в виду координаты в виде «50.44467/35.21212», как на maps-сайтах.
Тут я не уверен, но что-то мне подсказывает, что координаты GPS имеют какой-то фиксированные формат. Или автор решил, что как можно сделать, как в той на картинке из CSI?
Под "координатами GPS" обычно понимают географические координаты в системе координат, используемой в GPS. Насколько я понимаю, это должна быть какая-нибудь американская система координат, но я могу ошибаться.
Как и любые другие координаты, они могут быть измерены с разной точностью; эта точность зависит от возможностей измерительного оборудования, от метода измерения и от размеров объекта.
Но такой большой и сложный тип нужно применять осмысленно, а не из соображений «на всякий случай, пусть будет».
А что вы предлагаете использовать вместо float?
Четырёхбайтовые вещественные числа – реликт времён, когда память была ограничена 64 килобайтами.
В научных расчётах half precision точно не имеет никакого применения.
Только судя по архитектурной документации, там уже не мобильные фичи, а вполне себе серверные, как то RAS, виртуализация и прочие.
Я, конечно, не специалист в вычислениях, но в интернетах пишут, что half precision арифметика вполне неплохо показывает себя в задачах, связанных с deep learning. Так что, может быть не стоит считать, что нужны только double precision?
Это не относится к параллельным преобразованиям из матрицы в матрицу. Но матрица из миллиарда чисел не может непосредственно восприниматься человеком и потому не является конечным результатом.
Но матрица из миллиарда чисел не может непосредственно восприниматься человеком и потому не является конечным результатом.Это кто вам такую чушь сказал? DCI 4K, 60FPS, три компоненты изображения — вот вам уже больше полутора миллиардов чисел.
Или вы хотите сказать, что человек неспособен видеоизображение воспринимать?
При погрешности полбита на операцию, мы получаем в результате потерю точности не менее полумиллиарда цены младшего разряда, или 9 десятичных цифр, что уже превосходит всю разрядную сетку float.Рекомендую вам на досуге почитать про нейронные сети. Узнаете для себя много нового. В частности узнаете как можно с пользой использовать миллиард чисел так, чтобы во время практического использования алгоритма между собой сранивался десяток или около того.
Чтобы получить из анализа всех этих данных какой-то практический результат, мы должны произвести, как минимум, миллиард последовательных операций над нимиЗачем? Подумайте о компьютере, работающем на частоте 500 герц и порождающем ответ за 1/10 секунды, опираясь на матрицу из миллиардов чисел! Довольно очевидно, что этот компьютер не производит миллиардов последовательных вычислений — что не мешает ему быть весьма и весьма полезным в разных случаях… Или вы не верите в существование такого?
Посмотрите в зеркало!
Видеоизображение состоит из целых чисел, точность которых может не уменьшаться при операциях с ними.Почитайте хотя бы википедию, если ни на что другое времени нет: реальные сцены часто имеют динамический диапазон яркости в 1 000 000:1 и выше, при этом и в тенях и в свете глаз способен (из-за световой адаптации к яркости) различить детали
«из-за световой адаптации к яркости» — это в чистом виде плавучка и есть. При этом ни о каких double'ах или даже float'ах и речи не идёт.
То же самое с программной памятью компьютера, которая, к тому же, используется далеко не полностью каждую секунду, а очень выборочно. То же с нейронной сетью.И, тем не менее, на протяжении столетий только этот компьютер и использовался в научных вычислениях. А теперь вы вдруг говорите, что такого в принципе не может быть.
Hint: наука, как бы, весьма многообразна и при каком-нибудь анализа ДНК могут применяться самые разнообразные алгоритмы, совершенно не сводящиеся к любимым вами манипуляциям над матрицами миллиард на миллиард.
Кадр DCI 4K – это просто 8 миллионов целых чисел. Как целые числа, они не требуют вещественной арифметики. Динамический диапазон такого кадра равен динамическому диапазону устройства отображения, независимо от того, какие там использованы сцены.
Если речь идёт о мозге, то он не обрабатывает одновременно очень яркие и очень тёмные области. Он сначала смотрит на яркую область с расширенным зрачком, её распознаёт и запоминает результат, а потом смотрит на светлую область с суженным зрачком, её распознаёт и тоже запоминает результат. Потом умозрительно сводит эти два распознанных результата (уже имеющих очень малый информационный объём) вместе.
Кадр DCI 4K – это просто 8 миллионов целых чисел.Да — но это результат. Чтобы его получить вам вполне может потребоваться обрабатывать совершенно разные вещи, при этом вы не будете знать — какая часть попадёт в конечное изображение.
Кто тут говорил что точность для промежуточных представлений нужна большая, чем для результата? Вот HDR — это то же самое, только с динамическим диапазоном. Если в одном кадре у вас ярко светит солнце, а в следующем — оно чем-то загорожено, то вам потребуется резко «перескочить» в другой динамический диапазон.
Безусловно, всё можно сделать с огромным диапазоном, если считать тупо в лоб. Но я-то говорю как раз о том, что это низачем не нужно для потребного человеку скромного результата.
Если это реальная видеосъёмка, то там такая же ограниченная в разрядности КМОП-матрица на входе.Во-первых может быть не одна. Во-вторых — можно синтезировать из нескольких снимков изображение. В третьих — его можно рассчитать.
А если анимация, то ей незачем иметь солнце реальной огромной яркости.С чего вы взяли? Многие вполне себе наблюдаемые эффекты без этого нормально не рассчитать.
Но я-то говорю как раз о том, что это низачем не нужно для потребного человеку скромного результата.В том-то и дело, что нужно. Результаты, полученные с применением HDR заметно оличаются от тех, где HDR не применяется.
Можно, конечно, упираться и говорить что
Но так как это цепочка логических операций, а не вещественных (с точки зрения работы самого компьютера), то она выполняется без погрешности.Так можно дойти до того, чтобы обьявить что никаких чисел с плавающей точкой в природе вообще нет — это ж фикция! Там просто два целых числа, фигли мы вокруг этого какие-то теории разводим?
Есть электрохимические потенциалы нейронов, имеющие, впрочем, очень высокую точность по сравнению с точностью результата.Ну то есть всё точно так же, как с алгоритмами, которые используют числа с половинной точностью. И которые вы обьявили заочно «никому не нужной фигнёй».
Но это не то, что принято называть научными расчётами.Серьёзно? Методы Монте-Карло уже обьявлены лже-наукой? И выделение генов с помощью нейронных сетей — тоже?
Очень узкая у вас получается наука: подавляющее большинство вычислений, имеющих практический «выхлоп» окажется «за бортом».
Не любые операции накапливают погрешность одинаково! К примеру, если построить дерево из операций вида z = sqrt(x+y)
— то сложение погрешностей компенсируется квадратным корнем, и погрешность результата будет зависеть лишь от погрешностей самих операций вычисления квадратного корня! То есть относительная погрешность результата будет пропорциональна лишь логарифму объема исходных данных.
Пример, конечно же, синтетический — мне неизвестен смысл таких вычислений. Тем не менее, он опровергает тезис об обязательном накоплении погрешности.
Более реальные примеры связаны с теми же нейросетями. При прямом распространении сигнала каждый нейрон применяет к нему нелинейную функцию, которая "стягивает" значения к крайним точкам (0 и 1 или -1 и 1). Точно так же как и в примере с квадратным корнем —
В тех же нейросетях при прямом распространении сигнала каждый нейрон совершает нелинейное преобразование, которое "стягивает" значения к крайним точкам (0 и 1 или -1 и 1). Погрешности при таком распространении сигнала не накапливаются так же, как и в примере с корнем.
При обучении сети, конечно же, все получается сложнее. Но обучение — процесс итеративный, если что было сделано неправильно на первых итерациях — то будет исправлено на последующих.
Отличный план, Уолтер, если я понимаю. Одни знакомые пацаны именно так и поступили, по всему проекту использовали double. Жаль только, что потом оказалось, что этот проект на микроконтроллере xmega128, а sizeof( double ) внезапно 4. И, похоже, пониженная точность float там таки дала о себе знать в паре мест.
А не надо язык Си использовать для расчётов.
Там был С++11, кстати. Но это не отменяет того, что некоторая лажа произошла.
Лажа здесь в том, что хоть в Си, хоть в Двух Крестах, описание типа не означает ничего конкретного.
Вопрос же в разрядности, а не в том, как она описана в программе.
У чотких пацанчиков это называется real(8).
Любое другое представление вещественных чисел - ошибка природы.
Просто нужно более внимательно читать чего написано про платформу для которой пишешь. Например там int 16-битный, что они знали и учитывали. Про маленький double тоже знали, но не подумали, что это может повлиять.
Одинарная или двойная точность?