-
Notifications
You must be signed in to change notification settings - Fork 30
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
dpctl has issues with multiprocessing #1169
Comments
Actually, this reproducer does not need # run.py
import dpctl
import dpctl.memory as dpm
import multiprocessing
import os
def _exec(di):
print(os.getpid())
d = dpctl.select_default_device()
c = d.sycl_platform.default_context
q = dpctl.SyclQueue(c, d)
x = dpm.MemoryUSMDevice(40, queue=q)
x.memset(1)
print(2)
di["hey"] = [1,2,3]
def main():
print("starting")
with multiprocessing.Manager() as manager:
di_ = manager.dict()
p = multiprocessing.Process(
target=_exec,
args=(di_,)
)
p.start()
p.join(200)
print("Done")
if __name__ == "__main__":
d = dpctl.select_default_device()
c = d.sycl_platform.default_context
q = dpctl.SyclQueue(c, d)
x = dpm.MemoryUSMDevice(40, queue=q)
x.memset(1)
main() fails just the same:
My hunch is that DPC++ runtime global states do not like to be forked. Acting on such a hunch I whipped a C++ snippet building on a tutorial from geeks-for-geeks: // forking.cpp
#include <sycl/sycl.hpp>
#include <unistd.h>
#include <iostream>
int main(void) {
sycl::queue q0 { sycl::default_selector_v };
int *data_main = sycl::malloc_device<int>(10, q0);
q0.fill<int>(data_main, int(1), 10).wait();
sycl::free(data_main, q0);
std::cout << "PID before forking " << getpid() << std::endl;
pid_t c_pid = fork();
if (c_pid == -1) {
std::cout << "Fork failed" << std::endl;
std::terminate();
} else if (c_pid > 0) { // parent process
std::cout << "Parent PID after forking " << getpid() << std::endl;
sycl::queue q1 { sycl::default_selector_v };
int *data1 = sycl::malloc_device<int>(10, q1);
q1.fill<int>(data1, int(1), 10).wait();
sycl::free(data1, q1);
} else { // child process
std::cout << "Parent PID after forking " << getpid() << std::endl;
sycl::queue q2 {sycl::default_selector_v};
int *data2 = sycl::malloc_device<int>(10, q2);
q2.fill<int>(data2, int(1), 10).wait();
sycl::free(data2, q2);
}
return 0;
} Compiled this code with
|
Perhaps a better (proper forking) C++ reproducer: // forking.cpp
#include <sycl/sycl.hpp>
#include <unistd.h>
#include <sys/wait.h>
#include <iostream>
int main(void) {
sycl::queue q0 { sycl::default_selector_v };
int *data_main = sycl::malloc_device<int>(10, q0);
q0.fill<int>(data_main, int(1), 10).wait();
sycl::free(data_main, q0);
std::cout << "PID before forking " << getpid() << std::endl;
pid_t c_pid = fork();
if (c_pid < 0) {
std::cout << "Fork failed" << std::endl;
std::terminate();
} else if (c_pid > 0) { // parent process
int status = 0;
// just wait for the child process to do its thing
(void) waitpid(c_pid, &status, 0);
std::cout << "Status retuned by child process: " << status << std::endl;
} else { // child process
std::cout << "Child PID after forking " << getpid() << std::endl;
sycl::queue q2 {sycl::default_selector_v};
try {
int *data2 = sycl::malloc_device<int>(10, q2);
q2.fill<int>(data2, int(1), 10).wait();
sycl::free(data2, q2);
} catch (const std::exception &e) {
std::cerr << "Caught exception: " << e.what() << std::endl;
return -1;
}
return 0; // finish the child process
}
std::cout << "From parent process after joining" << std::endl;
return 0;
} Running:
Interestingly, replacing Using
I suppose this finding hints that presently |
Another useful variant, more closely related to the error seen in // $ cat forking.cpp
#include <sycl/sycl.hpp>
#include <unistd.h>
#include <sys/wait.h>
#include <iostream>
class my_filter_selector
{
public:
static constexpr int REJECT_DEVICE = -1;
my_filter_selector(const std::string &fs) : _impl(fs) {}
int operator()(const sycl::device &d) const { return _impl(d); };
private:
sycl::ext::oneapi::filter_selector _impl;
};
int main(void) {
sycl::queue q0 { sycl::default_selector_v };
int *data_main = sycl::malloc_device<int>(10, q0);
q0.fill<int>(data_main, int(1), 10).wait();
sycl::free(data_main, q0);
std::cout << "PID before forking " << getpid() << std::endl;
pid_t c_pid = fork();
if (c_pid < 0) {
std::cout << "Fork failed" << std::endl;
std::terminate();
} else if (c_pid > 0) { // parent process
int status = 0;
// just wait for the child process to do its thing
(void) waitpid(c_pid, &status, 0);
std::cout << "Status retuned by child process: " << status << std::endl;
} else { // child process
std::cout << "Child PID after forking " << getpid() << std::endl;
sycl::queue q2 { sycl::default_selector_v };
try {
int *data2 = sycl::malloc_host<int>(10, q2);
if (data2 == nullptr) {
throw std::runtime_error("Failed USM-host allocation");
}
q2.fill<int>(data2, int(1), 10).wait();
sycl::free(data2, q2);
} catch (const std::exception &e) {
std::cerr << "Caught exception: " << e.what() << std::endl;
return -1;
}
return 0; // finish the child process
}
std::cout << "From parent process after joining" << std::endl;
return 0;
}
|
Likely related to this defect reported in dpbench. |
I think it should be documented that |
@oleksandr-pavlyk totally agree. Especially because this limitation comes from level zero. Could we still use dpctl runtime with forks with opencl? |
Running this produces an output:
Uncommenting the line
# dpt.ones(1)
above call tomain()
causes the script to hang (looks like a deadlock).If in addition
x = dpt.ones(1, device="cpu")
is replaced withx = dpt.ones(1)
, the scripts errors out withThank you to @ZzEeKkAa for reporting a related issue which was distilled into this reproducer
The text was updated successfully, but these errors were encountered: