changeset 19: |
84097475a40a |
parent 18: |
d77884ec2b44 |
child 20: |
889759cafcc2 |
author: |
Richard Westhaver <ellis@rwest.io> |
date: |
Sun, 02 Jun 2024 20:06:36 -0400 |
files: |
draft/dylib-skel.org |
description: |
bump |
1.1--- a/draft/dylib-skel.org Sun Apr 28 19:49:20 2024 -0400
1.2+++ b/draft/dylib-skel.org Sun Jun 02 20:06:36 2024 -0400
1.3@@ -1,5 +1,8 @@
1.4 #+title: Shared Library Skeletons
1.5 * Overview
1.6+:PROPERTIES:
1.7+:ID: 748ba6e4-60db-4ff7-9d78-12c3f67644d8
1.8+:END:
1.9 + CODE :: [[https://lab.rwest.io/packy/stash/dysk][packy/stash/dysk]]
1.10 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.11 to rapidly develop high-quality software. As such, it's crucial that these two very
1.12@@ -13,12 +16,18 @@
1.13 most popular software stack (yet ;). This is an experiment and may not make it to our
1.14 code-base, but it's definitely something worth adding to the toolbox in case we need it.
1.15 ** FFI
1.16+:PROPERTIES:
1.17+:ID: 0e490b3b-0c15-4963-9d43-7d2a689ec371
1.18+:END:
1.19 The level of interop we're after in this case is [[https://en.wikipedia.org/wiki/Foreign_function_interface][FFI]].
1.20
1.21 Basically, calling Rust code from Lisp and vice-versa. There's an article about calling
1.22 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.23 for those interested.
1.24 *** Rust ABI
1.25+:PROPERTIES:
1.26+:ID: 6c24dc95-eea6-44fb-8c7f-bb49227ca7bf
1.27+:END:
1.28 The complication(s) with Rust I mentioned early is really just that /it is not C/. =C=
1.29 is old, i.e. well-supported with a stable ABI, making the process of creating bindings
1.30 for a C library a breeze in many languages.
1.31@@ -35,17 +44,29 @@
1.32 friction-less FFI development experience in Rust.
1.33
1.34 *** Overhead
1.35+:PROPERTIES:
1.36+:ID: faf8fbcb-3403-4e2f-b881-ef1404ad9780
1.37+:END:
1.38 Using FFI involves some overhead. Check [[https://github.com/dyu/ffi-overhead][here]] for an example benchmark across a few
1.39 languages. While building the NAS-T core, I'm very much aware of this, and will need a
1.40 few sanity benchmarks to make sure the cost doesn't outweigh the benefit. In particular,
1.41 I'm concerned about crossing multiple language barriers (Rust<->C<->Lisp).
1.42
1.43 * basic example
1.44+:PROPERTIES:
1.45+:ID: ab04fff7-51c9-4f1c-b61b-fa4739932343
1.46+:END:
1.47 ** Setup
1.48+:PROPERTIES:
1.49+:ID: 08420b18-3856-4b62-9523-98fb3398afca
1.50+:END:
1.51 For starters, I'm going to assume we all have Rust (via [[https://rustup.rs/][rustup]]) and Lisp ([[https://www.sbcl.org/][sbcl]] only)
1.52 installed on our GNU/Linux system (some tweaks needed for Darwin/Windows, not covered in
1.53 this post).
1.54 *** Cargo
1.55+:PROPERTIES:
1.56+:ID: cfeb1299-67c5-4a64-863e-f214e195e176
1.57+:END:
1.58 Create a new library crate. For this example we're focusing on a 'skeleton' for
1.59 /dynamic/ libraries only, so our experiment will be called =dylib-skel= or *dysk* for
1.60 short.
1.61@@ -68,7 +89,13 @@
1.62 This tells Rust to generate a shared C-compatible object with a =.so= extension which we
1.63 can open using [[https://man.archlinux.org/man/dlopen.3.en][dlopen]].
1.64 *** cbindgen
1.65+:PROPERTIES:
1.66+:ID: 435d84d6-c959-4bf6-ad37-fb6e524b54ff
1.67+:END:
1.68 **** install
1.69+:PROPERTIES:
1.70+:ID: 15115ab8-e7b4-4050-9567-bf026cc140d1
1.71+:END:
1.72 Next, we want the =cbindgen= program which we'll use to generate header files for
1.73 C/C++. This step isn't necessary at all, we just want it for further experimentation.
1.74
1.75@@ -80,6 +107,9 @@
1.76 cbindgen = "0.24"
1.77 #+end_src
1.78 **** cbindgen.toml
1.79+:PROPERTIES:
1.80+:ID: 21d776b7-9f8d-445c-83c2-8e0c28751827
1.81+:END:
1.82 #+begin_src conf-toml :tangle cbindgen.toml
1.83 language = "C"
1.84 autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */"
1.85@@ -96,6 +126,9 @@
1.86 header = "dysk.h"
1.87 #+end_src
1.88 **** build.rs
1.89+:PROPERTIES:
1.90+:ID: 469dd142-bafa-4076-8ba9-baf088e60260
1.91+:END:
1.92 #+begin_src rust :tangle build.rs
1.93 fn main() -> Result<(), cbindgen::Error> {
1.94 if let Ok(b) = cbindgen::generate(std::env::var("CARGO_MANIFEST_DIR").unwrap()) {
1.95@@ -103,6 +136,9 @@
1.96 else { panic!("failed to generate dysk.h from cbindgen.toml") } }
1.97 #+end_src
1.98 ** lib.rs
1.99+:PROPERTIES:
1.100+:ID: e9d50e77-4d1e-4d38-90aa-9977d8c6c888
1.101+:END:
1.102 #+begin_src rust :tangle lib.rs
1.103 //! lib.rs --- dysk library
1.104 use std::ffi::{c_char, c_int, CString};
1.105@@ -115,15 +151,24 @@
1.106 pub extern "C" fn plus1(n:c_int) -> c_int {n+1}
1.107 #+end_src
1.108 ** test.rs
1.109+:PROPERTIES:
1.110+:ID: 84b5dffe-7171-4d72-9788-f288e3185070
1.111+:END:
1.112 #+begin_src rust :tangle test.rs
1.113 //! test.rs --- dysk test
1.114 fn main() { let mut i = 0u32; while i < 500000000 {i+=1; dysk::plus1(2 as core::ffi::c_int);}}
1.115 #+end_src
1.116 ** compile
1.117+:PROPERTIES:
1.118+:ID: 30f4fb3e-0757-4df6-947f-8d1e11edabf9
1.119+:END:
1.120 #+begin_src sh
1.121 cargo build --release
1.122 #+end_src
1.123 ** load from SBCL
1.124+:PROPERTIES:
1.125+:ID: 69b517f2-648d-4e44-b956-7879b3dadf99
1.126+:END:
1.127 #+begin_src lisp :tangle dysk.lisp
1.128 ;;; dysk.lisp
1.129 ;; (dysk:hello) ;; => "hello from rust"
1.130@@ -137,6 +182,9 @@
1.131 (define-alien-routine plus1 int (n int))
1.132 #+end_src
1.133 ** benchmark
1.134+:PROPERTIES:
1.135+:ID: 86e4195d-601b-4736-b852-1209a6d38bdf
1.136+:END:
1.137 #+begin_src shell
1.138 time target/release/dysk-test
1.139 #+end_src