Skip to content

Commit

Permalink
disable GIL
Browse files Browse the repository at this point in the history
  • Loading branch information
jberg5 committed Jan 4, 2025
1 parent 898270b commit cabbf35
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 12 deletions.
43 changes: 34 additions & 9 deletions benchmark/timings-parallel.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
import numpy as np
import scipy.sparse as spa
from time import perf_counter_ns
from concurrent.futures import ThreadPoolExecutor


num_threads = proxsuite.proxqp.omp_get_max_threads()


def generate_mixed_qp(n, n_eq, n_in, seed=1):
Expand All @@ -23,7 +27,7 @@ def generate_mixed_qp(n, n_eq, n_in, seed=1):
u = A @ v
l = -1.0e20 * np.ones(m)

return P.toarray(), q, A[:n_eq, :], u[:n_eq], A[n_in:, :], u[n_in:], l[n_in:]
return P.toarray(), q, A[:n_eq, :], u[:n_eq], A[n_in:, :], l[n_in:], u[n_in:]


n = 500
Expand All @@ -32,36 +36,57 @@ def generate_mixed_qp(n, n_eq, n_in, seed=1):

num_qps = 128

print(f"Problem specs: {n=} {n_eq=} {n_in=}. Generating {num_qps} such problems.")
problems = [generate_mixed_qp(n, n_eq, n_in, seed=j) for j in range(num_qps)]
print(f"Generated problems. Solving {num_qps} problems with proxsuite.proxqp.omp_get_max_threads()={num_threads} threads.")

# qps = []
timings = {}
qps = proxsuite.proxqp.dense.VectorQP()

tic = perf_counter_ns()
for j in range(num_qps):
print("Setting up problem vector")
for H, g, A, b, C, l, u in problems:
qp = proxsuite.proxqp.dense.QP(n, n_eq, n_in)
H, g, A, b, C, u, l = generate_mixed_qp(n, n_eq, n_in, seed=j)
qp.init(H, g, A, b, C, l, u)
qp.settings.eps_abs = 1e-9
qp.settings.verbose = False
qp.settings.initial_guess = proxsuite.proxqp.InitialGuess.NO_INITIAL_GUESS
qps.append(qp)
timings["problem_data"] = (perf_counter_ns() - tic) * 1e-6
timings["setup_vector_of_qps"] = (perf_counter_ns() - tic) * 1e-6

print("Solving problem vector in parallel with default thread config")
tic = perf_counter_ns()
proxsuite.proxqp.dense.solve_in_parallel(qps=qps)
timings[f"solve_parallel_heuristics_threads"] = (perf_counter_ns() - tic) * 1e-6

print("Solving problem vector serially")
tic = perf_counter_ns()
for qp in qps:
qp.solve()
timings["solve_serial"] = (perf_counter_ns() - tic) * 1e-6

num_threads = proxsuite.proxqp.omp_get_max_threads()
print("Solving problem vector in parallel with various thread configs")
for j in range(1, num_threads):
tic = perf_counter_ns()
proxsuite.proxqp.dense.solve_in_parallel(j, qps)
proxsuite.proxqp.dense.solve_in_parallel(qps=qps, num_threads=j)
timings[f"solve_parallel_{j}_threads"] = (perf_counter_ns() - tic) * 1e-6

print("Solving each problem serially with dense backend.")
tic = perf_counter_ns()
for H, g, A, b, C, l, u in problems:
proxsuite.proxqp.dense.solve(H, g, A, b, C, l, u, initial_guess=proxsuite.proxqp.InitialGuess.NO_INITIAL_GUESS, eps_abs=1e-9)
timings["solve_serial_dense"] = (perf_counter_ns() - tic) * 1e-6

print("Solving each problem in parallel (with a ThreadPoolExecutor) with dense backend.")
def solve_problem(problem): # just a little helper function to keep things clean
H, g, A, b, C, l, u = problem
return proxsuite.proxqp.dense.solve(H, g, A, b, C, l, u, initial_guess=proxsuite.proxqp.InitialGuess.NO_INITIAL_GUESS, eps_abs=1e-9)

tic = perf_counter_ns()
proxsuite.proxqp.dense.solve_in_parallel(qps=qps)
timings[f"solve_parallel_heuristics_threads"] = (perf_counter_ns() - tic) * 1e-6
with ThreadPoolExecutor(max_workers=num_threads) as executor:
results = list(executor.map(solve_problem, problems))
timings["solve_parallel_dense"] = (perf_counter_ns() - tic) * 1e-6

for k, v in timings.items():
print(f"{k}: {v}ms")
print(f"{k}: {v:.3f}ms")
9 changes: 6 additions & 3 deletions bindings/python/src/expose-solve.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ solveDenseQp(nanobind::module_ m)
nanobind::arg("eps_duality_gap_abs") = nanobind::none(),
nanobind::arg("eps_duality_gap_rel") = nanobind::none(),
nanobind::arg("primal_infeasibility_solving") = false,
nanobind::arg("default_H_eigenvalue_estimate") = 0.);
nanobind::arg("default_H_eigenvalue_estimate") = 0.,
nanobind::call_guard<nanobind::gil_scoped_release>());

m.def(
"solve",
Expand Down Expand Up @@ -139,7 +140,8 @@ solveDenseQp(nanobind::module_ m)
nanobind::arg("eps_duality_gap_abs") = nanobind::none(),
nanobind::arg("eps_duality_gap_rel") = nanobind::none(),
nanobind::arg("primal_infeasibility_solving") = false,
nanobind::arg("default_H_eigenvalue_estimate") = 0.);
nanobind::arg("default_H_eigenvalue_estimate") = 0.,
nanobind::call_guard<nanobind::gil_scoped_release>());
}

} // namespace python
Expand Down Expand Up @@ -186,7 +188,8 @@ solveSparseQp(nanobind::module_ m)
nanobind::arg("eps_duality_gap_abs") = nanobind::none(),
nanobind::arg("eps_duality_gap_rel") = nanobind::none(),
nanobind::arg("primal_infeasibility_solving") = false,
nanobind::arg("default_H_eigenvalue_estimate") = 0.);
nanobind::arg("default_H_eigenvalue_estimate") = 0.,
nanobind::call_guard<nanobind::gil_scoped_release>());
}

} // namespace python
Expand Down

0 comments on commit cabbf35

Please sign in to comment.