# HG changeset patch # User Richard Westhaver # Date 1717373196 14400 # Node ID 84097475a40a1ab2d523763916de5443ac983835 # Parent d77884ec2b44578aed4268cd374d559cd7643470 bump diff -r d77884ec2b44 -r 84097475a40a draft/dylib-skel.org --- a/draft/dylib-skel.org Sun Apr 28 19:49:20 2024 -0400 +++ b/draft/dylib-skel.org Sun Jun 02 20:06:36 2024 -0400 @@ -1,5 +1,8 @@ #+title: Shared Library Skeletons * Overview +:PROPERTIES: +:ID: 748ba6e4-60db-4ff7-9d78-12c3f67644d8 +:END: + CODE :: [[https://lab.rwest.io/packy/stash/dysk][packy/stash/dysk]] 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 to rapidly develop high-quality software. As such, it's crucial that these two very @@ -13,12 +16,18 @@ most popular software stack (yet ;). This is an experiment and may not make it to our code-base, but it's definitely something worth adding to the toolbox in case we need it. ** FFI +:PROPERTIES: +:ID: 0e490b3b-0c15-4963-9d43-7d2a689ec371 +:END: The level of interop we're after in this case is [[https://en.wikipedia.org/wiki/Foreign_function_interface][FFI]]. Basically, calling Rust code from Lisp and vice-versa. There's an article about calling 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 for those interested. *** Rust ABI +:PROPERTIES: +:ID: 6c24dc95-eea6-44fb-8c7f-bb49227ca7bf +:END: The complication(s) with Rust I mentioned early is really just that /it is not C/. =C= is old, i.e. well-supported with a stable ABI, making the process of creating bindings for a C library a breeze in many languages. @@ -35,17 +44,29 @@ friction-less FFI development experience in Rust. *** Overhead +:PROPERTIES: +:ID: faf8fbcb-3403-4e2f-b881-ef1404ad9780 +:END: Using FFI involves some overhead. Check [[https://github.com/dyu/ffi-overhead][here]] for an example benchmark across a few languages. While building the NAS-T core, I'm very much aware of this, and will need a few sanity benchmarks to make sure the cost doesn't outweigh the benefit. In particular, I'm concerned about crossing multiple language barriers (Rust<->C<->Lisp). * basic example +:PROPERTIES: +:ID: ab04fff7-51c9-4f1c-b61b-fa4739932343 +:END: ** Setup +:PROPERTIES: +:ID: 08420b18-3856-4b62-9523-98fb3398afca +:END: For starters, I'm going to assume we all have Rust (via [[https://rustup.rs/][rustup]]) and Lisp ([[https://www.sbcl.org/][sbcl]] only) installed on our GNU/Linux system (some tweaks needed for Darwin/Windows, not covered in this post). *** Cargo +:PROPERTIES: +:ID: cfeb1299-67c5-4a64-863e-f214e195e176 +:END: Create a new library crate. For this example we're focusing on a 'skeleton' for /dynamic/ libraries only, so our experiment will be called =dylib-skel= or *dysk* for short. @@ -68,7 +89,13 @@ This tells Rust to generate a shared C-compatible object with a =.so= extension which we can open using [[https://man.archlinux.org/man/dlopen.3.en][dlopen]]. *** cbindgen +:PROPERTIES: +:ID: 435d84d6-c959-4bf6-ad37-fb6e524b54ff +:END: **** install +:PROPERTIES: +:ID: 15115ab8-e7b4-4050-9567-bf026cc140d1 +:END: Next, we want the =cbindgen= program which we'll use to generate header files for C/C++. This step isn't necessary at all, we just want it for further experimentation. @@ -80,6 +107,9 @@ cbindgen = "0.24" #+end_src **** cbindgen.toml +:PROPERTIES: +:ID: 21d776b7-9f8d-445c-83c2-8e0c28751827 +:END: #+begin_src conf-toml :tangle cbindgen.toml language = "C" autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */" @@ -96,6 +126,9 @@ header = "dysk.h" #+end_src **** build.rs +:PROPERTIES: +:ID: 469dd142-bafa-4076-8ba9-baf088e60260 +:END: #+begin_src rust :tangle build.rs fn main() -> Result<(), cbindgen::Error> { if let Ok(b) = cbindgen::generate(std::env::var("CARGO_MANIFEST_DIR").unwrap()) { @@ -103,6 +136,9 @@ else { panic!("failed to generate dysk.h from cbindgen.toml") } } #+end_src ** lib.rs +:PROPERTIES: +:ID: e9d50e77-4d1e-4d38-90aa-9977d8c6c888 +:END: #+begin_src rust :tangle lib.rs //! lib.rs --- dysk library use std::ffi::{c_char, c_int, CString}; @@ -115,15 +151,24 @@ pub extern "C" fn plus1(n:c_int) -> c_int {n+1} #+end_src ** test.rs +:PROPERTIES: +:ID: 84b5dffe-7171-4d72-9788-f288e3185070 +:END: #+begin_src rust :tangle test.rs //! test.rs --- dysk test fn main() { let mut i = 0u32; while i < 500000000 {i+=1; dysk::plus1(2 as core::ffi::c_int);}} #+end_src ** compile +:PROPERTIES: +:ID: 30f4fb3e-0757-4df6-947f-8d1e11edabf9 +:END: #+begin_src sh cargo build --release #+end_src ** load from SBCL +:PROPERTIES: +:ID: 69b517f2-648d-4e44-b956-7879b3dadf99 +:END: #+begin_src lisp :tangle dysk.lisp ;;; dysk.lisp ;; (dysk:hello) ;; => "hello from rust" @@ -137,6 +182,9 @@ (define-alien-routine plus1 int (n int)) #+end_src ** benchmark +:PROPERTIES: +:ID: 86e4195d-601b-4736-b852-1209a6d38bdf +:END: #+begin_src shell time target/release/dysk-test #+end_src