ホーム » VC OpenMP
「VC OpenMP」カテゴリーアーカイブ
#pragma omp critical
共通のリソースに対してのコードを書いていて,ちょっと気になったので調べてみた.
テスト用に書いたのコードは次の様なもの.
#ifdef _OPENMP
#include <omp.h>
#endif
#include <clocale>
#include <iostream>
#ifdef _MSC_VER
#include <tchar.h>
#else
#define _T(x) x
typedef char TCHAR ;
#endif
#ifdef _UNICODE
#define _tmain wmain
#define tout wcout
#else
#define _tmain main
#define tout cout
#endif
bool test_n (const long n)
{
std::tout << _T("test_") << n << _T("\t") ;
{
for (long index=0 ; index<10 ; index++) {
#ifdef _OPENMP
// #pragma omp critical // (test) // --- (C)
#endif
{
std::tout << (n*10 + index+1) << _T("\t") ;
}
}
}
return true ;
}
bool test_ (void)
{
std::tout << _T("test_") << std::endl ;
{
#ifdef _OPENMP
#pragma omp parallel for
#endif
for (long index=0 ; index<10 ; index++) {
#ifdef _OPENMP
#pragma omp critical // (test) // --- (P)
#endif
{
test_n(index) ;
std::tout << std::endl ;
}
}
}
return true ;
}
int _tmain (int argc,TCHAR* argv[])
{
{
::test_() ;
}
return 0 ;
}
ここで,関数 test_n() の次の行を有効にしてしまうと…
#pragma omp critical // (test) // — (C)
test_
test_0 致命的なユーザー エラー 1002: 同一名の 1 つで ‘#pragma omp critical’ が不適切に入れ子にされています
それぞれを異なる名称で指定する必要がある.
#pragma omp critical (test_n) // — (C)
#pragma omp critical (test_) // — (P)
VC リリース版ではうまく動作してしまうこともある?
Linux 環境でコンパイルした a.out を NAS 環境に持っていくと,DS220+ では実行できた.
-fopenmp を付けたコンパイルは不可.
TS253D では「libgomp.so がない」となって実行できない.
OpenMP Fatal User Error 1002
OpenMP が有効な時でも動作する様にテストしていると…
Fatal User Error 1002: A ‘#pragma omp critical’ is illegally nested in one of the same name
元々あった次のコードに #pragma omp critical としたことによるもの.
tstring Get_Self_Original (void)
{
static bool Yet = true ;
static tstring Self_original ;
if (Yet) {
tstring self_name = ::Get_module_name() ;
Self_original = Get_OriginalFilename(self_name.c_str()) ;
if (Self_original.empty()) {
Self_original = ::Path_GetName(self_name) ;
}
Yet = false ;
}
return Self_original ;
}
次の様に並列処理可能にすることで動作する様にはなるが …
tstring Get_Self_Original (void)
{
#ifdef _OPENMP
// #pragma omp critical (_Get_Self_Original_)
#endif
{
#ifdef _OPENMP
bool Yet = true ;
tstring Self_original ;
#else
static bool Yet = true ;
static tstring Self_original ;
#endif
if (Yet) {
tstring self_name = ::Get_module_name() ;
Self_original = Get_OriginalFilename(self_name.c_str()) ;
if (Self_original.empty()) {
Self_original = ::Path_GetName(self_name) ;
}
Yet = false ;
}
return Self_original ;
}
}
以前にも同じようなことをやっていた.
https://dev.mish.work/wordpress/2010/03/31/openmp-error-1002/
2023/04/27
この関数は exe の起動直後などに一度呼ばれることを意図しているので,critical をもっと局所的に.
tstring Get_Self_Original (void)
{
static bool Yet = true ;
static tstring Self_original ;
if (Yet) {
#ifdef _OPENMP
#pragma omp critical (_Get_Self_Original_)
#endif
{
tstring self_name = ::Get_module_name() ;
Self_original = Get_OriginalFilename(self_name.c_str()) ;
if (Self_original.empty()) {
Self_original = ::Path_GetName(self_name) ;
}
Yet = false ;
}
}
return Self_original ;
}
CMutex の使用でデッドロック?
何年も前(2015/11?)からなのかもしれないが,
今の PC になってから作成した exe が起動時やサムネイルの表示などで停止する現象が時々発生している.
発生する頻度が低いのと,現象が絞り切れていなかったのでそのままになっていた.
今日,デバッグしていると exe が起動しない.
その時までに起動済みのものは動作しているが,止まってしまうものもあり.
以前この現象が発生した時は再起動することで回避したが,今回はもう少し調べてみることにした.
デバッガで追いかけると CMutex の Lock(INFINITE) で戻って来ない.
使用している所は次の様な感じ.
{ _MutexS_ mt(FALSE,_T("DocIB::GetBitmap")) ; MutexS m(&mt) ; { // 共有のリソースに対する操作 } }
通常の動作では Unlock されないことがある様には思えないが…
デバッガを使っていて Lock 中に exe を強制終了してしまったか?
_MutexS_ UseGdiPlus::ME(FALSE,_T("UseGdiPlus::GP_Token")) ; { MutexS me(&ME) ; if (IsInitialized()) { // ... } // .. }
いい修正方法が思い浮かばないので,とりあえずメモ.
2021/02/19
その後いろいろと検索して調べたが,通常の動作ではロックされたままとなることはなさそう.
https://docs.microsoft.com/ja-jp/dotnet/standard/threading/mutexes
表示されてはないが起動したままの exe があるかと思い,タスクバーを見たが特になさそう.
昨日,わかっているものの幾つかは「タスクの終了」で終わらせている.
そうなるとロックされている Mutex は,自前のシェルエクステンションと思われる.
「タスクバー」に表示されている「エクスプローラ」は終了させたが,まだ誰かがロックしている.
画面には表示されていない explorer.exe が幾つかか存在したので,すべて終わらせた.
これでロックされた Mutex は解放されたみたい.
今度は,デバッガを使用してうまく起動しなかった exe をいろいろ試すことに.
デバッガで Lock した直後にブレイクポイントを設定して停止.
当然であるが他の exe を起動すると Lock の所で止まってしまう.
デバッガで Unlock の後まで実行すると,他の exe も止まっていた所から動き出す.
デバッガで Lock した直後に停止させて,他の exe を起動.
デバッガで「デバッガの停止」してみる(Unlock していない)と,他の exe が動き出す.
これらの動作を見ると,使い方としてはそれ程間違ってはなさそう.
コードを追いかける限りでは,「マズい」所がわからない.
複数のプロセスから呼ばれた時に意図しないタイミングとなってしまう所があるのか?
ある程度はっきりしたのは,自前のシェルエクステンションの GDIPlus 関係の時の Lock で止まっていること.
vcomp.dll が見つからないため、…
市販のアプリケーションやシェアウェアなどの利用の方は次のリンク先を参照してください.
https://mish.myds.me/wordpress/i-tools/2017/05/17/mfc140u-dll-error/
以下は開発者向けの情報です.
VC6 で作成したコンソール AP を VC7 以降に変換.
VC8 以降では OpenMP が使用できるので,コンパイルの設定を変更.
VC10 以降では問題ないが,VC8 ,VC9 でビルドしたものを実行すると,
—————————
gons_to.exe – システム エラー
—————————
vcomp.dll が見つからないため、コードの実行を続行できません。プログラムを再インストールすると、この問題が解決する可能性があります。
—————————
OK
—————————
—————————
gons_to.exe – システム エラー
—————————
VCOMP90.DLL が見つからないため、コードの実行を続行できません。プログラムを再インストールすると、この問題が解決する可能性があります。
—————————
OK
—————————
しばらくわからなかったが,以前にも書いていた.
https://mish.myds.me/wordpress/dev/2013/03/14/vcompd-dll-debug-error/
main() 関数がある cpp に以下を追加.
#ifdef _OPENMP
#include <omp.h>
#endif
ループ部分の OpenMP 対応
効果は低かったが,OpenMP を利用したコードに書きなおしてみた.
//--- オリジナルのコード ---------------------------------------------------------------- Ld2A lins ; { for (size_t indexVV=0 ; indexVV<vv_plf.size() ; indexVV++) { v_PLF v_plf = vv_plf[indexVV] ; if (v_plf.size() == 0) { continue ; } for (size_t indexV=0 ; indexV<v_plf.size() ; indexV++) { PLF plf = v_plf[indexV] ; if (plf.Is_line()) { Vd4A v4a = plf ; Vd3A v3a = ::ToVd3A(v4a) ; Vd2A v2a = ::ToVd2A(v3a) ; for (size_t indexL=1 ; indexL<v2a.size() ; indexL++) { Vd2 s = v2a[indexL-1] ; Vd2 e = v2a[indexL-0] ; Ld2 lin(s,e) ; lins.push_back(lin) ; } } } } } //---- OpenMP 対応 ------------------------------------------------------------------------ Ld2A PLF_to_Ld2A (const v_PLF& v_plf) { Ld2A lins ; { for (size_t indexV=0 ; indexV<v_plf.size() ; indexV++) { PLF plf = v_plf[indexV] ; if (plf.Is_line()) { Vd4A v4a = plf ; Vd3A v3a = ::ToVd3A(v4a) ; Vd2A v2a = ::ToVd2A(v3a) ; for (size_t indexL=1 ; indexL<v2a.size() ; indexL++) { Vd2 s = v2a[indexL-1] ; Vd2 e = v2a[indexL-0] ; Ld2 lin(s,e) ; lins.push_back(lin) ; } } } } return lins ; } Ld2A PLF_to_Ld2A (const vv_PLF& vv_plf) { Ld2A lins ; { #ifdef _OPENMP #pragma omp parallel for #endif for (long indexVV=0 ; indexVV<long(vv_plf.size()) ; indexVV++) { v_PLF v_plf = vv_plf[indexVV] ; if (v_plf.size() == 0) { continue ; } Ld2A ln_a = ::PLF_to_Ld2A(v_plf) ; #ifdef _OPENMP #pragma omp critical (PLF_to_Ld2A) #endif { lins.insert(lins.end(),ln_a.begin(),ln_a.end()) ; } } } return lins ; }
C:\Users\Iwao>C:\Temp\Test\Fill\GetX_1\ReleaseS.140\GetX_1.exe 31.856 C:\Users\Iwao>C:\Temp\Test\Fill\GetX_1\Release.140\GetX_1.exe 20.202 C:\Users\Iwao>
v_Vd2A GetCross (const vv_PLF& vv_plf,const Vd2& pt) { Ld2A lins = ::PLF_to_Ld2A(vv_plf) ; v_Vd2A v_pnts ; { Vd2A pnts ; pnts.push_back(pt) ; v_pnts.push_back(pnts) ; } { Ld2 lh(pt,pt+Vd2(1,0)) ; Ld2 lv(pt,pt+Vd2(0,1)) ; Vd2A work_pnts ; #ifdef _OPENMP #pragma omp parallel for #endif for (long index=0 ; index<long(lins.size()) ; index++) { Vd2 s = lins[index].S ; Vd2 e = lins[index].E ; Ed2 ext(s,e) ; Vd2 x ; if (get_cross_line(s,e,lh.S,lh.E,&x)) { if (::Is_point_in_extent(x,ext)) { // pnts.push_back(x) ; } } if (get_cross_line(s,e,lv.S,lv.E,&x)) { if (::Is_point_in_extent(x,ext)) { // pnts.push_back(x) ; } } { Vd2 h = ::get_near_on_line(s,e,pt) ; if (::Is_point_in_extent(h,ext)) { #ifdef _OPENMP #pragma omp critical (GetCross) #endif { work_pnts.push_back(h) ; if (work_pnts.size() > 10) { v_pnts.push_back(work_pnts) ; work_pnts.clear() ; } } } } } v_pnts.push_back(work_pnts) ; } return v_pnts ; }
実行時のエラー OpenMP
普段通っているコードなのに,ある条件(操作)で実行時にエラー.
エラーの場所はある範囲(DelFileE への登録)ではあるが,固定されてない.
CStringArray に CString の追加.
コールスタックを見ると MakeFace$omp$1 とある.
ucrtbased.dll!__VCrtDbgReportA () 不明
ucrtbased.dll!__CrtDbgReport () 不明
mfc140ud.dll!AfxAssertFailedLine(const char * lpszFileName, int nLine) 行 333 C++
mfc140ud.dll!CWnd::DestroyWindow() 行 1055 C++
mfc140ud.dll!CToolTipCtrl::DestroyToolTipCtrl() 行 73 C++
mfc140ud.dll!AFX_MODULE_THREAD_STATE::~AFX_MODULE_THREAD_STATE() 行 253 C++
mfc140ud.dll!AFX_MODULE_THREAD_STATE::`scalar deleting destructor'(unsigned int) C++
mfc140ud.dll!CThreadSlotData::DeleteValues(CThreadData * pData, HINSTANCE__ * hInst) 行 354 C++
mfc140ud.dll!CThreadSlotData::DeleteValues(HINSTANCE__ * hInst, int bAll) 行 396 C++
mfc140ud.dll!AfxTermLocalData(HINSTANCE__ * hInst, int bAll) 行 494 C++
mfc140ud.dll!DllMain(HINSTANCE__ * hInstance, unsigned long dwReason, void * __formal) 行 663 C++
mfc140ud.dll!dllmain_dispatch(HINSTANCE__ * const …, void * const reserved) 行 195 C++
mfc140ud.dll!_DllMainCRTStartup(HINSTANCE__ * const …, void * const reserved) 行 248 C++
ntdll.dll!_LdrpCallInitRoutine@16 () 不明
ntdll.dll!_LdrShutdownProcess@0 () 不明
ntdll.dll!_RtlExitUserProcess@4 () 不明
kernel32.dll!_ExitProcessStub@4 () 不明
ucrtbased.dll!__crt_hmodule_traits::close(struct HINSTANCE__ *) 不明
ucrtbased.dll!__crt_hmodule_traits::close(struct HINSTANCE__ *) 不明
ucrtbased.dll!__Exit () 不明
ucrtbased.dll!_raise () 不明
ucrtbased.dll!__acrt_lock_and_call<class <lambda_fe…55> >(enum __acrt_lock_id,class <…; &&) 不明
ucrtbased.dll!___acrt_MessageWindowA () 不明
ucrtbased.dll!__VCrtDbgReportA () 不明
ucrtbased.dll!__CrtDbgReport () 不明
mfc140ud.dll!AfxAssertFailedLine(const char * lpszFileName, int nLine) 行 333 C++
mfc140ud.dll!CStringArray::SetSize(int nNewSize, int nGrowBy) 行 165 C++
mfc140ud.dll!CStringArray::SetAtGrow(int …, const ATL::CStringT<wchar_t,… > & newElement) 行 265 C++
mfc140ud.dll!CStringArray::Add(const ATL::CStringT<wchar_t, … > & newElement) 行 322 C++
BlockIn.exe!DelFileE::Add(const wchar_t * fileName) 行 51 C++
BlockIn.exe!CacheFile::GetCF_Name(const wchar_t * srcName, const unsigned int dibWidth) 行 415 C++
BlockIn.exe!PartsA_To::ToIPX(const wchar_t * ipxName) 行 257 C++
BlockIn.exe!PartsA_To::DumpDebug(const int delFE, const wchar_t * pre_) 行 297 C++
BlockIn.exe!PartsA_To::DumpDebug(const wchar_t * pre) 行 47 C++
BlockIn.exe!PartsA_Fnc_DebugDump(PartsA & partsAry, const wchar_t * pre) 行 818 C++
BlockIn.exe!MaPat__DebugDump(const Parts & parts) 行 3293 C++
BlockIn.exe!MaPat::MakePartsFace(const int makeEdge) 行 3481 C++
BlockIn.exe!BAPat::MakePartsFace(const int makeEdge) 行 1508 C++
BlockIn.exe!BAPat::GetPartsFace(const int makeEdge) 行 1935 C++
BlockIn.exe!BlockInf::MakeFace(const int makeEdge) 行 3465 C++
> BlockIn.exe!BlockLay::MakeFace$omp$1() 行 4320 C++
[外部コード]
次の様に #pragma omp critical を追加.
BOOL DelFileE::Add (LPCTSTR fileName)
{
#ifdef _OPENMP
#pragma omp critical (DelFileE_Add)
#endif
{
CString filePath = ::FolderDelLastSP(fileName) ;
DelFileName.Add(filePath) ;
}
return TRUE ;
}
MessageBar クラスと OpenMP
新しい Message クラスを使用して,OpenMP 対応の動作のテスト.
void CTSttBView::OnTestDrawBar2()
{
Message tmp ;
ElapseTick et ;
#define BAR_COUNT_2 1000000
#ifdef _OPENMP
#pragma omp parallel for
#endif
for (int i=0 ; i<100 ; i++) {
Message bar ;
bar.SetBar (_T(“Test Message 1 M * 100”),BAR_COUNT_2,RGB(0,255,0)) ;
for (int index=0 ; index<BAR_COUNT_2 ; index++) {
bar.SetBarInc() ;
}
}
DWORD elapseT = et.GetElapse() ;
CString str ; str.Format(_T(“%.2f 秒”),elapseT/1000.) ;
AfxMessageBox(str) ;
}
1 億回 SetBarInc を呼出していて,10 秒程度だったのが,#pragma omp parallel for で 20 秒程度になってしまった.
inline bool MessageBase::SetBarInc (void)
{
if (GetBarMax() == 0) { return false ; }
if (GetBarCount() < GetBarMax()) { B_Counter++ ; }
else { B_Counter = 0 ; } // reset
IncCounterR() ;
{
long lastPos = long(100*(GetBarCount()-1)/GetBarMax()) ;
long new_Pos = long(100*(GetBarCount()-0)/GetBarMax()) ;
if (lastPos == new_Pos) { return true ; }
}
#ifdef _WINDOWS
#ifdef _MFC_VER
#ifdef _OPENMP
if (AfxGetMainWnd() == NULL) { return false ; }
#endif
#endif
#endif
return SetBarCount(B_Counter) ;
}
AfxGetMainWnd() を呼出す回数を減らすことにより,3 秒程度に.
vcompd.dll が見つからなかったため …
OpenMP エラー 1002
—————————
致命的なユーザー エラー 1002:
—————————
同一名の 1 つで ‘#pragma omp critical’ が不適切に入れ子にされています
2.9 ディレクティブの入れ子
局所的になる様に,呼び出し元での “#pragma omp critical” を削除.
class Profile { ... protected: LPCTSTR LastProfileName ; CString C_T_ProfileName ; ... } ; BOOL Profile::SaveProfileName(void) { CWinApp* app = AfxGetApp() ; if (app== NULL) { return FALSE ; } LastProfileName = AfxGetApp()->m_pszProfileName ; AfxGetApp()->m_pszProfileName = C_T_ProfileName ; return TRUE ; } BOOL Profile::LoadProfileName(void) { AfxGetApp()->m_pszProfileName = LastProfileName ; return TRUE ; }
omp critical ではなく,MFC 同期クラスを使用する様に変更.
// 以下は,テスト用のコード BOOL Profile::?et??? (LPCTSTR lpszSection,LPCTSTR lpszEntry,...) { ... CMutex m(FALSE,MN_LPN) ; m.Lock() ; // return ???Profile??? (lpszSection, lpszEntry, ...) ; BOOL ret = ???Profile??? (lpszSection, lpszEntry, ...) ; m.Unlock() ; return ret ; } // MFC 同期クラスの呼出しはコストがかからない様に修正する