diff options
author | Pjotr Prins | 2025-02-11 02:27:38 -0600 |
---|---|---|
committer | Pjotr Prins | 2025-02-11 02:27:38 -0600 |
commit | d2150e69343861d7a83b820fc6e74f7572857651 (patch) | |
tree | 122202d5b814c2c6507096c9e8f948ec169c90fb | |
parent | fd714598394a36d94846a5c2cfa1dc6a7e4bcced (diff) | |
download | presentations-d2150e69343861d7a83b820fc6e74f7572857651.tar.gz |
Add slides
-rw-r--r-- | prescheme-nim-local/prescheme-nim-transpilers-FOSDEM-2025-talk.org | 238 |
1 files changed, 238 insertions, 0 deletions
diff --git a/prescheme-nim-local/prescheme-nim-transpilers-FOSDEM-2025-talk.org b/prescheme-nim-local/prescheme-nim-transpilers-FOSDEM-2025-talk.org new file mode 100644 index 0000000..5b77991 --- /dev/null +++ b/prescheme-nim-local/prescheme-nim-transpilers-FOSDEM-2025-talk.org @@ -0,0 +1,238 @@ +#+TITLE: Small Headed Programming + +#+AUTHOR: Pjotr Prins + +# 15-20 min +# Images: C-c C-x C-v +# org-tree-slide-mode C-< and C-> + +* Reality + +> Small headed programming: languages that fit in the brain + +Does not have to be a *small* language per se. + +Fits are: + +- Many LISPs +- Zig +- C + +No fits are: + +- Python +- Rust +- C++ + +* What do I really want out of any language? + +- I want to express myself quickly and get results fast +- I want decent error messages +- I want to be able to drop in a REPL and debug +- I want my code to be recognizable and readable after two years +- I want the experience to be really fast at compile time +- I would like compile type checking *when* I want it +- I want few dependencies (wait?) + +* Thoughts on garbage collectors (GC) + +- Faster development, less mistakes +- GC is a great invention - what is not to like? + +- Start/stop the world issues - threading and RT - e.g. games +- Hurts when crossing language boundaries, i.e. bindings +- Some languages suffer more than others, but it is never trivial + +* Rust does not have GC + +- That means you always have to deal with memory. + +* Two Years Ago + +- Everyone needs two languages: one for quick coding and one for performance +- My 1st choice is to program in Guile and Zig (instead of Python and C) + +I went looking for convenience + a performance option + +Guile+Zig exploration: + +https://gitlab.com/pjotrp/guile-zig/-/blob/main/README.md + +* Zig + +- Zig is a minimalistic new language +- unapologetically focused on performance +- blazingly fast compiler +- it may even replace C++ in places +- Zig uses the C-ABI, has NO GC, so it is ideal for binding against other languages +- Suffers from the semi-colon +* Zig bootstrap + +- Compilers need to be bootstrapped (from C) +- At some point they become self-hosting +- Zig created a bootstrap in WASM - cool +- But a binary blob does not sit well with the likes of Debian +- Been an issue for years now and prevents deploying our toolstacks + +* Zig in Guix + +- Zig recently got bootstrapped from C source in Guix - it builds the WASM blob! +- GNU Guix now Supports the latest stable verion! +- Debian could copy that. + +When languages get popular enough, someone will create a bootstrap +path. Even when it is hard. + +* Why not Zig now? + +- We target HPC - need to create deployments +- Getting permission to run compiler tool stacks etc. can be painful +- Similarly for low-end (RISC-V) systems +- But C compilers are *always* there!! + +* Enter C-transpiling + +- C-transpilers w.o. GC, e.g. Nim, prescheme and Chicken (ref counter) +- We want no GC because of linking against guile, python, etc. +- Will compile C on any target system (ideally optimized!) +- Can distribute both original code and transpiled code (if it looks nice enough) +- Actually, nice output is important + +* Prescheme example + +Call C function to convert float to string: + +#+BEGIN_SRC C +void gcvt(float value, int ndigits, char *buf); +#+END_SRC + +#+BEGIN_SRC scheme + (define (gcvt v ndigits buf) + ((external "gcvt" (=> (float integer (^ char)) unit ) ) + v ndigits buf)) + + (let ((target (make-string 20)) + (f 1.2)) + (gcvt f 4 target)) +#+END_SRC + +Translates to + +#+BEGIN_SRC C + char *target_2X = (char *)calloc( 1, 1 + 20); + gcvt(1.2, 4, target_2X); + ps_write_string(target_2X, out_0X); +#+END_SRC + +* SIMDE + +SIMDe is a free software header-only library which provides fast, portable implementations of SIMD intrinsics for platforms which aren’t natively supported by the API in question. + +For example, with SIMDe you can use SSE, SSE2, SSE3, SSE4.1 and 4.2, AVX, AVX2, and many AVX-512 intrinsics on ARM, POWER, WebAssembly, or almost any platform with a C compiler. That includes, of course, x86 CPUs which don’t support the ISA extension in question (e.g., calling AVX-512F functions on a CPU which doesn’t natively support them). + + +* Prescheme simde (Lisp) + +#+BEGIN_SRC scheme + (define (simde-loadu ds) + ((external "simde_mm256_loadu_pd" (=> ((^ float)) m256d)) + ds )) + + (let ((a (make-vector 4 1.1)) + (b (make-vector 4 1.1)) + (c (make-vector 4 1.1))) + (vector-set! a 0 2.2) + (vector-set! a 1 2.2) + (vector-set! a 2 4.2) + (vector-set! a 3 5.2) + (vector-set! b 0 1.1) + (vector-set! b 1 2.1) + (vector-set! b 2 3.1) + (vector-set! b 3 4.1) + + (let* ((evens (simde-loadu a)) + (odds (simde-loadu b)) + (result (simde-storeu a (simde-sub-pd evens odds)))) + (vector-for-each (lambda (i val) + (gcvt val 4 target) + (write-string target out) + (newline out) + ) + result 4) +#+END_SRC + +* Prescheme simde (C) + +#+BEGIN_SRC C + #include <simde/x86/avx512.h> + + simde__m256d avx_7X; + simde__m256d odds_6X; + simde__m256d evens_5X; + // malloc and initialize a_3X and b_4X as array of floats + + evens_5X = simde_mm256_loadu_pd(a_3X); + odds_6X = simde_mm256_loadu_pd(b_4X); + avx_7X = simde_mm256_sub_pd(evens_5X, odds_6X); + simde_mm256_storeu_pd(a_3X, avx_7X); +#+END_SRC + +* Prescheme + +- Writes C beautifully from readable Lisp +- Prescheme writes C files +- Even C macros are written without loss +- Strings and vectors use malloc/free +- We can use Lisp macros to free on scope exit +- Currently needs a hack to allow for opaque pointers + +* Nim example + +#+BEGIN_SRC python + var evens = [1.0, 2.1, 3.2, 4.3] + var odds = [0.0, 1.1, 2.1, 3.3] + + var regs1 = mm256_loadu_pd(evens[0].addr) + var regs2 = mm256_loadu_pd(odds[0].addr) + var result = mm256_sub_pd(regs1,regs2) + echo cast[array[4, float64]](result) +#+END_SRC + +* Nim transpiled to C + +#+BEGIN_SRC C + regs1__simd_124 = _mm256_loadu_pd(((void*) ((&l1__simd_122[(((NI) 0))- 0])))); + regs2__simd_125 = _mm256_loadu_pd(((void*) ((&l2__simd_123[(((NI) 0))- 0])))); + result__simd_126 = _mm256_sub_pd(regs1__simd_124, regs2__simd_125); + nimZeroMem((void*)T3_, sizeof(tyArray__nHXaesL0DJZHyVS07ARPRA)); + LOC4.source = result__simd_126; + T3_[0] = dollar___simd_192(LOC4.dest); + echoBinSafe(T3_, 1); +#+END_SRC + +* Nim + +- Writes C from readable Nim +- C macros are written without loss +- Strings and vectors use reference counting + +- C files are written to a user cache in $HOME +- Generated C code has boiler plate, is verbose, and is not really meant for humans + + +* Chicken Scheme transpiler to C + +- GC free C transpiler was inspired by recent re-aliving prescheme! +* TODO Zig transpiler + +- Zig can actually generate C +- `zig build-obj -ofmt=c -lc main.zig` +- So with bootstrap this is worth looking into + +* Conclusion + +- prescheme is light, hackable and just generates light-weight C output +- Amazing mapping to low level +- Nim is cool too - but more involved and transpiled C is less easy to digest by humans +- Nim and Chicken have reference counting - prescheme is mapped - and macros can simplify management +- Think you Andrew Whatson and NLNet! |