-
This library is incredible. The overhead on very often-called, but fast functions (runtime of sub-microsecond) on a derived type is something like an order of magnitude faster. I'm moving from pybind11 to nanobind as we speak. One thing I was unclear of is how STL containers, namely auto indexer (int i, const std::vector<double> &e){
return e[i];
} does Related: it would be nice to add In pybind11 we had the ability to specify Alternatively, how would one access a numpy array buffer (1d of double) in |
Beta Was this translation helpful? Give feedback.
Replies: 8 comments 11 replies
-
Hi @ianhbell. issues are reserved for things that are serious problems with the library. I have converted this into a discussion. Now with regards to the questions you raised:
Wenzel |
Beta Was this translation helpful? Give feedback.
-
Sorry about that, I couldn't figure out where to ask the question other than via issues and this is clearly not a bug. Could you add that to the issue template to make it more clear somehow? Or add the discussion link to the main README? You are right, I thought Is it possible to put together an example of zero-cost access to a 1D array from Python? I am sure that would be widely useful for people. I couldn't find such an example going through all the examples and tests. |
Beta Was this translation helpful? Give feedback.
-
Here was my best attempt to call a function that indexes a #include <nanobind/nanobind.h>
#include <nanobind/tensor.h>
#include <Eigen/Dense>
namespace nb = nanobind;
using namespace nb::literals;
using tensor1d = nb::tensor<double, nb::shape<nb::any>, nb::c_contig, nb::device::cpu>;
auto construct_Eigen_tensor_view(const tensor1d& tensor){
return Eigen::Map<const Eigen::ArrayXd>(static_cast<const double*>(tensor.data()), tensor.shape(0));
}
auto indexer(const Eigen::ArrayXd &x, int i){
return x(i);
}
NB_MODULE(n2flann, m) {
m.def("indexer", [](const tensor1d& ten, int i){ return indexer(construct_Eigen_tensor_view(ten), i);}, "x"_a.noconvert(), "i"_a);
m.attr("__version__") = "0.0.0";
} with the calling code:
|
Beta Was this translation helpful? Give feedback.
-
Eigen is column-major by default: https://eigen.tuxfamily.org/dox/group__TopicStorageOrders.html "If the storage order is not specified, then Eigen defaults to storing the entry in column-major." |
Beta Was this translation helpful? Give feedback.
-
Sorry I read your response too quickly. The I tried to break down the problem into parts and it seems the problem is the passing of the Eigen::Map to the indexing function. Making the map is fast, both locally and in another function that returns it. It is only when passing the map to another function that the copy happens. This isn't is a nanobind problem, but it would be good to have some of these examples documented somewhere so that others can avoid re-inventing the wheel if Eigen casters are no longer available.
from #include <nanobind/nanobind.h>
#include <nanobind/tensor.h>
#include <Eigen/Dense>
namespace nb = nanobind;
using namespace nb::literals;
using tensor1d = nb::tensor<double, nb::shape<nb::any>, nb::f_contig, nb::device::cpu>;
Eigen::Map<const Eigen::ArrayXd> construct_Eigen_tensor_view(const tensor1d& tensor){
return Eigen::Map<const Eigen::ArrayXd>(static_cast<const double*>(tensor.data()), tensor.shape(0));
}
auto indexer(const Eigen::ArrayXd &x, int i){
return x(i);
}
NB_MODULE(n2flann, m) {
m.def("indexer", [](const tensor1d& ten, int i){
return indexer(construct_Eigen_tensor_view(ten), i);},
"x"_a.noconvert(), "i"_a);
m.def("indexer2", [](const tensor1d& ten, int i){
Eigen::Map<const Eigen::ArrayXd> view(static_cast<const double*>(ten.data()), ten.shape(0));
return view(i);},
"x"_a.noconvert(), "i"_a);
m.def("indexer3", [](const tensor1d& ten, int i){
auto view = construct_Eigen_tensor_view(ten);
return view(i);},
"x"_a.noconvert(), "i"_a);
m.attr("__version__") = "0.0.0";
}
|
Beta Was this translation helpful? Give feedback.
-
A bit more digging: this doesn't seem to work properly in |
Beta Was this translation helpful? Give feedback.
-
My previous point stands, |
Beta Was this translation helpful? Give feedback.
-
This would be the missing feature in my case to migrate all my pybind projects to nanobind! I'm still getting to know nanobind, and trying to adapt my existing codebase to have In pybind11 I have an ugly adapter between both, similar to Open3D but I'm still tring to figure out how to do the same for With nanobind I have the following draft: // tensor view of pointcloud
using vector3dvector = nb::tensor<nb::numpy, double, nb::shape<nb::any, 3>, nb::device::cpu>;
std::vector<Eigen::Vector3d> _to_eigen(vector3dvector tensor) {
std::vector<Eigen::Vector3d> eigen_vectors(tensor.shape(0));
for (auto i = 0; i < tensor.shape(0); ++i) {
eigen_vectors[i] = Eigen::Map<Eigen::Vector3d>(&tensor(i, 0));
}
return eigen_vectors;
}
auto _to_numpy(std::vector<Eigen::Vector3d>&& points) {
size_t shape[2] = {points.size(), 3};
return vector3dvector(points.data(), 2, shape);
} Any ideas? |
Beta Was this translation helpful? Give feedback.
My previous point stands,
Eigen::Ref<...>
->const Eigen::ArrayXd&
always invokes a copy. So, how should one make a function that calls to a function in an API that takesconst Eigen::ArrayXd&
as an argument?