Page Not Found
We could not find what you were looking for.
Please contact the owner of the site that linked you to the original URL and let them know their link is broken.
From 3de8cf929b4e4e710221c9fa9b028096776859c5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 13 Jan 2025 10:05:33 +0000 Subject: [PATCH] deploy: 0d882f8907a77795c879dc2e2acb022a98c146b0 --- 404.html | 4 ++-- assets/js/{1c4ae0dc.f844d960.js => 1c4ae0dc.b3c53158.js} | 2 +- assets/js/{1cd62bc7.ae7e9acf.js => 1cd62bc7.c610c713.js} | 2 +- assets/js/{32b584a3.97b27bd2.js => 32b584a3.ee6d4c10.js} | 2 +- assets/js/{b2f554cd.0cfabe3d.js => b2f554cd.505c317f.js} | 2 +- assets/js/{dd05a678.a0a4af3a.js => dd05a678.00795e33.js} | 2 +- assets/js/{main.7e1fbb86.js => main.e0c5ef23.js} | 4 ++-- ...1fbb86.js.LICENSE.txt => main.e0c5ef23.js.LICENSE.txt} | 0 ...{runtime~main.df429403.js => runtime~main.a549d6e8.js} | 2 +- blog/archive/index.html | 6 +++--- blog/atom.xml | 4 ++-- blog/index.html | 8 ++++---- blog/rss.xml | 4 ++-- blog/rust-python/index.html | 8 ++++---- docs/api/c-api/index.html | 4 ++-- docs/api/cli/index.html | 4 ++-- docs/api/dataflow-config/index.html | 4 ++-- docs/examples/index.html | 6 +++--- docs/footers/privacy-policy/index.html | 4 ++-- docs/guides/Debugging/logs/index.html | 4 ++-- docs/guides/Debugging/metrics/index.html | 4 ++-- docs/guides/Debugging/tracing/index.html | 4 ++-- docs/guides/Development/Arrow/index.html | 4 ++-- docs/guides/Development/Cuda/index.html | 4 ++-- docs/guides/Development/dynamic-node/index.html | 4 ++-- docs/guides/Development/hot-reload/index.html | 4 ++-- docs/guides/Installation/installing/index.html | 4 ++-- docs/guides/Installation/uninstalling/index.html | 4 ++-- docs/guides/Installation/updating/index.html | 4 ++-- docs/guides/dora-drives/carla/index.html | 4 ++-- docs/guides/dora-drives/control/index.html | 4 ++-- docs/guides/dora-drives/index.html | 4 ++-- docs/guides/dora-drives/installation/index.html | 4 ++-- docs/guides/dora-drives/obstacle_location/index.html | 4 ++-- docs/guides/dora-drives/planning/index.html | 4 ++-- docs/guides/dora-ros2-bridges/index.html | 4 ++-- docs/guides/getting-started/conversation_py/index.html | 4 ++-- docs/guides/getting-started/llm/index.html | 4 ++-- docs/guides/getting-started/webcam_plot/index.html | 4 ++-- docs/guides/getting-started/yolov8/index.html | 4 ++-- docs/guides/index.html | 4 ++-- docs/guides/support-matrix/index.html | 4 ++-- docs/nodes/index.html | 6 +++--- docs/nodes_operators/fot_op/index.html | 4 ++-- docs/nodes_operators/midas_op/index.html | 4 ++-- docs/nodes_operators/obstacle_location_op/index.html | 4 ++-- docs/nodes_operators/pid_control_op/index.html | 4 ++-- docs/nodes_operators/plot/index.html | 4 ++-- docs/nodes_operators/strong_sort_op/index.html | 4 ++-- docs/nodes_operators/webcam_op/index.html | 4 ++-- docs/nodes_operators/yolop_op/index.html | 4 ++-- docs/nodes_operators/yolov5_op/index.html | 4 ++-- docs/references/communication-layer/index.html | 4 ++-- docs/references/index.html | 4 ++-- docs/references/library-vs-framework/index.html | 4 ++-- docs/references/overview/index.html | 4 ++-- docs/references/state-management/index.html | 4 ++-- index.html | 4 ++-- installation-scripts/index.html | 4 ++-- search/index.html | 4 ++-- zh-CN/404.html | 4 ++-- .../js/{0ef6350b.1ce56390.js => 0ef6350b.497d8ec3.js} | 2 +- .../js/{1c4ae0dc.199b35f4.js => 1c4ae0dc.c5a8848b.js} | 2 +- .../js/{1cd62bc7.85a366fa.js => 1cd62bc7.33914947.js} | 2 +- .../js/{32b584a3.358999d3.js => 32b584a3.c68ec507.js} | 2 +- .../js/{50bc71d4.f378d3bf.js => 50bc71d4.fb07efd1.js} | 2 +- zh-CN/assets/js/{main.5b3f751b.js => main.d75a84d9.js} | 4 ++-- ...3f751b.js.LICENSE.txt => main.d75a84d9.js.LICENSE.txt} | 0 ...{runtime~main.440ad345.js => runtime~main.84a7929a.js} | 2 +- zh-CN/blog/archive/index.html | 6 +++--- zh-CN/blog/atom.xml | 4 ++-- zh-CN/blog/index.html | 8 ++++---- zh-CN/blog/rss.xml | 4 ++-- zh-CN/blog/rust-python/index.html | 8 ++++---- zh-CN/docs/api/c-api/index.html | 4 ++-- zh-CN/docs/api/cli/index.html | 4 ++-- zh-CN/docs/api/dataflow-config/index.html | 4 ++-- zh-CN/docs/examples/index.html | 6 +++--- zh-CN/docs/footers/privacy-policy/index.html | 4 ++-- zh-CN/docs/guides/Debugging/logs/index.html | 4 ++-- zh-CN/docs/guides/Debugging/metrics/index.html | 4 ++-- zh-CN/docs/guides/Debugging/tracing/index.html | 4 ++-- zh-CN/docs/guides/Development/Arrow/index.html | 4 ++-- zh-CN/docs/guides/Development/Cuda/index.html | 4 ++-- zh-CN/docs/guides/Development/dynamic-node/index.html | 4 ++-- zh-CN/docs/guides/Development/hot-reload/index.html | 4 ++-- zh-CN/docs/guides/Installation/installing/index.html | 4 ++-- zh-CN/docs/guides/Installation/uninstalling/index.html | 4 ++-- zh-CN/docs/guides/Installation/updating/index.html | 4 ++-- zh-CN/docs/guides/dora-drives/carla/index.html | 4 ++-- zh-CN/docs/guides/dora-drives/control/index.html | 4 ++-- zh-CN/docs/guides/dora-drives/index.html | 4 ++-- zh-CN/docs/guides/dora-drives/installation/index.html | 4 ++-- .../docs/guides/dora-drives/obstacle_location/index.html | 4 ++-- zh-CN/docs/guides/dora-drives/planning/index.html | 4 ++-- zh-CN/docs/guides/dora-ros2-bridges/index.html | 4 ++-- .../guides/getting-started/conversation_py/index.html | 4 ++-- zh-CN/docs/guides/getting-started/llm/index.html | 4 ++-- zh-CN/docs/guides/getting-started/webcam_plot/index.html | 4 ++-- zh-CN/docs/guides/getting-started/yolov8/index.html | 4 ++-- zh-CN/docs/guides/index.html | 4 ++-- zh-CN/docs/guides/support-matrix/index.html | 4 ++-- zh-CN/docs/nodes/index.html | 6 +++--- zh-CN/docs/nodes_operators/fot_op/index.html | 4 ++-- zh-CN/docs/nodes_operators/midas_op/index.html | 4 ++-- .../docs/nodes_operators/obstacle_location_op/index.html | 4 ++-- zh-CN/docs/nodes_operators/pid_control_op/index.html | 4 ++-- zh-CN/docs/nodes_operators/plot/index.html | 4 ++-- zh-CN/docs/nodes_operators/strong_sort_op/index.html | 4 ++-- zh-CN/docs/nodes_operators/webcam_op/index.html | 4 ++-- zh-CN/docs/nodes_operators/yolop_op/index.html | 4 ++-- zh-CN/docs/nodes_operators/yolov5_op/index.html | 4 ++-- zh-CN/docs/references/communication-layer/index.html | 4 ++-- zh-CN/docs/references/index.html | 4 ++-- zh-CN/docs/references/library-vs-framework/index.html | 4 ++-- zh-CN/docs/references/overview/index.html | 4 ++-- zh-CN/docs/references/state-management/index.html | 4 ++-- zh-CN/index.html | 4 ++-- zh-CN/installation-scripts/index.html | 4 ++-- zh-CN/search/index.html | 4 ++-- 120 files changed, 238 insertions(+), 238 deletions(-) rename assets/js/{1c4ae0dc.f844d960.js => 1c4ae0dc.b3c53158.js} (99%) rename assets/js/{1cd62bc7.ae7e9acf.js => 1cd62bc7.c610c713.js} (89%) rename assets/js/{32b584a3.97b27bd2.js => 32b584a3.ee6d4c10.js} (99%) rename assets/js/{b2f554cd.0cfabe3d.js => b2f554cd.505c317f.js} (99%) rename assets/js/{dd05a678.a0a4af3a.js => dd05a678.00795e33.js} (89%) rename assets/js/{main.7e1fbb86.js => main.e0c5ef23.js} (98%) rename assets/js/{main.7e1fbb86.js.LICENSE.txt => main.e0c5ef23.js.LICENSE.txt} (100%) rename assets/js/{runtime~main.df429403.js => runtime~main.a549d6e8.js} (96%) rename zh-CN/assets/js/{0ef6350b.1ce56390.js => 0ef6350b.497d8ec3.js} (89%) rename zh-CN/assets/js/{1c4ae0dc.199b35f4.js => 1c4ae0dc.c5a8848b.js} (99%) rename zh-CN/assets/js/{1cd62bc7.85a366fa.js => 1cd62bc7.33914947.js} (89%) rename zh-CN/assets/js/{32b584a3.358999d3.js => 32b584a3.c68ec507.js} (99%) rename zh-CN/assets/js/{50bc71d4.f378d3bf.js => 50bc71d4.fb07efd1.js} (99%) rename zh-CN/assets/js/{main.5b3f751b.js => main.d75a84d9.js} (99%) rename zh-CN/assets/js/{main.5b3f751b.js.LICENSE.txt => main.d75a84d9.js.LICENSE.txt} (100%) rename zh-CN/assets/js/{runtime~main.440ad345.js => runtime~main.84a7929a.js} (94%) diff --git a/404.html b/404.html index f87a9fb5..65b5c6a4 100644 --- a/404.html +++ b/404.html @@ -9,8 +9,8 @@ - - + +
We could not find what you were looking for.
Please contact the owner of the site that linked you to the original URL and let them know their link is broken.
create_list
for a very large list like: value = [1] * 100_000_000
is going to return in 2.27s \ud83d\ude9c",id:"-calling-create_list-for-a-very-large-list-like-value--1--100_000_000--is-going-to-return-in-227s-tractor",level:4},{value:"Implementation 2: PyBytes",id:"implementation-2-pybytes",level:3},{value:"> For the same list input, create_list_bytes
returns in 78 milliseconds. That's 30x better \ud83d\udc0e",id:"-for-the-same-list-input-create_list_bytes-returns-in-78-milliseconds-thats-30x-better-racehorse",level:4},{value:"Implementation 3: Apache Arrow",id:"implementation-3-apache-arrow",level:3},{value:"> Same list returns in 33 milliseconds . That's 2x better than PyBytes
\ud83d\udc0e\ud83d\udc0e",id:"-same-list-returns-in-33-milliseconds--thats-2x-better-than-pybytes-racehorseracehorse",level:4},{value:"Debugging",id:"debugging",level:2},{value:".unwrap()
",id:"unwrap",level:3},{value:"> Example error:",id:"-example-error",level:4},{value:"eyre",id:"eyre",level:3},{value:"> Same error as above but with eyre
which gives a better looking error message:",id:"-same-error-as-above-but-with-eyre-which-gives-a-better-looking-error-message",level:4},{value:"Python traceback with eyre
",id:"python-traceback-with-eyre",level:3},{value:"> Example error with no custom traceback:",id:"-example-error-with-no-custom-traceback",level:4},{value:"> Better errors with custom traceback:",id:"-better-errors-with-custom-traceback",level:4},{value:"Memory management",id:"memory-management",level:2},{value:"> Calling this function will consume 440MB of memory. \ud83d\udc4e",id:"-calling-this-function-will-consume-440mb-of-memory--1",level:4},{value:"> Calling this function will consume 80MB of memory. :thumbsup:",id:"-calling-this-function-will-consume-80mb-of-memory-thumbsup",level:4},{value:"Race condition",id:"race-condition",level:2},{value:"> This threaded print was printed after 10.0s. \ud83d\ude22",id:"-this-threaded-print-was-printed-after-100s-cry",level:4},{value:"> "1" was printed after 32\xb5s and "2" was printed after 80\xb5s, so there was no race condition. \ud83d\ude04",id:"-1-was-printed-after-32\xb5s-and-2-was-printed-after-80\xb5s-so-there-was-no-race-condition-smile",level:4},{value:"Tracing",id:"tracing",level:2}];function h(e){const n={a:"a",blockquote:"blockquote",code:"code",h1:"h1",h2:"h2",h3:"h3",h4:"h4",img:"img",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,a.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(n.p,{children:"Writing a rust library that is usable in multiple languages is not easy..."}),"\n",(0,r.jsxs)(n.p,{children:["This blogpost recollects things I have encountered while building ",(0,r.jsx)(n.a,{href:"https://github.com/webonnx/wonnx",children:"wonnx"})," and ",(0,r.jsx)(n.a,{href:"https://github.com/dora-rs/dora",children:"dora-rs"}),". I am going to use Rust-Python FFI through ",(0,r.jsx)(n.code,{children:"pyo3"})," as an example. You can then extrapolate those issues to other languages FFI."]}),"\n",(0,r.jsx)(n.h2,{id:"foreign-function-interface",children:"Foreign Function Interface"}),"\n",(0,r.jsx)(n.p,{children:"A foreign function interface (FFI) is an interface used to share data from different languages."}),"\n",(0,r.jsxs)(n.p,{children:["By default, python might not know what a Rust ",(0,r.jsx)(n.code,{children:"u16"})," is, so an interface is needed to make the two languages communicate."]}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.img,{src:"https://hackmd.io/_uploads/S1qiK8hRh.png",alt:""})}),"\n",(0,r.jsxs)(n.blockquote,{children:["\n",(0,r.jsxs)(n.p,{children:["Image from ",(0,r.jsx)(n.a,{href:"https://hacks.mozilla.org/2019/08/webassembly-interface-types/",children:"WebAssembly Interface Types: Interoperate with All the Things!"})]}),"\n"]}),"\n",(0,r.jsx)(n.p,{children:"Building interfaces is not easy. Most of the time, we have to use the C-ABI to build our FFI as it is the common denominator between languages."}),"\n",(0,r.jsx)(n.p,{children:"Thankfully, there are FFI libraries that create interfaces for us and we can just focus on the important stuff such as the logic, algorithm, and so on."}),"\n",(0,r.jsx)(n.p,{children:"However, those FFI libraries might have limitations. This is what we're going to discuss."}),"\n",(0,r.jsxs)(n.p,{children:["One example of such FFI library is ",(0,r.jsx)(n.a,{href:"https://github.com/PyO3/pyo3",children:(0,r.jsx)(n.code,{children:"pyo3"})}),". ",(0,r.jsx)(n.a,{href:"https://github.com/PyO3/pyo3",children:(0,r.jsx)(n.code,{children:"pyo3"})})," is one of the most used Rust-Python binding and creates FFIs for you. All we have to do is wrap our function with a ",(0,r.jsx)(n.code,{children:"#[pyfunction]"})," and that will make it usable in Python."]}),"\n",(0,r.jsx)(n.h2,{id:"interfacing-arrays",children:"Interfacing Arrays"}),"\n",(0,r.jsxs)(n.p,{children:["In this blog post, I'm going to build a toy Rust-Python project with ",(0,r.jsx)(n.code,{children:"pyo3"})," to illustrate the issues I have faced."]}),"\n",(0,r.jsxs)(n.p,{children:["You can try this blogpost at home by forking the ",(0,r.jsx)(n.a,{href:"https://github.com/haixuanTao/blogpost_ffi",children:"blogpost repository"}),"."]}),"\n",(0,r.jsx)(n.p,{children:"If you want to start from scratch, you can create a new project with:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"mkdir blogpost_ffi\nmaturin init # pyo3\n"})}),"\n",(0,r.jsx)(n.p,{children:"The default project will looks like this:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-rust",children:"use pyo3::prelude::*;\n\n/// Formats the sum of two numbers as string.\n#[pyfunction]\nfn sum_as_string(a: usize, b: usize) -> PyResultcreate_list
for a very large list like: value = [1] * 100_000_000
is going to return in 2.27s \ud83d\ude9c",id:"-calling-create_list-for-a-very-large-list-like-value--1--100_000_000--is-going-to-return-in-227s-tractor",level:4},{value:"Implementation 2: PyBytes",id:"implementation-2-pybytes",level:3},{value:"> For the same list input, create_list_bytes
returns in 78 milliseconds. That's 30x better \ud83d\udc0e",id:"-for-the-same-list-input-create_list_bytes-returns-in-78-milliseconds-thats-30x-better-racehorse",level:4},{value:"Implementation 3: Apache Arrow",id:"implementation-3-apache-arrow",level:3},{value:"> Same list returns in 33 milliseconds . That's 2x better than PyBytes
\ud83d\udc0e\ud83d\udc0e",id:"-same-list-returns-in-33-milliseconds--thats-2x-better-than-pybytes-racehorseracehorse",level:4},{value:"Debugging",id:"debugging",level:2},{value:".unwrap()
",id:"unwrap",level:3},{value:"> Example error:",id:"-example-error",level:4},{value:"eyre",id:"eyre",level:3},{value:"> Same error as above but with eyre
which gives a better looking error message:",id:"-same-error-as-above-but-with-eyre-which-gives-a-better-looking-error-message",level:4},{value:"Python traceback with eyre
",id:"python-traceback-with-eyre",level:3},{value:"> Example error with no custom traceback:",id:"-example-error-with-no-custom-traceback",level:4},{value:"> Better errors with custom traceback:",id:"-better-errors-with-custom-traceback",level:4},{value:"Memory management",id:"memory-management",level:2},{value:"> Calling this function will consume 440MB of memory. \ud83d\udc4e",id:"-calling-this-function-will-consume-440mb-of-memory--1",level:4},{value:"> Calling this function will consume 80MB of memory. :thumbsup:",id:"-calling-this-function-will-consume-80mb-of-memory-thumbsup",level:4},{value:"Race condition",id:"race-condition",level:2},{value:"> This threaded print was printed after 10.0s. \ud83d\ude22",id:"-this-threaded-print-was-printed-after-100s-cry",level:4},{value:"> "1" was printed after 32\xb5s and "2" was printed after 80\xb5s, so there was no race condition. \ud83d\ude04",id:"-1-was-printed-after-32\xb5s-and-2-was-printed-after-80\xb5s-so-there-was-no-race-condition-smile",level:4},{value:"Tracing",id:"tracing",level:2}];function h(e){const n={a:"a",blockquote:"blockquote",code:"code",h1:"h1",h2:"h2",h3:"h3",h4:"h4",img:"img",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,a.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(n.p,{children:"Writing a rust library that is usable in multiple languages is not easy..."}),"\n",(0,r.jsxs)(n.p,{children:["This blogpost recollects things I have encountered while building ",(0,r.jsx)(n.a,{href:"https://github.com/webonnx/wonnx",children:"wonnx"})," and ",(0,r.jsx)(n.a,{href:"https://github.com/dora-rs/dora",children:"dora-rs"}),". I am going to use Rust-Python FFI through ",(0,r.jsx)(n.code,{children:"pyo3"})," as an example. You can then extrapolate those issues to other languages FFI."]}),"\n",(0,r.jsx)(n.h2,{id:"foreign-function-interface",children:"Foreign Function Interface"}),"\n",(0,r.jsx)(n.p,{children:"A foreign function interface (FFI) is an interface used to share data from different languages."}),"\n",(0,r.jsxs)(n.p,{children:["By default, python might not know what a Rust ",(0,r.jsx)(n.code,{children:"u16"})," is, so an interface is needed to make the two languages communicate."]}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.img,{src:"https://hackmd.io/_uploads/S1qiK8hRh.png",alt:""})}),"\n",(0,r.jsxs)(n.blockquote,{children:["\n",(0,r.jsxs)(n.p,{children:["Image from ",(0,r.jsx)(n.a,{href:"https://hacks.mozilla.org/2019/08/webassembly-interface-types/",children:"WebAssembly Interface Types: Interoperate with All the Things!"})]}),"\n"]}),"\n",(0,r.jsx)(n.p,{children:"Building interfaces is not easy. Most of the time, we have to use the C-ABI to build our FFI as it is the common denominator between languages."}),"\n",(0,r.jsx)(n.p,{children:"Thankfully, there are FFI libraries that create interfaces for us and we can just focus on the important stuff such as the logic, algorithm, and so on."}),"\n",(0,r.jsx)(n.p,{children:"However, those FFI libraries might have limitations. This is what we're going to discuss."}),"\n",(0,r.jsxs)(n.p,{children:["One example of such FFI library is ",(0,r.jsx)(n.a,{href:"https://github.com/PyO3/pyo3",children:(0,r.jsx)(n.code,{children:"pyo3"})}),". ",(0,r.jsx)(n.a,{href:"https://github.com/PyO3/pyo3",children:(0,r.jsx)(n.code,{children:"pyo3"})})," is one of the most used Rust-Python binding and creates FFIs for you. All we have to do is wrap our function with a ",(0,r.jsx)(n.code,{children:"#[pyfunction]"})," and that will make it usable in Python."]}),"\n",(0,r.jsx)(n.h2,{id:"interfacing-arrays",children:"Interfacing Arrays"}),"\n",(0,r.jsxs)(n.p,{children:["In this blog post, I'm going to build a toy Rust-Python project with ",(0,r.jsx)(n.code,{children:"pyo3"})," to illustrate the issues I have faced."]}),"\n",(0,r.jsxs)(n.p,{children:["You can try this blogpost at home by forking the ",(0,r.jsx)(n.a,{href:"https://github.com/haixuanTao/blogpost_ffi",children:"blogpost repository"}),"."]}),"\n",(0,r.jsx)(n.p,{children:"If you want to start from scratch, you can create a new project with:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"mkdir blogpost_ffi\nmaturin init # pyo3\n"})}),"\n",(0,r.jsx)(n.p,{children:"The default project will looks like this:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-rust",children:"use pyo3::prelude::*;\n\n/// Formats the sum of two numbers as string.\n#[pyfunction]\nfn sum_as_string(a: usize, b: usize) -> PyResultcreate_list
for a very large list like: value = [1] * 100_000_000
is going to return in 2.27s \ud83d\ude9c",id:"-calling-create_list-for-a-very-large-list-like-value--1--100_000_000--is-going-to-return-in-227s-tractor",level:4},{value:"Implementation 2: PyBytes",id:"implementation-2-pybytes",level:3},{value:"> For the same list input, create_list_bytes
returns in 78 milliseconds. That's 30x better \ud83d\udc0e",id:"-for-the-same-list-input-create_list_bytes-returns-in-78-milliseconds-thats-30x-better-racehorse",level:4},{value:"Implementation 3: Apache Arrow",id:"implementation-3-apache-arrow",level:3},{value:"> Same list returns in 33 milliseconds . That's 2x better than PyBytes
\ud83d\udc0e\ud83d\udc0e",id:"-same-list-returns-in-33-milliseconds--thats-2x-better-than-pybytes-racehorseracehorse",level:4},{value:"Debugging",id:"debugging",level:2},{value:".unwrap()
",id:"unwrap",level:3},{value:"> Example error:",id:"-example-error",level:4},{value:"eyre",id:"eyre",level:3},{value:"> Same error as above but with eyre
which gives a better looking error message:",id:"-same-error-as-above-but-with-eyre-which-gives-a-better-looking-error-message",level:4},{value:"Python traceback with eyre
",id:"python-traceback-with-eyre",level:3},{value:"> Example error with no custom traceback:",id:"-example-error-with-no-custom-traceback",level:4},{value:"> Better errors with custom traceback:",id:"-better-errors-with-custom-traceback",level:4},{value:"Memory management",id:"memory-management",level:2},{value:"> Calling this function will consume 440MB of memory. \ud83d\udc4e",id:"-calling-this-function-will-consume-440mb-of-memory--1",level:4},{value:"> Calling this function will consume 80MB of memory. :thumbsup:",id:"-calling-this-function-will-consume-80mb-of-memory-thumbsup",level:4},{value:"Race condition",id:"race-condition",level:2},{value:"> This threaded print was printed after 10.0s. \ud83d\ude22",id:"-this-threaded-print-was-printed-after-100s-cry",level:4},{value:"> "1" was printed after 32\xb5s and "2" was printed after 80\xb5s, so there was no race condition. \ud83d\ude04",id:"-1-was-printed-after-32\xb5s-and-2-was-printed-after-80\xb5s-so-there-was-no-race-condition-smile",level:4},{value:"Tracing",id:"tracing",level:2}];function h(e){const n={a:"a",blockquote:"blockquote",code:"code",h1:"h1",h2:"h2",h3:"h3",h4:"h4",img:"img",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,a.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(n.p,{children:"Writing a rust library that is usable in multiple languages is not easy..."}),"\n",(0,r.jsxs)(n.p,{children:["This blogpost recollects things I have encountered while building ",(0,r.jsx)(n.a,{href:"https://github.com/webonnx/wonnx",children:"wonnx"})," and ",(0,r.jsx)(n.a,{href:"https://github.com/dora-rs/dora",children:"dora-rs"}),". I am going to use Rust-Python FFI through ",(0,r.jsx)(n.code,{children:"pyo3"})," as an example. You can then extrapolate those issues to other languages FFI."]}),"\n",(0,r.jsx)(n.h2,{id:"foreign-function-interface",children:"Foreign Function Interface"}),"\n",(0,r.jsx)(n.p,{children:"A foreign function interface (FFI) is an interface used to share data from different languages."}),"\n",(0,r.jsxs)(n.p,{children:["By default, python might not know what a Rust ",(0,r.jsx)(n.code,{children:"u16"})," is, so an interface is needed to make the two languages communicate."]}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.img,{src:"https://hackmd.io/_uploads/S1qiK8hRh.png",alt:""})}),"\n",(0,r.jsxs)(n.blockquote,{children:["\n",(0,r.jsxs)(n.p,{children:["Image from ",(0,r.jsx)(n.a,{href:"https://hacks.mozilla.org/2019/08/webassembly-interface-types/",children:"WebAssembly Interface Types: Interoperate with All the Things!"})]}),"\n"]}),"\n",(0,r.jsx)(n.p,{children:"Building interfaces is not easy. Most of the time, we have to use the C-ABI to build our FFI as it is the common denominator between languages."}),"\n",(0,r.jsx)(n.p,{children:"Thankfully, there are FFI libraries that create interfaces for us and we can just focus on the important stuff such as the logic, algorithm, and so on."}),"\n",(0,r.jsx)(n.p,{children:"However, those FFI libraries might have limitations. This is what we're going to discuss."}),"\n",(0,r.jsxs)(n.p,{children:["One example of such FFI library is ",(0,r.jsx)(n.a,{href:"https://github.com/PyO3/pyo3",children:(0,r.jsx)(n.code,{children:"pyo3"})}),". ",(0,r.jsx)(n.a,{href:"https://github.com/PyO3/pyo3",children:(0,r.jsx)(n.code,{children:"pyo3"})})," is one of the most used Rust-Python binding and creates FFIs for you. All we have to do is wrap our function with a ",(0,r.jsx)(n.code,{children:"#[pyfunction]"})," and that will make it usable in Python."]}),"\n",(0,r.jsx)(n.h2,{id:"interfacing-arrays",children:"Interfacing Arrays"}),"\n",(0,r.jsxs)(n.p,{children:["In this blog post, I'm going to build a toy Rust-Python project with ",(0,r.jsx)(n.code,{children:"pyo3"})," to illustrate the issues I have faced."]}),"\n",(0,r.jsxs)(n.p,{children:["You can try this blogpost at home by forking the ",(0,r.jsx)(n.a,{href:"https://github.com/haixuanTao/blogpost_ffi",children:"blogpost repository"}),"."]}),"\n",(0,r.jsx)(n.p,{children:"If you want to start from scratch, you can create a new project with:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"mkdir blogpost_ffi\nmaturin init # pyo3\n"})}),"\n",(0,r.jsx)(n.p,{children:"The default project will looks like this:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-rust",children:"use pyo3::prelude::*;\n\n/// Formats the sum of two numbers as string.\n#[pyfunction]\nfn sum_as_string(a: usize, b: usize) -> PyResultcreate_list
for a very large list like: value = [1] * 100_000_000
is going to return in 2.27s \ud83d\ude9c",id:"-calling-create_list-for-a-very-large-list-like-value--1--100_000_000--is-going-to-return-in-227s-tractor",level:4},{value:"Implementation 2: PyBytes",id:"implementation-2-pybytes",level:3},{value:"> For the same list input, create_list_bytes
returns in 78 milliseconds. That's 30x better \ud83d\udc0e",id:"-for-the-same-list-input-create_list_bytes-returns-in-78-milliseconds-thats-30x-better-racehorse",level:4},{value:"Implementation 3: Apache Arrow",id:"implementation-3-apache-arrow",level:3},{value:"> Same list returns in 33 milliseconds . That's 2x better than PyBytes
\ud83d\udc0e\ud83d\udc0e",id:"-same-list-returns-in-33-milliseconds--thats-2x-better-than-pybytes-racehorseracehorse",level:4},{value:"Debugging",id:"debugging",level:2},{value:".unwrap()
",id:"unwrap",level:3},{value:"> Example error:",id:"-example-error",level:4},{value:"eyre",id:"eyre",level:3},{value:"> Same error as above but with eyre
which gives a better looking error message:",id:"-same-error-as-above-but-with-eyre-which-gives-a-better-looking-error-message",level:4},{value:"Python traceback with eyre
",id:"python-traceback-with-eyre",level:3},{value:"> Example error with no custom traceback:",id:"-example-error-with-no-custom-traceback",level:4},{value:"> Better errors with custom traceback:",id:"-better-errors-with-custom-traceback",level:4},{value:"Memory management",id:"memory-management",level:2},{value:"> Calling this function will consume 440MB of memory. \ud83d\udc4e",id:"-calling-this-function-will-consume-440mb-of-memory--1",level:4},{value:"> Calling this function will consume 80MB of memory. :thumbsup:",id:"-calling-this-function-will-consume-80mb-of-memory-thumbsup",level:4},{value:"Race condition",id:"race-condition",level:2},{value:"> This threaded print was printed after 10.0s. \ud83d\ude22",id:"-this-threaded-print-was-printed-after-100s-cry",level:4},{value:"> "1" was printed after 32\xb5s and "2" was printed after 80\xb5s, so there was no race condition. \ud83d\ude04",id:"-1-was-printed-after-32\xb5s-and-2-was-printed-after-80\xb5s-so-there-was-no-race-condition-smile",level:4},{value:"Tracing",id:"tracing",level:2}];function h(e){const n={a:"a",blockquote:"blockquote",code:"code",h1:"h1",h2:"h2",h3:"h3",h4:"h4",img:"img",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,a.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(n.p,{children:"Writing a rust library that is usable in multiple languages is not easy..."}),"\n",(0,r.jsxs)(n.p,{children:["This blogpost recollects things I have encountered while building ",(0,r.jsx)(n.a,{href:"https://github.com/webonnx/wonnx",children:"wonnx"})," and ",(0,r.jsx)(n.a,{href:"https://github.com/dora-rs/dora",children:"dora-rs"}),". I am going to use Rust-Python FFI through ",(0,r.jsx)(n.code,{children:"pyo3"})," as an example. You can then extrapolate those issues to other languages FFI."]}),"\n",(0,r.jsx)(n.h2,{id:"foreign-function-interface",children:"Foreign Function Interface"}),"\n",(0,r.jsx)(n.p,{children:"A foreign function interface (FFI) is an interface used to share data from different languages."}),"\n",(0,r.jsxs)(n.p,{children:["By default, python might not know what a Rust ",(0,r.jsx)(n.code,{children:"u16"})," is, so an interface is needed to make the two languages communicate."]}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.img,{src:"https://hackmd.io/_uploads/S1qiK8hRh.png",alt:""})}),"\n",(0,r.jsxs)(n.blockquote,{children:["\n",(0,r.jsxs)(n.p,{children:["Image from ",(0,r.jsx)(n.a,{href:"https://hacks.mozilla.org/2019/08/webassembly-interface-types/",children:"WebAssembly Interface Types: Interoperate with All the Things!"})]}),"\n"]}),"\n",(0,r.jsx)(n.p,{children:"Building interfaces is not easy. Most of the time, we have to use the C-ABI to build our FFI as it is the common denominator between languages."}),"\n",(0,r.jsx)(n.p,{children:"Thankfully, there are FFI libraries that create interfaces for us and we can just focus on the important stuff such as the logic, algorithm, and so on."}),"\n",(0,r.jsx)(n.p,{children:"However, those FFI libraries might have limitations. This is what we're going to discuss."}),"\n",(0,r.jsxs)(n.p,{children:["One example of such FFI library is ",(0,r.jsx)(n.a,{href:"https://github.com/PyO3/pyo3",children:(0,r.jsx)(n.code,{children:"pyo3"})}),". ",(0,r.jsx)(n.a,{href:"https://github.com/PyO3/pyo3",children:(0,r.jsx)(n.code,{children:"pyo3"})})," is one of the most used Rust-Python binding and creates FFIs for you. All we have to do is wrap our function with a ",(0,r.jsx)(n.code,{children:"#[pyfunction]"})," and that will make it usable in Python."]}),"\n",(0,r.jsx)(n.h2,{id:"interfacing-arrays",children:"Interfacing Arrays"}),"\n",(0,r.jsxs)(n.p,{children:["In this blog post, I'm going to build a toy Rust-Python project with ",(0,r.jsx)(n.code,{children:"pyo3"})," to illustrate the issues I have faced."]}),"\n",(0,r.jsxs)(n.p,{children:["You can try this blogpost at home by forking the ",(0,r.jsx)(n.a,{href:"https://github.com/haixuanTao/blogpost_ffi",children:"blogpost repository"}),"."]}),"\n",(0,r.jsx)(n.p,{children:"If you want to start from scratch, you can create a new project with:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"mkdir blogpost_ffi\nmaturin init # pyo3\n"})}),"\n",(0,r.jsx)(n.p,{children:"The default project will looks like this:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-rust",children:"use pyo3::prelude::*;\n\n/// Formats the sum of two numbers as string.\n#[pyfunction]\nfn sum_as_string(a: usize, b: usize) -> PyResultYour Docusaurus site did not load properly.
\nA very common reason is a wrong site baseUrl configuration.
\nCurrent configured baseUrl = ${e} ${"/"===e?" (default value)":""}
\nWe suggest trying baseUrl =
\n