silx-kit / pyFAI

Fast Azimuthal Integration in Python
Other
105 stars 95 forks source link

running the AzimuthalIntegration with OpenCL in multiprocessing #1652

Closed malte-storm closed 2 years ago

malte-storm commented 2 years ago

Hi,

I have a problem with running the AzimuthalIntegration in independent processes (or even in just 1 process) with OpenCL in Linux. A code example is given below. If I run it on Windows, it works fine. Also, if I run the code locally, everything works as well. The same if I use the "cython" method, then also the independent process on Linux works fine.

The issue occurs in line 1328 of the azimuthalIntegrator.py . This call never returns and I receive the following warnings: WARNING:silx.opencl.common:Unable to create context on 0/0: clGetDeviceIDs failed: INVALID_DEVICE WARNING:silx.opencl.common:Last chance to get an OpenCL device ... probably not the one requested

(or multiple versions for multiple processes). I am a bit lost here and I do not know if the issue originates in the pyFAI implementation or is due to the userlying silx OpenCL. Any help would be appreciated.

System stats (where the OpenCL code is not working): Linux: CentOS 7 Linux-Kernel 3.10 Python 3.9.10 pyFAI 0.21.2 silx 1.0.0 pyopencl 2022.1

A minimal code example to replicate the problem is given below.

import os
import time
import multiprocessing as mp

import numpy as np
import h5py
import hdf5plugin
from pyFAI.azimuthalIntegrator import AzimuthalIntegrator

_path = #####

MASK = np.load(os.path.join(_path, 'eiger_mask.npy'))
FILENAME = os.path.join(_path, 'test_data_000001.h5')

def process_data(ai, raw_data):
    _q, _I = ai.integrate1d(raw_data, 1600, mask=MASK,
                            method=('bbox', 'csr', 'opencl'))
    return _I

def processor(inQ, outQ):
    ai = AzimuthalIntegrator(detector='Eiger 9M')
    ai.read(os.path.join(_path, 'pyfai_calib.poni'))

    with h5py.File(FILENAME, 'r') as _file:
        raw = _file['/entry/data/data'][0, ...]
    while True:
        try:
            _index = inQ.get()
            if _index is None:
                break
            _I = process_data(ai, raw)
            outQ.put([_index, _I])
        except Exception as e:
            break

if __name__ == '__main__':
    _n_workers = 4
    _n_calls = 10

    inQ = mp.Queue()
    outQ = mp.Queue()

    workers = [mp.Process(target=processor, args=(inQ,outQ))
               for _ in range(_n_workers)]

    for w in workers:
        w.start()

    for i in range(_n_calls):
        inQ.put(i)

    for i in range(_n_calls):
        _, _ = outQ.get()

    for i in range(_n_workers):
        inQ.put(None)

    for w in workers:
        w.join()
kif commented 2 years ago

Hi, I already met this a couple of years ago ... this was related to the way multiprocessing was starting other executables and the way OpenCL was initiating contexts.

You are running under linux and I guess everything works without "multiprocessing" ? Have a look at: https://docs.python.org/3/library/multiprocessing.html#multiprocessing.get_all_start_methods and switch from "fork" to "spawn". It should make it.

malte-storm commented 2 years ago

Thanks for the tip, that works well.