One of the most popular and cheap means of implementing multi-thread computing in C ++ is
OpenMP .
The advantages of technology are obvious: simplicity; small and easily switchable code changes; support from the authors of the most popular compilers:
- Visual c ++
- GCC 4.2
- Intel C ++ Compiler
The battle check is successful, and now the parallelized code penetrates into the most secluded corners of the project, beats all performance records, producing smart statistics confirming the success of the release.
A couple of years pass, you successfully migrate to Visual Studio 2010, ... and you find yourself sitting in a puddle. If yesterday the processing of large amounts of data on machines with multi-core processors took place in a matter of seconds, then today the presence of any background application that occupies one or more cores with its own computations practically hangs up the application.
')
Why is this happening, and how to deal with it?
In the new OpenMP implementation, before you exit the active area to the Idle state, your program will wait for the completion of I / O operations, and wait using the active SpinWait.
Those. if we created N threads using OMP (1 per core), and we suddenly found out that one of the cores is occupied by another application, then 2 or more threads will be executed on one core with a high probability, switching between which will be interleaved with 200-ms pauses .
But in the calculation programs, we do not want any additional synchronization!
Intel developers are aware of this, and offer the user to take care of additional restrictions with the help of the kmp_set_blocktime () option. Unfortunately, their colleagues from Microsoft decided not to confuse users with unnecessary settings.
If you rewrite the program for honest threading pool laziness or does not allow religion - I suggest using my experience ... OpenMP downgrade to the original version from Microsoft Visual Studio 2005. Also, the instruction is suitable for Visual Studio 2008 with minimal changes.
First, copy to a separate folder vcomp.lib, vcompd.lib from the Visual C ++ 2005 set (you can refer directly to the installed distribution, but this is not so convenient). Go to the properties of using OpenMP projects, and add our directory to the Additional Library Directories. Voila - now the project is linked with the “correct”, fast-running version of OpenMP.
But that is not all. Replace the inclusion of <omp.h> with the following header file:
#pragma once #include <omp.h> #ifndef __OMP_LIBRARIES_ASSEMBLY_NAME_PREFIX #define __OMP_LIBRARIES_ASSEMBLY_NAME_PREFIX "Microsoft.VC80" #endif #ifndef _OMP_VC_ASSEMBLY_PUBLICKEYTOKEN #define _OMP_VC_ASSEMBLY_PUBLICKEYTOKEN "1fc8b3b9a1e18e3b" #endif #ifndef __OMP_CRT_ASSEMBLY_VERSION #define __OMP_CRT_ASSEMBLY_VERSION "8.0.50727.762" #endif #if defined(_DEBUG) #if defined(_M_IX86) #pragma comment(linker,"/manifestdependency:\"type='win32' " \ "name='" __OMP_LIBRARIES_ASSEMBLY_NAME_PREFIX ".DebugOpenMP' " \ "version='" __OMP_CRT_ASSEMBLY_VERSION "' " \ "processorArchitecture='x86' " \ "publicKeyToken='" _OMP_VC_ASSEMBLY_PUBLICKEYTOKEN "'\"") #elif defined(_M_AMD64) #pragma comment(linker,"/manifestdependency:\"type='win32' " \ "name='" __OMP_LIBRARIES_ASSEMBLY_NAME_PREFIX ".DebugOpenMP' " \ "version='" __OMP_CRT_ASSEMBLY_VERSION "' " \ "processorArchitecture='amd64' " \ "publicKeyToken='" _OMP_VC_ASSEMBLY_PUBLICKEYTOKEN "'\"") #elif defined(_M_IA64) #pragma comment(linker,"/manifestdependency:\"type='win32' " \ "name='" __OMP_LIBRARIES_ASSEMBLY_NAME_PREFIX ".DebugOpenMP' " \ "version='" __OMP_CRT_ASSEMBLY_VERSION "' " \ "processorArchitecture='ia64' " \ "publicKeyToken='" _OMP_VC_ASSEMBLY_PUBLICKEYTOKEN "'\"") #endif #else // _DEBUG #if defined(_M_IX86) #pragma comment(linker,"/manifestdependency:\"type='win32' " \ "name='" __OMP_LIBRARIES_ASSEMBLY_NAME_PREFIX ".OpenMP' " \ "version='" __OMP_CRT_ASSEMBLY_VERSION "' " \ "processorArchitecture='x86' " \ "publicKeyToken='" _OMP_VC_ASSEMBLY_PUBLICKEYTOKEN "'\"") #elif defined(_M_AMD64) #pragma comment(linker,"/manifestdependency:\"type='win32' " \ "name='" __OMP_LIBRARIES_ASSEMBLY_NAME_PREFIX ".OpenMP' " \ "version='" __OMP_CRT_ASSEMBLY_VERSION "' " \ "processorArchitecture='amd64' " \ "publicKeyToken='" _OMP_VC_ASSEMBLY_PUBLICKEYTOKEN "'\"") #elif defined(_M_IA64) #pragma comment(linker,"/manifestdependency:\"type='win32' " \ "name='" __OMP_LIBRARIES_ASSEMBLY_NAME_PREFIX ".OpenMP' " \ "version='" __OMP_CRT_ASSEMBLY_VERSION "' " \ "processorArchitecture='ia64' " \ "publicKeyToken='" _OMP_VC_ASSEMBLY_PUBLICKEYTOKEN "'\"") #endif #endif // _DEBUG
This is necessary for correct loading of the manifest in executable and .dll files. Do not forget that even if OpenMP is used in loadable .dll files, you must also register the manifest for the executable file!
The values of __OMP_LIBRARIES_ASSEMBLY_NAME_PREFIX, _OMP_VC_ASSEMBLY_PUBLICKEYTOKEN and __OMP_CRT_ASSEMBLY_VERSION are taken from <crtassem.h> included in the Visual C ++ 2005 shipment.
The project is still not going to - now the studio is outraged to the limit by the fact that the symbol __You_must_link_with_Microsoft_OpenMP_library is not defined.
Yes, it was a very subtle hint from the compiler.
Let's look at the contents of the generated .obj file. In my opinion, the
objconv utility in disassembler mode is best suited for this.
We find out that we need to define a byte size variable with the specified name. Unfortunately, in C and C ++ we will not be able to accurately recreate the imported character, so we will have to use MASM32.
Add a meaningless variable:
PUBLIC __You_must_link_with_Microsoft_OpenMP_library data segment __You_must_link_with_Microsoft_OpenMP_library db 1 data ends end
compile:
ml /c antiomp.asm
and we add the antiomp.obj output to the Additional Input of projects using OpenMP.
All - we should have a working code. You can check the OpenMP version in two ways:
- run the application, pick up a debugger and find the OpenMP library in the list of loaded modules (Debug | Windows | Modules)
- try to find in the executable files the substring vcomp100. If everything is done according to the instructions, then this line should not be
Have a nice parallel programming!