194 / 29 / 5
Регистрация: 11.04.2015
Сообщений: 726
|
||||||||||||||||
1 | ||||||||||||||||
Непредсказуемые изменения точности расчётов внутри kernel10.04.2024, 00:29. Показов 1890. Ответов 45
Метки нет (Все метки)
Всем доброго времени суток!
Не так много времени прошло, но я снова вынужден обратиться за помощью к сообществу. Не по теме: Ну, тут правда то ли я тупой, то ли лыжи не едут. Я ДВА ДНЯ потратил на отладку, чтобы узнать, что проблема ни разу не в моих алгоритмах - просто OpenCL'ю иногда хочется посчитать 9 знаков после запятой, а не 15. Прошу прощения. Смотрите, есть простая программа по вычислению длины отрезка дуги на поверхности сферы:
Поясняю. Вот код управляющей программы (Qt 5, CUDA 12.4, OpenCL 3.0 (auto)): Кликните здесь для просмотра всего текста
и код файла CalcDist.cl: Кликните здесь для просмотра всего текста
Если просто запустить эту программу и сравнить значения дистанции, то мы увидим Код
CPU dist = 3.359239459231472269e-01 NV dist = 3.359239460197458449e-01 Главная причина этого в том, что значение переменной p2 оказывается таким: Код
CPU p2 = 5.272612557671862987e-08 NV p2 = 5.272612559188061227e-08 Однако, если мы после основного расчёта (внутри kernel) дистанции продублируем его, разделив расчёт переменной p2 на три отдельных операции, то, О ЧУДО, вывод окажется следующим: Код
CPU p2_h1 = 4.886896713611054155e-01 CPU p2_h2 = 4.886896186349798388e-01 CPU p2_sum = 5.272612557671862987e-08 NV p2_h1 = 4.886896713611054155e-01 NV p2_h2 = 4.886896186349798388e-01 NV p2_sum = 5.272612557671862987e-08 Не по теме: Какие-то приколы из мира Arduino, чесслово Я думал, что нашёл проблему, и заменил старые одношаговые секции кода новыми - многошаговыми. КАК же я был удивлён, когда на выходе снова получил восемь несчастных корректных знаков после запятой из 15. Отсюда и сам мой вопрос - а что, собственно, происходит? Почему, я даже не знаю, как это правильно назвать, видеокарта не хочет с первого раза нормально считать? Почему корректный расчёт происходит только при избыточном дублировании? На всякий случай, прикладываю полный консольный вывод приведённой выше программы: Код
Using platform: "NVIDIA CUDA" Using device: "NVIDIA GeForce RTX 4080" Using CL version: "OpenCL 3.0 CUDA" CPU p2 = 5.272612557671862987e-08 CPU res = 3.359239459231472269e-01 CPU p2_h1 = 4.886896713611054155e-01 CPU p2_h2 = 4.886896186349798388e-01 CPU p2_sum = 5.272612557671862987e-08 CPU dist = 3.359239459231472269e-01 NV p2 = 5.272612559188061227e-08 NV res = 3.359239460197458449e-01 NV p2_h1 = 4.886896713611054155e-01 NV p2_h2 = 4.886896186349798388e-01 NV p2_sum = 5.272612557671862987e-08 NV new_res = 3.359239459231471714e-01 NV dist = 3.359239460197458449e-01 Не по теме: За помощь с перевоспитанием сумасбродной видеокарты, судя по всему
0
|
10.04.2024, 00:29 | |
Ответы с готовыми решениями:
45
Повышение точности расчетов Повышение точности расчетов в Matlab Уменьшение точности расчетов (округление) Преобразовать функцию для повышения точности расчетов Построить график изменения вероятности нахождения в состояниях со временем (по результатам расчетов) |
194 / 29 / 5
Регистрация: 11.04.2015
Сообщений: 726
|
|||||||||||||
19.04.2024, 01:10 [ТС] | 21 | ||||||||||||
Да, конечно, расскажу. Делалось это с использованием MatLab, естественно
Как строится расчёт синуса для точности float (если не CORDIC)? Через интервальные аппроксимации параболами (см. рисунок). Например, точность float (ошибка <1.0E-08 ) обеспечивается всего лишь ~150 интервалами (если разбиваем от 0 до pi/2 ), при этом, точности 1.0E-12 достигнуть не удаётся и при числе интервалов в 5000. Более того - точность не растёт при увеличении числа интервалов свыше 3000, а падает. Очевидно, что точности double таким образом достичь не удастся.Для сходящихся функций (синус, косинус) прекрасно работает ряд Тейлора. Точно так же делим участок [0, pi/2] на N интервалов и раскладываем функцию синуса в ряд Тейлора в точке центра каждого интервала, после чего записываем коэффициенты получившегося полинома (с заранее заданной степенью i[SUB]max[/SUB] ) в таблицу:
<3.0E-16 (по данным MatLab) на 125 интервалах с полиномом 6 степени.Отдельно стоит сказать о том, как я нахожу в таблице нужные коэффициенты полинома. Поскольку участок равномерно разбит на N интервалов, можно использовать простую линейную функцию вида y = k*x , где y - номер интервала, а x - аргумент функции sin(x) . Отмечу, однако, что мой "бенчмарк" C++ сказал мне, что "MatLab не прав", и выдал максимальную ошибку в 1.2E-14, что, в целом, меня устроило.С тангенсом я не стал разбираться и бахнул tan(x) = sin(x) / cos(x) . Дорого, но я использую эту функцию довольно редко.Функции же арксинуса и арккосинуса расходятся, поэтому Тейлор тут мдееееее.. Да тут, как оказалось, что угодно будет мдееееее. Оказывается, арк-функции вычисляют через арктангенс, который сходится по формулам, использующим корень. Но разложить функцию арктангенса через ряд Тейлора, как я понял, невозможно, поэтому пользуются другими "аппроксиматорами". Я решил использовать аппроксимацию Паде: степень полинома числителя - 3, знаменателя - 5, число интервалов - 159. Эта функцию определена для любого x , кроме inf и NaN (нужно для inf - бахайте туда x=квинтиллион ). Порядок точности получился тем же, что и для синуса с косинусом. Оговорки есть только с интервальным разбиением - здесь не одно равномерное разбиение, а три (x: 0-10, 10-20, 20-40) и пять определённых вручную "широких" интервалов, неравномерно уходящих в "бесконечность".Ссылок нет, поскольку когда я это всё делал, большей теорией, чем та, что я сейчас написал, я не обладал. Неделя экспериментов, и программная реализация тригонометрии для double готова. В шейдеры, кстати, я загружал таблицы через SSBO; хотел захардкодить, но компилятор оПтИмИзИрУеТ константы, если они кажется ему слишком большими. Ох уж эти 10 Кб информации. Не по теме: Кстати я также реализовал CORDIC для double в шейдерах (только для синуса). Работало это чудо в 11 (ужасть) раз медленнее, чем моя "табличная" тригонометрия. Я не ожидал, что операции смещения битов в целых числах дороже, чем возведения в степень, умножения и сложения. Да, но так мы найдём две точки, а точкой пересечения является лишь одна из них (это ж дуги). Нужна проверка. Проверка простая - найти 6 дистанций и сравнить их попарно. Именно здесь моя коса находит себе камень - какая-то дистанция оказывается точной лишь до 3 знака и руинит проверку.
1
|
19.04.2024, 01:31 | 22 |
Продолжаю эксперименты с точностью)
Получаю интересные результаты, если коротко: зря мы гоним на точность OpenCL, или, брать результат работы NativeCPU код за эталон - так себе идея У меня есть ф-ия, которая генерит вершины спирали на поверхности сферы. Когда я увеличил количество вершин за оборот до 36000, то есть вершины генерятся с шагом в 0,01 градус и начало спирали (первая вершина) в центре(на нулевом расстоянии) то увидел следующее: Видно, что CPU рапартует нам о нулевом расстоянии, тогда как разница между координатами точек точно не нулевая (от 1e-7 до 1e-10) Тогда я решил распечать первые 10 расстояний посчитаных на CPU: Кликните здесь для просмотра всего текста
Короче, можно смело резать долготу < 1.25E-9 и широту < 1.0e-6 Но это тоже не сильно поможет так как напимер distCPU 6,7,8 абсолютно идентичны, тогда как на OpenCL - нет
1
|
194 / 29 / 5
Регистрация: 11.04.2015
Сообщений: 726
|
|
19.04.2024, 15:07 [ТС] | 23 |
Ну, смотрите - как я разрабатывал этот алгоритм. Сначала всё было на бумаге. Потом я написал реализацию на
C++ . Чтобы её проверить и отладить, я составил несколько тестовых наборов данных и вручную рассчитал результаты для каждого из них (ну, как вручную - с помощью систем символьной математики), чтобы точно понимать, что, где и как должно "идти" в любой момент времени.Как обычно, чуда не произошло и на этот раз, и моя C++ -реализация обрабатывает тестовые наборы не только правильно, но и с той же точностью на каждом шаге, какую дала символьная математика (1e-16).Именно поэтому, по крайней мере, в рамках данной проблемы, я и принимаю результаты, получаемые на CPU, за эталон, с которым и сравниваю данные, идущие с GPU. Да и давайте честно - библиотеке <math.h> сто лет в обед; не доверять ей - не доверять ничему.Далее. Как я уже говорил, вычислительный шейдер OpenGL (4.3) выдаёт мне те же данные, что и CPU с оговоркой на точность моей переопределённой тригонометрии. Но OpenCL настолько далеко даже от вычислительного шейдера (спецификация к которому не менялась с 2014 года), что я уверен, что дело в OpenCL. Возможно, дело в OpenCL 3.0, который у меня выставляется автоматически, но я пока не понял, как указать программе конкретную версию. Возможно, это просто ошибка в драйверах, но даже на примере вашего .exe шника мы видим неадекватное снижение точности даже по сравнению с GPU-собратом RX6800XT. Опять же, упомяну, что если я ставлю float в OpenGL, то точности не хватает только при определённых входных данных, а если в OpenCL - оно в принципе считает неверно.А это можно, да Разобраться бы со скачками точности, а там и ограничить можно.
0
|
824 / 241 / 47
Регистрация: 24.01.2013
Сообщений: 747
|
|
19.04.2024, 17:20 | 24 |
а если в программу OpenCL вставить ваши самописные ряды? какая точность?
З.Ы. и там еще такой нюанс, что математический сопроцессор в CPU считает точнее чем double, там 19-20 десятичных знаков. Потом при записи в память результат округляется до 15-16 знаков. А видяха если и поддерживает double то в точности по стандарту 15-16 знаков. В общем, я бы все таки поставил драйвер OpenCL для процессора и подсунул ему девайс-ЦПУ. Велика вероятность, что дело не в OpenCL, а в видеокарте, тем более, что это Nvidia, там дядюшка Хуанг "срезает углы" где только можно.
1
|
19.04.2024, 17:40 | 25 |
Если будет время попробуйте посчитать расстояние между точки(center) и точками cpt 4 и 5 на CPU QT
потому как у меня CPU возвращает одинаковое расстояние 0.0000949369370937348 км
1
|
194 / 29 / 5
Регистрация: 11.04.2015
Сообщений: 726
|
||||||||||||||||||||||
20.04.2024, 01:49 [ТС] | 26 | |||||||||||||||||||||
Итак Табличка (все расстояния приведены к метрам)
Теперь о главном - первый замер показывает, что вычислительный шейдер справился в десять раз лучше со своей работой. Отключение же оптимизаций не помогло OpenCL'ю перестать врать и никак не приблизило его даже к уровню шейдера (как я и говорил - столь же неправильные, но другие, значения). Второй замер оказался интереснее (перепроверено дважды). Вычислительный шейдер снова сильно обходит OpenCL-реализацию, однако лишь до тех пор, пока не ставишь флаг отключения оптимизаций - с флагом совпадают 19 (!все!) знаков, что, помятуя о точности double , определяемой 16 знаками максимум, говорит нам о том, что видеокарта применила точно такой же механизм, что применил процессор (ведь абсолютно одинаковый мусор с 17 по 19 знаки может идти только в этом случае).И вот об этом "красивом" случае я и говорю - если вычислительный шейдер ведёт себя последовательно, выдавая мне "9.5" правильных знаков из 16, то kernel делает, что захочет - на одних входных данных на тебе 8 точных знаков, и ничего ты с этим не поделаешь, а на других - на тебе 9 знаков, но если отключишь оптимизацию, то, так уж и быть, посчитаю точно. А почему для первого набора ты не хочешь считать точно с отключенной оптимизацией? Принцип "хочу / не хочу"? Ну, компьютеры не так работают (вроде бы). Драйверы NVidia шалят, или в OpenCL 3.0 напортачили?Не по теме: Меня, как программиста-учёного, глубоко расстраивают ситуации, в которых ты, даже используя дорогущие профессиональные решения (OpenCL 3.0.15 + CUDA 12.4 + RTX4080), не имеешь физической возможности собрать программу так, чтобы она работала правильно. Конечно, не исключаю, что именно игровые драйверы RTX4080 вносят неоценимый вклад во все мои проблемы. Как бы то ни было, в скорейшем времени я запущу тестовую программу на A100, и мы сможем узнать больше о виновниках торжества. На самом деле, сообщение от snake32 пришлось как нельзя кстати, с точки зрения Вашего вопроса. В таблице выше присутствует ячейка, которая (на мой взгляд) полностью исключает влияние встроенной в OpenCL тригонометрии на происходящее. Есть много фактов того, что в вакууме (и при редких обстоятельствах) встроенная тригонометрия работает неотличимо от <math.h> 'овской и не может являться причиной бед. Поэтому я не вижу особого смысла в тесте моей тригонометрии - она объективно хуже, хотя компилятор, действительно, может прийти в заблуждение и перестать оптимизировать тригонометрию (если он это делает). Может, попробую после тестов на A100.Это да, но и снова мы приходим к ячейке таблицы, в которой результаты от CPU и GPU совпали по 19 знакам. А должны были по 15-16, если бы они считали по-разному. Мистерия. Да, тоже займусь скоро. Хоть меня и пугает перспектива "поломки" чего-нибудь, о которой говорил snake32. Ну, вот даже если это так, я не буду готов снять ответственность с разработчиков - Khronos Group - которым в голову пришла гениальная идея об уменьшении контроля разработчика над процессом компиляции kernel'а. С точки зрения научных расчётов, на данный момент OpenCL 3.0 неприменим, поскольку есть лишь один флаг сборки, способный увеличить (привести к уровню, на котором она должна быть по умолчанию) точность, который хочет - работает, не хочет - не работает, а скорость режет. Как я понял, на данный момент не существует инструмента, позволяющего компилировать kernel отдельно от вызова cl::program::build() . Есть очень старые инструменты компиляции OpenCL-кода в PTX-формат, но они не развиваются со времён OpenCL 1.2.
1
|
824 / 241 / 47
Регистрация: 24.01.2013
Сообщений: 747
|
|
20.04.2024, 17:10 | 27 |
Да там ничего страшного, установка интеловского драйвера OpenCL затирает доступ к видеокартам NV или AMD и в списке девайсов останется только процессор и встройка от Интел (iGPU). Повторная установка драйвера видеокарты все исправит.
Добавлено через 1 час 7 минут а что по быстродействию с флагом и без него? может отключение флага оптимизации вообще приводит к тому что расчет идет на процессоре? потому то и совпадают все 19 знаков, иначе такого просто физически не может быть
1
|
194 / 29 / 5
Регистрация: 11.04.2015
Сообщений: 726
|
|
20.04.2024, 18:38 [ТС] | 28 |
В данном случае разница такая, что замерам не поддаётся (
<std::chrono> выводит наносекундную разницу, и верить этому, или нет - я не знаю). Моя большая программа, однако, при входных данных, не вызывающих сбоев, показывает разницу втрое. Но я не исследовал вопрос по-нормальному.Ну, это было бы странно, поскольку OpenCL пишет, что используется RTX4080, во время исполнения программы "дёргается" график "GPU utilization" в мониторе ресурсов, немного заполняется её память (по тому же монитору), да и процессор библиотека не видит, ведь я ещё не поставил соответствующие драйверы. Очень сомневаюсь, одним словом. Да ну почему же. Если в игровых GPU инструкции x64 эмулируются, то что мешает производителю эмулировать и x87 ?
0
|
824 / 241 / 47
Регистрация: 24.01.2013
Сообщений: 747
|
|
20.04.2024, 19:29 | 29 |
неа, процессор будет виден, просто драйвер от Интела лучше работает (использует инструкции AVX и др.), соответственно быстродействие с ним в несколько раз лучше. В теме snake32 тут рядом посмотрите тесты, там в его программе 2 процессора можно выбрать в списке, и работают по разному они. Т.е. второй появится после установки драйвера OpenCL от Интел. Разница в скорости расчета раз в 5-6.
эмуляция если и будет то с точностью 15-16 знаков, да и какая эмуляция даст совпадение по всем знакам? сильно сомневаюсь
0
|
194 / 29 / 5
Регистрация: 11.04.2015
Сообщений: 726
|
||||||
20.04.2024, 22:20 [ТС] | 30 | |||||
Ну, слушайте, не виден
Что я делаю в коде:
Код
Using platform: "NVIDIA CUDA" num_devices: 1 Using device: "NVIDIA GeForce RTX 4080" Using CL version: "OpenCL 3.0 CUDA" 0 , я получаю падение программы, да и не удивительно - ведь она видит лишь одно пригодное устройство.Поэтому я не сомневаюсь, что расчёт идёт исключительно на GPU. Да и колонка №1 в таблице говорит о том, что даже с -cl-opt-disable регулярно идут бредовые данные, то есть, на CPU программа не переходит.Что я выяснил из интернетов - если вычислители отправляют два числа, у которых имеет место полное совпадение в битовом представлении (все 64 бита одинаковы), то средство вывода их на экран в десятичном виде будет давать одно и то же, даже мусор, теоретически, будет одинаковым. Я вывел на экран 30 значений после запятой, и они также совпали. Предполагаю, что GPU просто выдала абсолютно такой же набор битов. Также я не думаю, что это ошибка или баг.
0
|
824 / 241 / 47
Регистрация: 24.01.2013
Сообщений: 747
|
|
20.04.2024, 23:56 | 31 |
ну не знаю, у меня был виден изначально. Потом я установил драйвер OpenCL от Интела и в списке девайсов появился еще один процессор.
Теперь в списке 3 девайса - видеокарта и два процессора, при том что эти процессоры считают по разному, как писал выше.
0
|
22.04.2024, 14:56 | 32 |
У вас похоже что-то уже стояло или версия драйвера OpenCL от интел другой версии...
или винда для цп-intel сама ставит эксклюзивно (хотя когда у меня был i7-6700K тоже ничего не было без драйвера), но для амд без установки драйвера процессор не участвует точно. Ромуальд_7, короче я не знаю как у вас такая точность получается... я уже и векторную форму проверил и в ТГ-группе Delphi/Lazarus спросил везде +/- одно и тоже и получается что не более 0,5 метра точность (5e-4 км на около нулевых расстояний), не важно где считать (CPU/OpenCL), да там и считать особо нечего: два синуса, три косинуса и арккосинус. А в векторной форме вообще 3 умножения и арккосинус, думаю, это самый быстрый способ, хотя это надо ещё проверить... Может позже сделаю бенч, только на Delphi перенесу, чтобы компилятор был другой, на всякий случай.
1
|
194 / 29 / 5
Регистрация: 11.04.2015
Сообщений: 726
|
|
22.04.2024, 19:56 [ТС] | 33 |
snake32, Ну, тут уж что уж
В любом случае, спасибо!
0
|
23.04.2024, 10:11 | 34 |
Да не за что. Самому интересно
Ромуальд_7, а у вас случайно не 32-битный режим? В 32-битном обычно для вычислений используется сопроцессор x87, где есть режим вычислений 10-байт (extended), а в x64 - максимум только векторные 8-байтные регистры (double)
0
|
194 / 29 / 5
Регистрация: 11.04.2015
Сообщений: 726
|
|
23.04.2024, 10:16 [ТС] | 35 |
snake32, нене, точно не 32-битный. Да а даже и если так, что бы изменилось? (Просто интересно)
А хотя, вот как работает метод .build() ? Как он, опять же, компилирует?
0
|
824 / 241 / 47
Регистрация: 24.01.2013
Сообщений: 747
|
|
23.04.2024, 12:00 | 36 |
Ромуальд_7, это то что я писал, сопроцессор x87 считает (внутри себя, т.е. если нет промежуточных сохранений) с точностью Extended (80-bit), а x64 это на расширениях AVX, SSE - тут только 64 bit.
0
|
194 / 29 / 5
Регистрация: 11.04.2015
Сообщений: 726
|
|
23.04.2024, 12:46 [ТС] | 37 |
Да я ещё тогда понял
Вопрос-то главный в том, что компилятор kernel'а делает, а не что я указываю для gcc.
0
|
824 / 241 / 47
Регистрация: 24.01.2013
Сообщений: 747
|
|
23.04.2024, 13:01 | 38 |
Ромуальд_7, так kernel компилирует драйвер, а gcc это код "обвязки", так же как с вычислительным шейдером.
0
|
194 / 29 / 5
Регистрация: 11.04.2015
Сообщений: 726
|
|
23.04.2024, 13:11 [ТС] | 39 |
Вооот, но я никак не могу найти подтверждения этому.
Смотрите, в спецификации есть часть: Код
5.8.6.1. Preprocessor Options These options control the OpenCL C/C++ preprocessor which is run on each program source before actual compilation. These options are ignored for programs created with IL. -D name Predefine name as a macro, with definition 1. -D name=definition The contents of definition are tokenized and processed as if they appeared during translation phase three in a #define directive. In particular, the definition will be truncated by embedded newline characters. -D options are processed in the order they are given in the options argument to clBuildProgram or clCompileProgram. Note that a space is required between the -D option and the symbol it defines, otherwise behavior is implementation-defined. -I dir Add the directory dir to the list of directories to be searched for header files. dir can optionally be enclosed in double quotes. This option is not portable due to its dependency on host file system and host operating system. It is supported for backwards compatibility with previous OpenCL versions. Developers are encouraged to create and use explicit header objects by means of clCompileProgram followed by clLinkProgram. nvcc , вроде вышеупомянутого --fmad=false , но когда я передаю параметр через -D он просто игнорируется. Я не понял, что такое IL , но, похоже, ядро компилируется именно через него.
0
|
824 / 241 / 47
Регистрация: 24.01.2013
Сообщений: 747
|
|
23.04.2024, 13:20 | 40 |
Ромуальд_7, с этим не могу помочь, не разбирался с этими параметрами, увы.
Добавлено через 4 минуты там препроцессор для kernel'а свой, отдельный от С++. Может есть возможность в самом kernel'е задать дефайн?
0
|
23.04.2024, 13:20 | |
23.04.2024, 13:20 | |
Помогаю со студенческими работами здесь
40
Электротехника: График изменения тока протекающего через конденсатор на переменном E. Из расчетов переходных процессов Непредсказуемые синие экраны Stripos - непредсказуемые результаты Изменения внутри Combobox Изменения внутри файла стилей средствами JS Искать еще темы с ответами Или воспользуйтесь поиском по форуму: |