C++ Builder
| Главная | Уроки | Статьи | FAQ | Форум | Downloads | Литература | Ссылки | RXLib | Диски |

 
Многопоточное программирование мат вычислений, Напоролся на косяк! Делюсь опытом.
klen
Отправлено: 26.06.2006, 22:06


Машинист паровоза

Группа: Участник
Сообщений: 239



Здравствуйте, тема такая:
Занимаюсь расчетными задачами(в данный момент пишу ядро ародинамического расчета обтекания тела воздхом по методу дискретных вихрей, дале хочу сделать среду разработки воздушных винтов ).
Так вот после приобретения Athlon64 X2 ударился в паралельные вычисления, и довольно успешно. На практике из физических расчетов вобщемто только итерационные методы не поддаются распаралеливанию и численное интегрирование диффур.
И случился у меня такой ГЛЮК!! если запускать расчет (а было это рачет матрицы СЛУ 800x800) матрицы системы а потом ее решать в контексте основного потока то результат один, если в паралельных нитках — другой! Я был в просрациыях три дня не мог понять в чем проблема. Вроде данные не перекрещиваются и тд. Чесно говоря я уже подумал что у АМД сопроцессор глючит — эта мысль ставила крест на всей моей дальнейшей деятельности и оссациировалась с веревкой и мылом.
Ответ пришол неожиданно. Рано и ли позно я доше до трассировки ассемблера на сопроцессоре для выяснения когда же возникают различия в реакции на одни и теже входные данные. Анализ показал:
1. Oказалось в windows кадый поток хранит свое слово состояния сопроцессора — читай режим работы сопроцессора.
2. В VCL приложениях потоки класса TThread и собственно "ручные потоки" создаваемые CreatThread не наследуют режим сопроцессра главного потока.
3. В жизни не лазил в сопроцессор на низком уровне — А ЗРЯ!!!

Решается это просто — явным указанием режима сопроцессора в начале выполнения функции потока (можно Win API, можно VCL функции приведенные ниже):
SetPrecisionMode (...) ;
SetRoundMode(...) ;

В резульате того что в основном потоке и в потоках запускаемых на вычисления имелся различный режим округления решения системы размером 800x800 в разных потоках давал разный результат, что для меня стало необяснимым откровением железного ящика.

А я ошибку в коде искал три дня smile.gif
Будите много и сильно считать циферки, не забудьте учесть мой опыт:)
Grigoriy
Отправлено: 27.06.2006, 13:54


Мастер участка

Группа: Участник
Сообщений: 381



В общем я могу добавить так.

В устройстве для выполнения операций с плавающей точкой процессора есть регистр управления CWR.

11 и 10 биты регулируют режим округления.
00 — округление к ближайшему целому числу
01 — округление в меньшую сторону
10 — округление в большую сторону
11 — отбрасывание дробной части

8 и 9 биты регулируют длину мантиссы для выполнения операций деления.
00 — длина мантиссы 24 бита
10 — длина мантиссы 53 бита
11 — длина мантиссы 64 бита (максимум)

Инструкции для работы с этим регистром есть такие:

fldcw m16
fstcw m16

Первая команда загружает из 16-битной ячейки памяти значение в CWR.
Вторая команда сохраняет CWR в 16-битную ячейку памяти.

Вы можете использовать эти команды для сохранения и загрузки регистра управления при работе с разными потоками.

Вы можете изменять биты регулирующие режимы округления и размер мантиссы.

Пример кода
CODE

void __fastcall TForm1::Button1Click(TObject *Sender)
{
short unsigned int vf;
double a1,a2,a3;
a1=Edit1->Text.ToDouble();
a2=Edit2->Text.ToDouble();
asm
{
finit;//приводим сопроцессор в состояние по умолчанию
fstcw vf;//сохраняем слово управления
or word ptr vf,0000100000000000b;//устанавливаем биты так, чтобы
and word ptr vf,1111101111111111b;//значение округлялось в большую сторону
fldcw vf;//обновляем слово управления
fld a1;//грузим делимое
fdiv a2;//делим на делитель
frndint;//округляем частное
fstp a3;//сохраняем частное
};
Edit3->Text=FloatToStr(a3);
}


Отредактировано Grigoriy — 27/06/2006, 13:56

Вернуться в Вопросы программирования в C++Builder