changelog shortlog graph tags branches changeset file revisions annotate raw help

Mercurial > org > notes / 20231105.org

revision 9: 4839b0675118
parent 4: 812feca5a874
     1.1--- a/20231105.org	Sat Jul 27 02:45:34 2024 -0400
     1.2+++ b/20231105.org	Sun Aug 11 14:46:59 2024 -0400
     1.3@@ -1,6 +1,12 @@
     1.4 * DRAFT dylib-skel-1
     1.5+:PROPERTIES:
     1.6+:ID:       b4d1bc91-f344-45fd-becc-cb20f00a3a61
     1.7+:END:
     1.8 - State "DRAFT"      from              [2023-11-05 Sun 22:23]
     1.9 ** Overview
    1.10+:PROPERTIES:
    1.11+:ID:       2e490c4b-344e-4790-9184-1c05ba675f15
    1.12+:END:
    1.13 Our core languages are [[https://www.rust-lang.org/][Rust]] and [[https://lisp-lang.org/][Lisp]] - this is the killer combo which will allow NAS-T
    1.14 to rapidly develop high-quality software. As such, it's crucial that these two very
    1.15 different languages (i.e. compilers) are able to interoperate seamlessly.
    1.16@@ -14,12 +20,18 @@
    1.17 code-base, but it's definitely something worth adding to the toolbox in case we need it.
    1.18 
    1.19 ** FFI
    1.20+:PROPERTIES:
    1.21+:ID:       985019fc-612a-44ab-b726-b9067432ad87
    1.22+:END:
    1.23 The level of interop we're after in this case is [[https://en.wikipedia.org/wiki/Foreign_function_interface][FFI]].
    1.24 
    1.25 Basically, calling Rust code from Lisp and vice-versa. There's an article about calling
    1.26 Rust from Common Lisp [[https://dev.to/veer66/calling-rust-from-common-lisp-45c5][here]] which shows the basics and serves as a great starting point
    1.27 for those interested.
    1.28 *** Rust != C
    1.29+:PROPERTIES:
    1.30+:ID:       2f71a3c1-0b14-46a6-9d8d-f6ec697729cc
    1.31+:END:
    1.32 The complication(s) with Rust I mentioned early is really just that /it is not C/. =C=
    1.33 is old, i.e. well-supported with a stable ABI, making the process of creating bindings
    1.34 for a C library a breeze in many languages.
    1.35@@ -31,17 +43,29 @@
    1.36 like the C ABI can.
    1.37 
    1.38 *** Overhead
    1.39+:PROPERTIES:
    1.40+:ID:       4ea79f68-55ec-4da3-a184-8343d49532b6
    1.41+:END:
    1.42 Using FFI involves some overhead. Check [[https://github.com/dyu/ffi-overhead][here]] for an example benchmark across a few
    1.43 languages. While building the NAS-T core, I'm very much aware of this, and will need a
    1.44 few sanity benchmarks to make sure the cost doesn't outweigh the benefit. In particular,
    1.45 I'm concerned about crossing multiple language barriers (Rust<->C<->Lisp).
    1.46 
    1.47 ** Rust -> C -> Lisp
    1.48+:PROPERTIES:
    1.49+:ID:       a498276c-8525-4a43-aa40-4b05f76a29a9
    1.50+:END:
    1.51 *** Setup
    1.52+:PROPERTIES:
    1.53+:ID:       19f96ef7-af92-496e-9d42-70c4d4c85051
    1.54+:END:
    1.55 For starters, I'm going to assume we all have Rust (via =rustup=) and Lisp (=sbcl= only)
    1.56 installed on our GNU/Linux system (some tweaks needed for Darwin/Windows, not covered in
    1.57 this post).
    1.58 **** Cargo
    1.59+:PROPERTIES:
    1.60+:ID:       c929e0b6-b6f2-4383-9412-1610329ab28c
    1.61+:END:
    1.62 Create a new library crate. For this example we're focusing on a 'skeleton' for
    1.63 /dynamic/ libraries only, so our experiment will be called =dylib-skel= or *dysk* for
    1.64 short.
    1.65@@ -64,7 +88,13 @@
    1.66 This tells Rust to generate a shared C-compatible object with a =.so= extension which we
    1.67 can open using [[https://man.archlinux.org/man/dlopen.3.en][dlopen]].
    1.68 **** cbindgen
    1.69+:PROPERTIES:
    1.70+:ID:       256ac288-c5a0-473a-ab65-2d6503bd423c
    1.71+:END:
    1.72 ***** install
    1.73+:PROPERTIES:
    1.74+:ID:       fc476f64-6b68-417a-8540-ca23ce27fa25
    1.75+:END:
    1.76 Next, we want the =cbindgen= program which we'll use to generate header files for
    1.77 C/C++. This step isn't necessary at all, we just want it for further experimentation.
    1.78 
    1.79@@ -76,6 +106,9 @@
    1.80 cbindgen = "0.24"
    1.81 #+end_src
    1.82 ***** cbindgen.toml
    1.83+:PROPERTIES:
    1.84+:ID:       111e27f7-0b9c-4eef-9117-f7c8ba3f511c
    1.85+:END:
    1.86 #+begin_src conf-toml :tangle cbindgen.toml
    1.87 language = "C"
    1.88 autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */"
    1.89@@ -92,6 +125,9 @@
    1.90 header = '"dysk.h"'
    1.91 #+end_src
    1.92 ***** build.rs
    1.93+:PROPERTIES:
    1.94+:ID:       9fc271b2-9acb-4f4b-aa61-82d60d2ddb9e
    1.95+:END:
    1.96 #+begin_src rust :tangle build.rs
    1.97 fn main() -> Result<(), cbindgen::Error> {
    1.98   if let Ok(b) = cbindgen::generate(std::env::var("CARGO_MANIFEST_DIR").unwrap()) {
    1.99@@ -99,6 +135,9 @@
   1.100   else { panic!("failed to generate dysk.h from cbindgen.toml") } }
   1.101 #+end_src
   1.102 *** lib.rs
   1.103+:PROPERTIES:
   1.104+:ID:       6b524921-2ae0-43f0-bb85-d9955b0e689c
   1.105+:END:
   1.106 #+begin_src rust :tangle lib.rs
   1.107 //! lib.rs --- dysk library
   1.108 use std::ffi::{c_char, c_int, CString};
   1.109@@ -111,15 +150,24 @@
   1.110 pub extern "C" fn dysk_plus1(n:c_int) -> c_int {n+1}
   1.111 #+end_src
   1.112 *** test.rs
   1.113+:PROPERTIES:
   1.114+:ID:       cc7c6538-33a6-40c6-94ef-2a9c259c975a
   1.115+:END:
   1.116 #+begin_src rust :tangle test.rs
   1.117 //! test.rs --- dysk test
   1.118 fn main() { let mut i = 0u32; while i < 500000000 {i+=1; dysk::dysk_plus1(2 as core::ffi::c_int);}}
   1.119 #+end_src
   1.120 *** compile
   1.121+:PROPERTIES:
   1.122+:ID:       337a24d1-f305-4e1a-9052-47a53591cb2f
   1.123+:END:
   1.124 #+begin_src sh
   1.125 cargo build --release
   1.126 #+end_src
   1.127 *** load from SBCL
   1.128+:PROPERTIES:
   1.129+:ID:       a4813269-92fb-4f52-aef0-3a36dce3cf69
   1.130+:END:
   1.131 #+begin_src lisp :tangle dysk.lisp
   1.132 (load-shared-object #P"target/release/libdysk.so")
   1.133 (define-alien-routine dysk-hello c-string)
   1.134@@ -128,6 +176,9 @@
   1.135 (dysk-hello) ;; => "hello from rust"
   1.136 #+end_src
   1.137 *** benchmark
   1.138+:PROPERTIES:
   1.139+:ID:       1a8ca441-f290-46c7-b979-1e7e0d1d063b
   1.140+:END:
   1.141 #+begin_src shell
   1.142 time target/release/dysk-test
   1.143 #+end_src