Pull to refresh

Comments 114

Какой-то субурный набор наблюдений про числа с плавающей точкой.

Самые большие относительные ошибки внезапно лезут не при сложении, а при вычитании близких значений.
3.1415 — 3.1414 -> В результате этой операции в мантиссе младшие 4 знака заполнены нулями, т.е. мусором.
На импортном языке называется catastrophic cancellation.

В общем и среднем по архитектурам, можно ожидать, что векторизованный код должен отличатся float vs. double по производительности в 2x для арифметических операций ±/*. С делилкой уже не так, с корнями, экспонентами и прочими синусами разрыв больше.

В выводах написано — много складываете, то складывайте в даблах. А что делать, если я умножаю много — float нормально?
Возьмите сначала логарифм, тогда задача сведется к складыванию :)

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


С третьей стороны, вполне возможно, что компилятор все равно все оптимизирует так, чтобы промежуточных результатов использовались регистры FPU (80 бит).

С третьей стороны, вполне возможно, что компилятор все равно все оптимизирует так, чтобы промежуточных результатов использовались регистры FPU (80 бит).
В современном мире FPU стараются не использовать без необходимости. Все рекомендации и от AMD и от Intel'а рекомендуют его избегать.
long long в примере вместо size_t ранит сердца опытных разработчиков (особенно знающих, что такое n32).
Кроме того всегда нужно оценивать необходимую разрядность мантиссы вместо тупого использования 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 в данных или именно младшие разряды.
Жаль, что не раскрыта тема вычислений на GPU, ведь именно там наблюдается наибольшая потеря скорости при переключении с float на double (например, для GeForce 900 series разница будет в 32 раза).
Обычно расчёты ведут на x86 (IA-32, AMD64). Соответственно полезно знать про внутреннее представление чисел (Extended precision). Кроме 80 бит добавляются новые виды нечисел (не-чисел).
Вместо разрядности чисел можно выбирать другие алгоритмы расчёта (с улучшениями в сохранении точности).
Также: округлением чисел можно управлять.
Насколько понимаю, результат расчётов может зависеть от использования обычного FPU с обычными числами или использования SIMD (SSE, AVX): в первом случае внутреннее представление будет 80 бит, во втором — 64 или 32.
И
https://habrahabr.ru/post/112953/#comment_3623058
https://habrahabr.ru/post/112953/#comment_8730233
80 бит только тогда, когда вас совершенно не интересует производительность решения, но есть ощущение, что ещё чуть-чуть и хватит точности. По большому счёту, в 2017 году можно смело рекомендовать не смотреть на 80 бит никогда, если только это не жуткое легаси.
Если даблов не хватает, а производительность не интересует, то есть библитеки для вычислений произвольной точность, типа GNU MPFR.
Если даблов не хватает, но производительность важна — меняйте алгоритм.
Инструкции из набора x87 это legacy, в процессорах эти инструкции работают весьма медленно. И если нет требований именно к точности вычислений, лучше про них вообще забыть. Но выполнять точные вычисления на x87 это так себе занятие. В документации описаны ситуации, когда младший бит мантиссы результата принимает непредсказуемое значение. К сожалению, прямо сейчас я не смогу предоставить ссылку на конкретный раздел документации, но если интересно, завтра могу уточнить.
> Инструкции из набора x87 это legacy, в процессорах эти инструкции работают весьма медленно.

На десктопных и серверных процессорах это пока что не так. (Коллега 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. Но сейчас и его помеху снизили до минимума.
Относительно неточных вычислений, можно прочитать в «Intel® 64 and IA-32 Architectures Software Developer’s Manual», том 1, раздел 8.3.10, «Transcendental Instruction Accuracy»:

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 дают разные результаты в младшем бите.
Более того — это реально создаёт проблемы. У меня есть знакомые, которые исследовали как белок сворачивается. И вот это вот свойство выпило им много крови при написаниях тестов.

Понятно что получались два примерно разных результата. Понятно что «в природе» белок мог свернуться туда и сюда в таких случаях. Но когда на Intel'е он сворачивается в одну сторону, а на AMD — в другую… это просто усложняет отладку, вот и всё… но всё равно выливается в серьёзные затраты.
Если на разных FPU белок сворачивается в разные стороны, значит, вычисления очень чувствительны к погрешностям такого порядка (~1ulp), а это уже признак, что им надо или повышать точность значений (double недостаточно), или менять модель на более устойчивую. При устойчивой модели такие различия могут сбить младшие разряды (нарушение духа IEEE754 — результаты должны быть воспроизводимы на любом железе), но не привести к принципиально различным результатам.
Если на разных 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. Но важно ли это, с учётом предыдущего абзаца?
Сам себе поправлю (сбили с толку этим «необходима точность 0.5 ulp или лучше») — не «необходима точность», а «получается точность». Необходимая по IEEE754 — идеальная точность, но с округлением.
Непредсказуемым от этого результат в стиле FPU не становится, но нестандартным — вполне может.
На самом деле речь идёт просто о том, что машинный результат операции не обязательно является ближайшим представимым приближением к идеальному результату. Но при этом если идеальный1 > идеальный2, то и машинный1 >= машинный2.
Смещение может быть, а может и не быть, в зависимости от конкретной реализации FPU, и нигде это не описано точно для конкретной модели FPU. Это и называется «непредсказуемо». Другими словами, если в документации нет описания, позволяющего гарантированно точно рассчитать значение младшего бита в регистре после выполнения операции, значение этого бита в результате операции является непредсказуемым. То есть на конкретное поведение нельзя закладываться. Завтра Вы купите новый процессор, и в нём FPU будет реализован по-другому.
Нет, естественно, реальное железо это конечный автомат, в котором полностью непредсказуемого результата не будет — результат будет рассчитан по каким-то правилам. Но покуда эти правила не определены в архитектурной документации, результат называется непредсказуемым, на него нельзя закладываться пользователям, и это даёт RTL-дизайнерам пространство для оптимизации железа.
Чтобы быть более конкретным, в данном случае я исхожу из позиции Design Validation, когда процессорные инструкции оцениваются с точки зрения «для вот таких значений регистров на входе должно получиться строго конкретное значение результата, путём выполнения преобразований, описанных документацией».
> Завтра Вы купите новый процессор, и в нём FPU будет реализован по-другому.

С этим безусловно согласен. Но термин «непредсказуемый» мне тут откровенно не нравится, потому что слишком сильно намекает на варианты типа «сразу повторили ту же операцию, а результат другой».
Считаю тогда, что разногласий тут нет.
Под определение «дважды подряд выполнили операцию на одних и тех же исходных данных — получили разные результаты» скорее подойдёт термин «невоспроизводимый».
Что касается управления точностью: если особая точность не нужна, можно вообще включить режим denormal flush to zero. Работает в этом режим обычно пошустрее. Ну и в 32-битном ARM единственная возможность использовать векторизацию — это использовать single precision арифметику и denormal flush to zero.

А еще не стоит забывать, что на самом деле бинарная арифметика плохо подходит для многих видов вычислений из реальной жизни. Например для финансовых данных. Так как все вычисления должны воплняться по правилам десятичной арифметики, а не двоичной.


Бинарная арифметика хорошо подходит там, где основание системы счисления не имеет значения и не требуется контроль округления младших разрядов. Например почти все виды данных связанных с измерением объектов реального физического мира (цаще всего это научные и инженерные данные). Кстати, все примеры в статье — это как раз такие данные ("Размер комнаты", "Окружность Земли" и т.д.).

В частности поэтому в IEEE 754 2008 года добавили десятичную арифметику. Существую даже железки которые поддерживают десятичную арифметику с плавающей точкой на уровне инструкций.

Увы, поддержка IEEE 754 decimal32/decimal64 очень слабая и на сколько мне известно, на поддержку десятичной арифметики в новом железе все производители забили (поправьте если это не так). Плюс дополнительно проблема осложнаяется тем, что тут требуется некоторая поддержка со стороны языков программирования. А её нет (за редким исключением).

Десятичная плавучка, считаем, работает только у IBM, но сразу на двух их железных платформах — zSeries (S/390) и Power. Зато у них работает сразу с BCD (Chen-Ho) мантиссой.
Есть поддержка в GCC (почему-то только для C), софтово, с двоичной мантиссой.
Десятичная плавучка сама по себе имеет для финансовых целей только одно объективное существенное преимущество: это возможность контроля за корректностью операций через inexact exception. Если в decfloat32 ровно 7 цифр мантиссы, а мы складываем 99999.99+0.02 и получаем 100000.00 (100000.01 округлилось) — нам про это inexact скажет. В двоичном варианте практически любые операции с долями приведут к жуткому зашумлению inexactʼами даже на вполне законных операциях.

А где нет 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% у десятичных).

Вам оппонент говорит про вычисления с фиксированной точкой (которые обычно делаются в целых числах с подразумеваемым порядком), а Вы возражаете про двоичную плавучку. Это совсем другой случай. Те же 0.1 + 0.2 в варианте в сотых долях просто превратились бы в 10+20 и вычислились бы абсолютно точно и беспроблемно.

А вот (частично повторю соседний комментарий) 99999.99 + 0.02 при вычислении в decfloat32 окажутся 100000 вместо верного, но уже не представимого, 100000.01. И только inexact exception может свистнуть вам о проблеме, но кто на него обращает внимание?

Виноват, я упустил, что khim писал про вычисления с фиксированной точкой.


Да, чаще всего именно через нее вычисления с контролируемой точностью и проводятся.

В таких случаях хорошо бы использовать что-то типа numeric(n,m) используемых в БД.

Да, но при жёсткой дисциплине на их использование. Например, сложение-вычитание с разным m недопустимо; при умножении и делении надо уточнять m результата; присвоение с другим m тоже недопустимо без явной конверсии с указанием правила округления… В сумме таких правил накапливается столько, что часто легче явно вести в целых, а масштаб (то самое 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."


Причина в том, что точность теряется при сложении больших и маленьких чисел

Если требуется складывать много больших и маленьких чисел, то это большая проблема алгоритма, а не выбора между форматами чисел. Маленькие надо складывать с маленькими, а большие с большими.


Скорее всего, этот код будет работать так же быстро, как и первый, но при этом не будет теряться точность

Можно подобрать числа так, что и этот код будет терять точность. Опят же — надо смотреть алгоримт.


Что в сухом остатке? Сомнительная статья с ошибками и неопределённостями. Читайте лучше классику.

В Intel 80-битная арифметика — это legacy 1980 года издания. Если процессор поддерживает наборы инструкций SSE и SSE2 (вообще все процессоры Intel начиная с Pentium 4), single и double precision floating point там считается гораздо быстрее, чем в формате 80bit x87. И расчёты в SSE/SSE2 происходят именно в 32-битном и 64-битном форматах. Почитайте что ли архитектурную документацию на досуге.

Я что-то говорил про SSE? SIMD это отдельная тема для разговоров. Выше уже упомянули, что на GPU вообще всё по-другому. В этом-то как раз и проблема статьи — всё в кучу, без привязки к конкретным алгоритмам и устройствам.


UPD: А, я понял. Вас смутил "хорошо подогнанный конвейер с использованием SIMD". Я же хотел сказать, что нельзя в кучу смешивать FPU, SIMD и разные процессорные архитектуры.

Насколько я помню, SSE — это не только про SIMD, там есть и скалярные операции. Поэтому, собственно, нормальные компиляторы и не генерируют кода с инструкциями x87, если без этого можно обойтись.
Поэтому, собственно, нормальные компиляторы и не генерируют кода с инструкциями 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 никак не влияет на производительность.
А давайте на ваш комментарий посмотрим, а?

Если у вас хорошо подогнанный конвейер с использованием SIMD, то вы сможете удвоить производительность FLOPS, заменив double на float. Если нет, то разница может быть гораздо меньше, но сильно зависит от вашего CPU. На процессоре Intel Haswell разница между float и double маленькая, а на ARM Cortex-A9 разница большая.
Снова кони и люди. Ни слова о том, что, например, у Intel в FPU всегда используется 80-bit precision, а результат потом просто округляется до нужного.
Так что же авторы статьи напутали? Скрыли от читателей «необычайно важный факт», который так же интересен в современном мире, как какая-нибудь PDP-10 с 36-битными и 72-битными числами?

Можно привести пример другой полярности, и моя позиция будет ровно такая же.
Ваша позиция, как я понял, заключается в том, что статьи, позволяющие за полчаса сделать выбор в 90% случаев «никому не нужны», а нужны фундаментальные статьи, которые позволяют сделать такой выбор правильно в 99% случаев, но требуют на изучение месяц.

Но это — не очень конструктивно. Разработчику какой-нибудь MMORPG просто никто не даст изучать месяц фундаментальную литературу — ему нужно принять решение «здесь и сейчас»… ну хорошо — может до завтра есть ещё время подумать…
Здесь ускорение достигнуто исключительно за счёт параллелизации. В этом легко убедиться, если посмотреть ассемблерный код. В тех алгоритмах, где автоматическое распараллеливание затруднено или невозможно, такого выигрыша даже и близко не будет. В этом также можно убедиться на более сложных и нелинейных алгоритмах.
Параллелизацию можно остановить, если пометить x как volatile (это вообще надо было с самого начала сделать). Но и с этим я вижу 4-кратную разницу (на подручном AMD E1-1200 — так что это не только Intel).

Насколько синтетичность теста влияет — вопрос более серьёзный — тут надо таки взять что-то образцовое (ну, 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». Что показывает ещё одну сторону некорректности теста.

Для полезного теста таки надо брать реальную задачу (какие-нибудь СЛАУ или Рунге-Кутты) и сравнивать на ней. Вся эта синтетика не годится ни к чёрту. Вернусь в город — попробую.
Я в своё время делал кучу таких сравнений непосредственно на ассемблере, и результат тот же — идентичны. Команды SSE (неупакованные) могут давать слегка лучший результат, потому что:
1) иногда параллелятся в конвейерах (команды FPU не параллелится из-за стековой архитектуры),
2) чуть более эффективный код из-за возможности произвольного доступа к регистрам.

Также где-то в документации Intel встречалось упоминание, что и SSE, и FPU используют один и тоже мат.процессор (архитектурно).
Я в своё время делал кучу таких сравнений непосредственно на ассемблере, и результат тот же — идентичны.
«В своё время» — это как раз ключевое. «В своё время» программы были 32-битными и использовали FPU и в хвост и в гриву. Потому скорость его работы была такая же (и иногда и быстрее), чем у SSE.

А вот на Atom'ах и Bobcat'ах — да, FPU уже начали резать. Интересно узнать что там на Ryzen'е — не удивлюсь, что у него FPU уже тоже работает в режиме «заглушки».

Также где-то в документации Intel встречалось упоминание, что и SSE, и FPU используют один и тоже мат.процессор (архитектурно).
В каком-нибудь Pentium4 — возможно, в современных процессорах — нет. Именно вследствии векторизации. AVX требует кучи ALU — и их глупо делать 80-битными, если только один из восьми будет использоваться в таком режиме. Потому на процессорах с поддержкой AVX FPU — это уже отдельный модуль, а дальше возникает вопрос: а нафига ему быть таким большим и сложным, если он, по факту, никем и ни для чего не используется?
> А вот на Atom'ах и Bobcat'ах — да, FPU уже начали резать.

Если это «уже» относится к тому, что они они планшетные — ok, соглашусь (таких зверей под рукой удобно нет). Если к развитию архитектуры чего-то уровня хотя бы настольника — не пойдёт, в тех объёмах FPU уже ничего существенно не значит.

> Интересно узнать что там на Ryzen'е — не удивлюсь, что у него FPU уже тоже работает в режиме «заглушки».

Скоро у нас, похоже, такие будут, проверю. Но — заранее полагаю, что в нём не будут делать такую глупость. AMD всегда заботилась об FPU лучше, чем Intel.
Не может публично доступная документация Intel говорить о том, что SSE и x87 реализованы на одном и том же исполнительном устройстве. Это детали реализации, которые не раскрывает ни один разработчик CPU. Архитектурная документация описывает только то, каким будет результат вычислений, а не то, как он будет достигнут в железе.
Откройте документ «Intel® 64 and IA-32 Architectures Optimization Reference Manual» и убедитесь, что для конкретных семейств процессоров расписано: количество исполнительных портов, какие команды в них поступают в каком случае, какие задержки (в тактах) на вход конкретной команды, как они увеличиваются при специфическом сочетании с предыдущими командами.
Например, для SandyBridge там сказано, что все умножения (кроме чисто целочисленных) и деления в SSE поступают в порт 0, а сложения плавучих и целочисленные умножения — в порт 1 (таблица 2-14).
Видимо, Intel имеет несколько отличное от Вашего мнение, что можно публиковать, они не боятся давать такие подробности ;)
Согласен, Intel публикует некоторые микроархитектурные данные. Только приведённая Вами таблица ничего не говорит об исполнительных устройствах. Она говорит, на какие порты scheduler может отправить микрооперацию конкретной группы. То есть, если x87 и AVX FP обрабатываются портом 0, это не значит, что за это отвечает одна и та же логика.

И если посмотреть на что-нибудь свежее, типа Skylake (схема 2-1 приведённого Вами документа), то там вообще не говорится про x87 и MMX. Что может говорить о том, что обработка этих инструкций вытащена из общего пайплайна и лежит где-то сбоку. Это, как вы понимаете, не способствует производительности этих инструкций.
> То есть, если x87 и AVX FP обрабатываются портом 0, это не значит, что за это отвечает одна и та же логика.

В общем случае — да. Но с учётом обычной прямой логики (насколько она применима к Intel) и сложности такого блока — это уже ближе к конспирологии.
Не буду дальше вглубляться — у нас ни у кого точных данных нет — закроем на этом.

> И если посмотреть на что-нибудь свежее, типа Skylake (схема 2-1 приведённого Вами документа), то там вообще не говорится про x87 и MMX.

Skylake у меня есть, мерил — в соседнем комментарии. Максимум найденной разницы — около 1.4 раза. Могли усложнить их выборку — факт. Но это ещё даже не разы. Посмотрим на следующие версии…
Да, и если уж речь зашла про Optimization reference manual, там тоже написано, что x87 может работать медленнее:
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.
Стек — да, безусловно (какой безумец его ввёл в x87?)
Latency — да, там в таблицах видно, что обычно на такт-два больше.
Ещё они могли тут иметь в виду проблемы с денормализованными. SSE тормозит на заметно меньшем количестве случаев с денормализованными, чем FPU; вообще же это дикий позор, что за 30+ лет с KCS реализации это не исправили.
Но в среднем это всё-таки даже не разы.
На SSE есть ещё способ поднять performance — отказаться от обработки denormal, включив FTZ и DAZ, что заставит все denormal числа интерпретировать как нули. Как я понимаю, в x87 такой возможности нет.
Вот просто потрясающий пример, как можно из одних и тех же данных делать противоположные выводы. ;)

Денормализованные — это не ужас, и не повод тратить тысячи тактов на ерунду. Алгоритмы для этого отработаны уже годами, максимум потерь в аппаратной реализации — два такта на результат. И если у SSE, сделанного через 20 лет после FPU, есть проблема с денормализованными — это значит, что внутри там взята всё та же тупая реализация времён судорожного клепания K-C-S.

А то, что Вы видите флаг справляться с ними в случае SSE, и не видите в случае FPU — это уже фирменный стиль Intel, который просто отказался помочь пользователям FPU, с мотивацией типа «нефиг, пусть быстрее сбегают на SSE».
В том и вопрос: производитель CPU совершенно открыто говорит: x87 это legacy. Не нужно его использовать без крайней необходимости.

И да, я нигде не говорил о том, что x87 в разы медленнее, не надо мне приписывать чужих слов. Я говорил, что он просто медленнее, вы и сами это признали.
> И да, я нигде не говорил о том, что x87 в разы медленнее

OK, у Вас было «весьма медленнее». Это эмоциональная оценка, но по-моему таки «весьма» это «не менее чем в разы» (иначе это несущественно по сравнению с прочими обстоятельствами).

> Я говорил, что он просто медленнее, вы и сами это признали.

Более точно — что уже начался период, что он хоть и немного, но медленнее.
Далее khim@ настаивает, что это будет нарастать, а я неизбежно соглашаюсь ;(
Ну, не знаю, для меня 1.4 раза — это уже «весьма», особенно когда я наблюдаю, как народ борется за лишние 5% перфоманса.
|| какой безумец его ввёл в x87?
x87 был отдельным сопроцессором ( физически отдельная микросхема ) без стека там, вероятно, сложно было…
Control & status передаются в FPU и обратно без всякого стека. Значит, могли, если хотели.
Тут всё-таки идеологические соображения. Но непонятно, какие именно.

Не стану настаивать, но насколько помню была проблема в т.ч. с количеством ног. 8086 у меня не было но на z80 их уже не хватало, и в т.ч. поэтому было мультиплекирование (И ИМХО существовал 8088, по крайней мере это было бы логичным поводом для его выпуска)
В итоге, т.к. два чипа висели на одном потоке команд нужно было как-то данные сохранять в сопроцессоре для обработке (там же ещё операции долгие и основной поток команд мог выполнятся параллельно на основном процессоре).
После выхода 486 такое поведение стало ненужным но осталось, а потом intel решил постепенно заменять сопроцессор на наборы инструкций для основного ядра mmx и т.п. в порчдке их востребованности и возможностей по реализации.

> В итоге, т.к. два чипа висели на одном потоке команд нужно было как-то данные сохранять в сопроцессоре для обработке (там же ещё операции долгие и основной поток команд мог выполнятся параллельно на основном процессоре).

Передача в фиксированный регистр и обратно всё равно быстрее, и это делалось хотя бы для управляющего регистра.
Всё равно непонятно…

Не пойму, почему быстрее для разных чипов?
Смотрю control и status они грузятся не из cpu а по адресу, т.е. никакой прямой загрузки из cpu?
А вот прямая загрузка в fpu данных без использования cpu вроде как вполне возможна. Пока их не сделали на одном кристалле, такое решение как раз повышало производительность на мой взгляд.

> Не пойму, почему быстрее для разных чипов?

Вы не о том. Я всё время говорю о преимуществе прямой адресации fp регистров, а не через стек, а вы — о проблемах двух чипов и общения с памятью.
FPU и так читает из памяти и команды, и данные, и пишет в основном в память (не в CPU). Ну читали бы в fp2 напрямую, а не на вершину стека — то же самое, но проще.

А вот лучше ли давать сопроцессору общаться с памятью напрямую или через CPU — вопрос уже другой. В MIPS, ARM и т.п. общаются только через CPU. Но я не знаю их мотивации в этом случае, да и делалось это всё на 10+ лет позже.
Понял, так ведь у fpu очень узкое применение и стековый подход для вычислений там связан с двумя вещами
1. Так называемая польская нотация вроде (последовательное вычисление выражений)
2. Простота реализации (в операции участвуют два регистра, схематически не нужно городить выбор нужных регистров, такжĥе упрощается набор команд).
На тот момент времени думаю данное решение было оптимальным, это потом внутренняя частота процессора стала выше частоты памяти, операции стали выполнятся за один такт и в процессорах появились конвейерные cache-ы
Идея понятна, но мне кажется всё-таки не адекватной тому времени. Из тех же соображений могли бы сделать такие же подходы к основным операциям; стековая архитектура по сравнению с аккумуляторной даже выигрывала бы во многих случаях, особенно по простоте написания кода. Но основной 8086 всё-таки был сделан в классическом регистровом стиле.

Я склоняюсь к идее, что они пытались сделать разработку с точки зрения удобства компиляции. В то время (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 транзисторов? Он же экономит транзисторы ценой потери времени, а не наоборот.

> микрокомандный исполнитель в 45000 транизисторов не впихнёшь.

Вы с прямым углом не перепутали? Микрокомандный исполнитель как раз можно даже в несколько сотен транзисторов впихнуть, в зависимости от размера логического блока и самой команды. И это практически везде считается методом упрощения дизайна, ценой замедления.

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

Спасибо, в курсе. У Intel было несколько таких суперзакидонов, и притом очень интересных. А почему в кавычках?
Вот FreeBSD 10.3, процессор Intel i3-4170 (Haswell)
Выкидываем. На Haswell'е ещё приличный FPU.

Но в варианте с SSE в 32 битах, тест с long double быстрее в 2 раза чем с double (!) — видимо, настолько дорогие у него движения типа «записать из FPU, прочитать для SSE».
Да, пересылки между FPU и SSE весьма и весьма дороги. Это отдельные модули. Но мой тест был не об этом.

Для полезного теста таки надо брать реальную задачу (какие-нибудь СЛАУ или Рунге-Кутты) и сравнивать на ней.
В реальной задаче как раз «затыки» сложнее увидеть. Хотя для «оценки масштабов бедствия» — да, нужна реальная задача…
> Выкидываем. На Haswell'е ещё приличный FPU.

По сравнению с чем?
Если с супермелкими изделиями вроде 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 раза, при тщательном исследовании наверняка будет ещё меньше.

И я уверен, что для всех уровней процессоров от хорошего лаптопа и выше это сохранится ещё долго.
> Тут я не уверен, но что-то мне подсказывает, что координаты GPS имеют какой-то фиксированные формат. Или автор решил, что как можно сделать, как в той на картинке из CSI?

Я уверен, имелись в виду координаты в виде «50.44467/35.21212», как на maps-сайтах.
Там всё ещё веселее. GPS — это ж не машинка из которой координаты вылезают. Из железки только время до спутника получается, остальное — нужно считать. Триангуляция, поправки разные, вот это вот всё.
Тут я не уверен, но что-то мне подсказывает, что координаты GPS имеют какой-то фиксированные формат. Или автор решил, что как можно сделать, как в той на картинке из CSI?

Под "координатами GPS" обычно понимают географические координаты в системе координат, используемой в GPS. Насколько я понимаю, это должна быть какая-нибудь американская система координат, но я могу ошибаться.


Как и любые другие координаты, они могут быть измерены с разной точностью; эта точность зависит от возможностей измерительного оборудования, от метода измерения и от размеров объекта.

За пределы точности double выйти намного проще, чем кажется. Например, аппроксимацией методом наименьших квадратов достаточно большого количества точек (несколько тысяч). При использовании трансцендентных функций погрешности растут ещё быстрее, и с ними можно столкнуться и без всяких накопительных вычислений — просто при вычислении сложной формулы.
Вот тут — вы правы, конечно. Именно за счёт __float128 POWER'ы до сих пор из Top500 не пропали.

Но такой большой и сложный тип нужно применять осмысленно, а не из соображений «на всякий случай, пусть будет».
Я освоил технологию double-double и живу счастливо. И кстати, при её реализации на ассемблере 80-битный real оказался весьма полезным)
Забудьте про существование типа float и не парьте себе мозг.
Эммм…
А что вы предлагаете использовать вместо float?
17 цифр достаточно для большинства применений. В двойной точности работает большинство численных библиотек. С другой стороны, 64 бита – удобный размер для выравнивания в памяти.

Четырёхбайтовые вещественные числа – реликт времён, когда память была ограничена 64 килобайтами.
Что ж тогда ARM вообще half precision вычисления вводит в ARMv8.1? Они-то вообще на 16-битных числах.
Встроенные системы не имею в виду. Статья о научных вычислениях, как следует из её первых слов.
Ага, 64-битная архитектура ARMv8.1 с аппаратной виртуализацией — это очень встроенные решения. Осталось только понять, куда они встроены (-:
В мобильные телефоны?

В научных расчётах half precision точно не имеет никакого применения.
Пардон, ошибся, не ARMv8.1, а ARMv8.2.

Только судя по архитектурной документации, там уже не мобильные фичи, а вполне себе серверные, как то RAS, виртуализация и прочие.

Я, конечно, не специалист в вычислениях, но в интернетах пишут, что half precision арифметика вполне неплохо показывает себя в задачах, связанных с deep learning. Так что, может быть не стоит считать, что нужны только double precision?
Давно известны строгие математические оценки на этот счёт, с которыми автор статьи не дал себе труда познакомиться, прежде чем заявлять о преимуществе float (или, тем более, half) для экономии памяти. Предположим, у нас есть 4 гигабайта памяти, то есть миллиард чисел типа single. Чтобы получить из анализа всех этих данных какой-то практический результат, мы должны произвести, как минимум, миллиард последовательных операций над ними (иначе либо мы используем не все данные, либо цепочка операций не сойдётся к одному результату). При погрешности полбита на операцию, мы получаем в результате потерю точности не менее полумиллиарда цены младшего разряда, или 9 десятичных цифр, что уже превосходит всю разрядную сетку float. Вот и весь лёнинг.

Это не относится к параллельным преобразованиям из матрицы в матрицу. Но матрица из миллиарда чисел не может непосредственно восприниматься человеком и потому не является конечным результатом.
Но матрица из миллиарда чисел не может непосредственно восприниматься человеком и потому не является конечным результатом.
Это кто вам такую чушь сказал? DCI 4K, 60FPS, три компоненты изображения — вот вам уже больше полутора миллиардов чисел.

Или вы хотите сказать, что человек неспособен видеоизображение воспринимать?

При погрешности полбита на операцию, мы получаем в результате потерю точности не менее полумиллиарда цены младшего разряда, или 9 десятичных цифр, что уже превосходит всю разрядную сетку float.
Рекомендую вам на досуге почитать про нейронные сети. Узнаете для себя много нового. В частности узнаете как можно с пользой использовать миллиард чисел так, чтобы во время практического использования алгоритма между собой сранивался десяток или около того.

Чтобы получить из анализа всех этих данных какой-то практический результат, мы должны произвести, как минимум, миллиард последовательных операций над ними
Зачем? Подумайте о компьютере, работающем на частоте 500 герц и порождающем ответ за 1/10 секунды, опираясь на матрицу из миллиардов чисел! Довольно очевидно, что этот компьютер не производит миллиардов последовательных вычислений — что не мешает ему быть весьма и весьма полезным в разных случаях… Или вы не верите в существование такого?
Посмотрите в зеркало!
Видеоизображение состоит из целых чисел, точность которых может не уменьшаться при операциях с ними. То же самое с программной памятью компьютера, которая, к тому же, используется далеко не полностью каждую секунду, а очень выборочно. То же с нейронной сетью.
Видеоизображение состоит из целых чисел, точность которых может не уменьшаться при операциях с ними.
Почитайте хотя бы википедию, если ни на что другое времени нет: реальные сцены часто имеют динамический диапазон яркости в 1 000 000:1 и выше, при этом и в тенях и в свете глаз способен (из-за световой адаптации к яркости) различить детали

«из-за световой адаптации к яркости» — это в чистом виде плавучка и есть. При этом ни о каких double'ах или даже float'ах и речи не идёт.

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

Hint: наука, как бы, весьма многообразна и при каком-нибудь анализа ДНК могут применяться самые разнообразные алгоритмы, совершенно не сводящиеся к любимым вами манипуляциям над матрицами миллиард на миллиард.
Световая адаптация к яркости — это чисто механическое изменение площади зрачка, не имеющее никакого отношения к обработке сцены в мозге. Просто диафрагмирование.
Наважно как именно это сделано. Важно что для генерации реалистичной картинки вам нужно обрабатывать как очень яркие, так и очень тёмные области одновременно если вы не знаете зарание — какую часть избражения вам придётся показать в сгенерированной в конечном итоге картинке.
Вы сейчас говорите про обработку в DCI 4K, 60FPS или в человеческом мозге?

Кадр DCI 4K – это просто 8 миллионов целых чисел. Как целые числа, они не требуют вещественной арифметики. Динамический диапазон такого кадра равен динамическому диапазону устройства отображения, независимо от того, какие там использованы сцены.

Если речь идёт о мозге, то он не обрабатывает одновременно очень яркие и очень тёмные области. Он сначала смотрит на яркую область с расширенным зрачком, её распознаёт и запоминает результат, а потом смотрит на светлую область с суженным зрачком, её распознаёт и тоже запоминает результат. Потом умозрительно сводит эти два распознанных результата (уже имеющих очень малый информационный объём) вместе.
Кадр DCI 4K – это просто 8 миллионов целых чисел.
Да — но это результат. Чтобы его получить вам вполне может потребоваться обрабатывать совершенно разные вещи, при этом вы не будете знать — какая часть попадёт в конечное изображение.

Кто тут говорил что точность для промежуточных представлений нужна большая, чем для результата? Вот HDR — это то же самое, только с динамическим диапазоном. Если в одном кадре у вас ярко светит солнце, а в следующем — оно чем-то загорожено, то вам потребуется резко «перескочить» в другой динамический диапазон.
Промежуточные представления чего? Если это реальная видеосъёмка, то там такая же ограниченная в разрядности КМОП-матрица на входе. А если анимация, то ей незачем иметь солнце реальной огромной яркости.

Безусловно, всё можно сделать с огромным диапазоном, если считать тупо в лоб. Но я-то говорю как раз о том, что это низачем не нужно для потребного человеку скромного результата.
Если это реальная видеосъёмка, то там такая же ограниченная в разрядности КМОП-матрица на входе.
Во-первых может быть не одна. Во-вторых — можно синтезировать из нескольких снимков изображение. В третьих — его можно рассчитать.

А если анимация, то ей незачем иметь солнце реальной огромной яркости.
С чего вы взяли? Многие вполне себе наблюдаемые эффекты без этого нормально не рассчитать.

Но я-то говорю как раз о том, что это низачем не нужно для потребного человеку скромного результата.
В том-то и дело, что нужно. Результаты, полученные с применением HDR заметно оличаются от тех, где HDR не применяется.

Можно, конечно, упираться и говорить что 640K, VGAAtari 2600 — достаточно для щастья, но это уже чистой воды волюнтаризм: да, в игры для Atari 2600 вполне можно играть, но это не значит что FP16 не нужен!
Не только. Большинство изученных нервных датчиков, IIRC, передают сигнал как логарифм входного воздействия (при этом собственно уровень такого сигнала после логарифмирования передаётся как частота импульсов).

Ну это просто вопрос выбора единицы измерения

Собственно, компьютер производит именно миллиарды последовательных вычислений, результатом которых является состояние регистров процессора. Но так как это цепочка логических операций, а не вещественных (с точки зрения работы самого компьютера), то она выполняется без погрешности.
Но так как это цепочка логических операций, а не вещественных (с точки зрения работы самого компьютера), то она выполняется без погрешности.
Так можно дойти до того, чтобы обьявить что никаких чисел с плавающей точкой в природе вообще нет — это ж фикция! Там просто два целых числа, фигли мы вокруг этого какие-то теории разводим?
С точки зрения микропроцессора, никаких вещественных чисел нет. Он оперирует целыми битами в регистрах, абсолютно детерминированно. Но когда вы начинаете интерпретировать эти биты, как приближение идеальных математических вещественных чисел, в результате этого приближения и появляются все те проблемы с точностью вычислений, которые обсуждаются.
Что касается человеческого «компьютера». Если вы запомните хотя бы матрицу 10*10 вещественных чисел, то уже можете считать, что у вас хорошая память. Никаких миллиардов чисел там и близко нет. Есть электрохимические потенциалы нейронов, имеющие, впрочем, очень высокую точность по сравнению с точностью результата. Достаточно их чуть-чуть поменять, например, гормонами — и результат работы мозга меняется радикально.
Есть электрохимические потенциалы нейронов, имеющие, впрочем, очень высокую точность по сравнению с точностью результата.
Ну то есть всё точно так же, как с алгоритмами, которые используют числа с половинной точностью. И которые вы обьявили заочно «никому не нужной фигнёй».
Ну если вас устраивает алгоритм, который в зависимости от случайных погрешностей может найти корни уравнения, а может решить пойти по бабам, то в этом смысле низкая точность подходит. Но это не то, что принято называть научными расчётами.
Но это не то, что принято называть научными расчётами.
Серьёзно? Методы Монте-Карло уже обьявлены лже-наукой? И выделение генов с помощью нейронных сетей — тоже?

Очень узкая у вас получается наука: подавляющее большинство вычислений, имеющих практический «выхлоп» окажется «за бортом».
Вы видели метод Монте-Карло, ушедший по бабам?

Не любые операции накапливают погрешность одинаково! К примеру, если построить дерево из операций вида z = sqrt(x+y) — то сложение погрешностей компенсируется квадратным корнем, и погрешность результата будет зависеть лишь от погрешностей самих операций вычисления квадратного корня! То есть относительная погрешность результата будет пропорциональна лишь логарифму объема исходных данных.


Пример, конечно же, синтетический — мне неизвестен смысл таких вычислений. Тем не менее, он опровергает тезис об обязательном накоплении погрешности.


Более реальные примеры связаны с теми же нейросетями. При прямом распространении сигнала каждый нейрон применяет к нему нелинейную функцию, которая "стягивает" значения к крайним точкам (0 и 1 или -1 и 1). Точно так же как и в примере с квадратным корнем —


В тех же нейросетях при прямом распространении сигнала каждый нейрон совершает нелинейное преобразование, которое "стягивает" значения к крайним точкам (0 и 1 или -1 и 1). Погрешности при таком распространении сигнала не накапливаются так же, как и в примере с корнем.


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

Sign up to leave a comment.

Articles