Skip to content

Commit 1803997

Browse files
committed
Refactoring and studying leaks reported by asan/lsan in pybind11
Apparently, CPython intentionally does not free all memory at shutdown. The line defining the scoped_interpreter triggers Py_InitializeFromConfig, where CPython allocates internal state.
1 parent 9ff677a commit 1803997

4 files changed

Lines changed: 20 additions & 8 deletions

File tree

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
cmake_minimum_required(VERSION 3.15..3.27)
1+
cmake_minimum_required(VERSION 3.15..4.2.3)
22
project(aoc-2023-21 LANGUAGES CXX)
33
set(CMAKE_CXX_STANDARD 23)
4-
#set(CMAKE_CXX_FLAGS "-Wall -Wconversion -Wextra -pedantic -fsanitize=address,pointer-overflow,signed-integer-overflow,undefined")
4+
set(CMAKE_CXX_FLAGS "-ggdb -Wall -Wconversion -Wextra -pedantic -fsanitize=address,pointer-overflow,signed-integer-overflow,undefined -fsanitize-address-use-after-scope -DGLIBCXX_DEBUG")
55
find_package(pybind11 REQUIRED)
66
add_executable(step_counter step_counter.cpp)
77
target_link_libraries(step_counter PRIVATE pybind11::embed)
8-
set(CMAKE_BUILD_TYPE Release)
8+
set(CMAKE_BUILD_TYPE Debug)

adventofcode/2023/day/21/lsan.supp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
leak:libpython
2+
leak:_PyObject_Malloc
3+
leak:_PyMem_RawMalloc
4+
leak:Py_InitializeFromConfig
5+
leak:PyRun_
6+
leak:_PyRuntime

adventofcode/2023/day/21/run

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
PYTHONMALLOC=malloc LSAN_OPTIONS=suppressions=lsan.supp ./build/step_counter < step_counter_sample_input.txt

adventofcode/2023/day/21/step_counter.cpp

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ void print(grid_t const& g)
4444
int main()
4545
{
4646
read_input();
47+
pybind11::scoped_interpreter const guard{};
4748
std::pair const ans = solve();
4849
std::cout << "Part one: " << ans.first << "\nPart two: " << ans.second << '\n';
4950
}
@@ -113,17 +114,21 @@ ii get_start_position(grid_t const& G)
113114
int64_t fit_polynomial_and_extrapolate(std::vector<int64_t> const& x, std::vector<int64_t> const& y)
114115
{
115116
namespace py = pybind11;
116-
py::scoped_interpreter const guard{};
117-
py::object const scipy_interpolate = py::module::import("scipy.interpolate");
117+
118+
py::gil_scoped_acquire gil;
119+
120+
py::object const scipy_interpolate = py::module_::import("scipy.interpolate");
121+
py::object const numpy_polynomial = py::module_::import("numpy.polynomial");
122+
118123
py::object const poly =
119124
scipy_interpolate.attr("lagrange")(py::array_t<int64_t>(x.size(), x.data()),
120125
py::array_t<int64_t>(y.size(), y.data()));
121-
//TODO does the call chain leak?
122-
py::buffer_info const coef_buf = poly.attr("coef").cast<py::array_t<double>>().request();
126+
127+
py::array_t<double> const coef_arr = poly.attr("coef").cast<py::array_t<double>>();
128+
py::buffer_info const coef_buf = coef_arr.request();
123129
std::vector<double> coef_vec(static_cast<double*>(coef_buf.ptr),
124130
static_cast<double*>(coef_buf.ptr) + coef_buf.size);
125131
std::ranges::reverse(coef_vec);
126-
py::object const numpy_polynomial = py::module::import("numpy.polynomial");
127132
return std::llround(numpy_polynomial.attr("Polynomial")
128133
(py::array_t<double>(coef_vec.size(), coef_vec.data()))
129134
(26501365).cast<double>());

0 commit comments

Comments
 (0)