4

I am currently looking to speed up my code using the power of multiprocessing. However I am encountering some issues when it comes to calling the compiled code from python, as it seems that the compiled file disappears from the code's view when it includes any form of multiprocessing.

For instance, with the following test code:

#include <omp.h>

int main() {
    int thread_id;
#pragma omp parallel
    {
        thread_id = omp_get_thread_num();
    }
    return 0;
}

Here, I compile the program, then turn it into a .so file using the command

gcc -fopenmp -o theories/test.so -shared -fPIC -O2 test.c

I then attempt to run the said code from test.py:

from ctypes import CDLL
import os

absolute_path = os.path.dirname(os.path.abspath(__file__))
# imports the c libraries
test_lib_path = absolute_path + '/theories/test.so'
test = CDLL(test_lib_path)
test.main()
print('complete')

I get the following error:

FileNotFoundError: Could not find module 'C:\[my path]\theories\test.so' (or one of its dependencies). Try using the full path with constructor syntax.

However, when I comment out the multiprocessing element to get the follwing code:

#include <omp.h>

int main() {
    int thread_id;
    /*
#pragma omp parallel
    {
        thread_id = omp_get_thread_num();
    }
    */
    return 0;
}

I then have a perfect execution with the python program printing out "complete" at the end.

I'm wondering how this has come to happen, and how the code can seemingly be compiled fine but then throw problems only once it's called from python (also I have checked and the file is in fact created).

UPDATES:

  1. I have now checked that I have libgomp-1.dll installed
  2. I have uninstalled and reinstalled MinGW, with no change happening.
  3. I have installed a different, 64 bit version of gcc and, using a different (64 bit python 3.10) version of python have reproduced the same error. This also has libgomp-1.dll.
CristiFati
  • 38,250
  • 9
  • 50
  • 87
Arkleseisure
  • 416
  • 5
  • 19
  • I cannot reproduce the problem on replit with [this bash code](https://pastebin.com/jZHDferC). Same on another machine. Are you sure the directory exists? Is the environment correctly configured and GCC/GOMP correctly installed? Is the so library the right one? Besides, It looks like your OS is a Windows from the file path but SO files are shared library mainly for Linux, not Windows. Do you use WSL? Can you specify your target OS? – Jérôme Richard Jan 02 '23 at 00:41
  • @JérômeRichard I thought that my gcc was installed correctly but given this error I am starting to think that might not be the case. Also my system is indeed windows, I've just got into the bad habit of using .so files cos I saw it in a random tutorial online and it seems to work fine generally. Not sure if that may be another source of problems? – Arkleseisure Jan 02 '23 at 00:47
  • 1
    On Windows, the GOMP DLL should be found, otherwise, the loading will fail. You need the PATH environment variable to be configured so the GOMP DLL can be found and loaded when your dynamic library is loaded. Besides, I advised you to use standard extension (.dll files on Windows) so to avoid weird issue though it may not have an impact here. – Jérôme Richard Jan 02 '23 at 00:57
  • @JérômeRichard does GOMP need to be installed independently of GCC? – Arkleseisure Jan 02 '23 at 01:24
  • AFAIK, generally, no, but it depends of how GCC has been installed (it might be compiled without the support of GOMP though it is unusual). GOMP is a separate library generally located with other DLLs. On my Windows, GCC has been installed with MSys, it is added in the PATH environment variable and is located in `C:\msys64\mingw64\bin` (`libgomp-1.dll`). Do you have such a DLL file? – Jérôme Richard Jan 02 '23 at 01:47
  • @JérômeRichard I have just had a look, and I do indeed have such a file. – Arkleseisure Jan 02 '23 at 15:14
  • Note that the PATH is not update for all application dynamically. You should check it has been modified. The simplest solution to ensure the PATH is modified on Windows is to reboot the machine. – Jérôme Richard Jan 03 '23 at 20:46
  • @JérômeRichard I changed the path manually and rebooted visual studio... I had already checked that this updated the path used by changing the path to one that didn't exist. Since this gave the expected error, and the compiled versions of my code are running 40% faster with the 64 bit version I presume that the new gcc is being used properly. – Arkleseisure Jan 04 '23 at 16:17

2 Answers2

2

Note where the error message says "or one of its dependencies".

Try running ldd on your test.so file to see if it's completely linked.

EDIT1:

Generally, gcc requires binutils. (On ms-windows, I guess they could be combined.) Which means that you should have objdump.

If you run objdump -x test.so|more, you should see some lines starting with "NEEDED" in the "Dynamic section". Those are the shared libraries needed by this one.

Roland Smith
  • 42,427
  • 3
  • 64
  • 94
  • as I understand, ldd is a linux only command. I know I'm using .so files, but I am currently on a windows computer so I am having trouble doing this. – Arkleseisure Jan 02 '23 at 00:52
  • objdump worked fine but it did not print out a dynamic section, and I did not see the "NEEDED" you talked about. However, there was a section called Import tables, which said that it imported the files "KERNEL32.dll", "msvcrt.dll" and "libgomp-1.dll". Looking in my MINGW Installation Manager I saw that I had the package mingw32-libgomp-dll installed, which it tells me should give me the file libgomp-1.dll. The package is version 9.2.0-2, and under versions it has the flag "FIXME: data sheet unavailable; a compiler for this data category has yet to be implemented". – Arkleseisure Jan 02 '23 at 15:11
  • I think that that is the only thing that seemed off from the thing. – Arkleseisure Jan 02 '23 at 15:12
2

I think this has the same root cause as [SO]: Can't import dll module in Python (@CristiFati's answer) (also check [SO]: PyWin32 and Python 3.8.0 (@CristiFati's answer)).

A .dll (.so) is only loaded when its dependencies are successfully loaded (recursively).
[SO]: Python Ctypes - loading dll throws OSError: [WinError 193] %1 is not a valid Win32 application (@CristiFati's answer) focuses on a different error, but covers this topic.

When commenting out omp_get_thread_num call, the linker no longer links test.so to libgomp*.dll (as it doesn't need anything from it), and code runs fine (all required .dlls are found).

To fix the issue, you should add to os.add_dll_directory (before attempting to load the .so):

  1. libgomp*.dll's directory

  2. MinGW's bin directory

  3. Any other directory that may contain .dlls that are required (dependents)

To see a .dll dependencies, check [SO]: Discover missing module using command-line ("DLL load failed" error) (@CristiFati's answer).

Notes:

CristiFati
  • 38,250
  • 9
  • 50
  • 87
  • Thanks, although I should probably add that the most convenient answer is in fact Mad Physicist's answer in your first link, where they mention that you actually just have to set winmode=0 in the CDLL call. – Arkleseisure Jan 06 '23 at 23:07
  • So you're saying that **only** changing `test = CDLL(test_lib_path, winmode=0)`, makes the problem go away? – CristiFati Jan 07 '23 at 07:55
  • Yup, as I understand the default is winmode=None, which according to that answer "does not appear to respond to changes to os.environ['PATH'], sys.path or os.add_dll_directory". – Arkleseisure Jan 07 '23 at 08:57
  • That part (of the answer) is not entirely correct (also check the comments). I assume you have *libgomp\*.dll* directory in your *PATH*. I will edit my (other) answer to add some more information (and possibly test code). – CristiFati Jan 07 '23 at 09:12
  • Aha yes I see, I think i do indeed have the directory for the gomp dll on my path so I presume that's what fixed it. – Arkleseisure Jan 07 '23 at 09:22
  • I added some more information, which I hope sheds some light on the topic. – CristiFati Jan 07 '23 at 12:31