Week 9
Eyes on the goal …. Hello everyone, hope this blog finds you well.
In this week we tackled a major issue, that could make the entire project go to crap, making this the most challenging but fun week in my GSoC journey upto now.
Aim :
My aim for this week is to reduce almost 12-15 Mb of disk space. Pocketpy is an small interpreter, the entire repository not being anything more than 3Mb in size. At this point my numpy directory itself is almost 15Mb in size. That means at this point numpy is an independent project, which cannot be merged into the main pocketpy repository. This was clearly mentioned to me as an expectation for a successful project by my mentors.
These are some of disadvantages of adding a project of high disk space to a lightweight project:
- Increases build time.
- Requires higher disk space usage.
- Introduces complexity in dependency management.
- Leads to longer download and setup times.
Work :
I raised the following issue this week -
- Issue#46 : Giving up on np.matmul will save about ~14Mb of disk space
I made the following pull requests this week -
- PR#43 : Added the boolean dtype
- PR#44 : Add round member function for ndarray class
-
PR#47 : Add an array creation registry.
The current code for array creation looks as follows. But there was a need for potentially better design. The improvement here is that, is it somehow possible to take in the
dtpye
dynamically and not separately in an if-else routine (make the vectors dynamically wrt to the dtpye given as well).
This will remove redundancy of same code and avoid bloating while building the executable.The new approach to tackle this is as follows : 1. Prepare a template that can be used to represent vector of any dimension. The definition looks something like this -
template <typename T, std::size_t N> struct nvector_impl { using type = std::vector<typename nvector_impl<T, N - 1>::type>; }; template <typename T> struct nvector_impl<T, 0> { using type = T; }; template <typename T, std::size_t N> using nvector = typename nvector_impl<T, N>::type;
- Then I can write a single function to transform a
nvector<int, N>
tonvector<T, N>
to adjust my code for various dtypes.
I thought of the function like this -template <typename T, std::size_t N> nvector<T, N> transform(nvector<int, N>& values) { nvector<T, N> result; if constexpr(N != 0) { for(auto& value: values) { result.push_back(transform<T, N - 1>(value)); } } else { result = T(values); } return result; }
- In the end, all of my snippets can be written by the same pattern.
template<std::size_t N> void register_f(py::module_& m){ m.def("array", [](const nvector<int_, N>& values, const std::string& dtype){ if(dtype == "bool"){ return std::unique_ptr<ndarray_base>(new ndarray_bool(transform<bool_>(values))); } // ... }); }
- PR#48 : Discard xflens for space relaxation.
This pull request fixes the issue I have mentioned above.
- PR#48 : Discard xflens for space relaxation.
Explaining everything on a deeper note the issue is as follows -
Since we wanted to make xtensor headers to be ideally < 5Mb to integrate with pocketpy main. I observed the following
04:24:51 |base|anurag_b@nirpan.org@DQW-221-51 include ±|main ✗|→ du -sh xtensor
1.9M xtensor
04:25:13 |base|anurag_b@nirpan.org@DQW-221-51 include ±|main ✗|→ du -sh xtensor-blas/
132K xtensor-blas/
04:25:23 |base|anurag_b@nirpan.org@DQW-221-51 include ±|main ✗|→ du -sh xflens
13M xflens
04:25:32 |base|anurag_b@nirpan.org@DQW-221-51 include ±|main ✗|→ du -sh xtl
836K xtl
You would notice that xtensor
is not that heavy in terms of disk size. Infact xflens
is contributing to about 90% of the size.
We are using xtensor-blas
at only one point in our code, to implement the matmul function here.
https://github.com/pocketpy/gsoc-2024-dev/blob/d0e6874475315f9a71d669678b4b9b71f94c1cb7/numpy/include/numpy.hpp#L236-L237
This is because xtensor does not support matmul
, it supports dot
in xt::linalg
which is dependent on xtensor-blas
which inturn depends onxflens
headers.
So this leads to the question that should we give up np.matmul from our module, if the use cases are partial in the first place. It will give us huge relaxation in disk space.
- PR#49 : Added a custom matmul implementation.
arr1 = np.array([1, 2, 3, 4, 5]) assert_equal(arr1 * arr1, np.array([1, 4, 9, 16, 25])) arr2 = np.array([[1.33, 2.66], [3.99, 5.33]]) assert np.allclose(arr2 * arr2, np.array([[1.7689, 7.0756], [15.9201, 28.4089]]))
Future Work :
In this upcoming week, we will write code to register more dtypes and fix existing issues that came across in the testing phase.