veer66

veer66

ความเห็นเกี่ยวกับ Haskell

ผมเพิ่งเคยลองเขียน Haskell นิด ๆ หน่อย ๆ เรียกว่ายังรู้เรื่องก็ได้แต่อยากบันทึกความคิดของตัวเองไว้ จะผิดถูกอย่างไรก็เรียนเชิญอภิปรายได้เลยครับ

ผมมองว่าคุณของ Haskell มันทำให้ programmer เห็นได้ชัดว่า function ไหนไปยุ่งกับ IO หรือ database และ function ไหนไม่ยุ่ง

ถ้าเขียน Python ดูเผิน ๆ ไม่ไล่ดูเนื้อ function ทั้งสองแบบมันก็หน้าตาเหมือนกัน

พอเห็นได้ว่ามันต่างกัน ก็อาจจะคิดขึ้นมาได้ว่าควรเขียน function ที่อิสระจาก IO จาก database ให้มาก เพราะพวกนี้เขียนง่าย test ง่ายทั้ง auto ทั้ง manual ทำ hot reload ก็ง่าย

แต่คนที่สำเหนียกไม่ได้ก็มี เช่น (ผมเอง) ทำไปทำมามี monad กระจายไปทุก function

แอปหรือบริการออนไลน์มักจะมี database หรือไฟล์ก็ตามไว้เก็บค่าหรือสถานะต่าง ๆ เช่น จำนวนวัคซีนที่ยังไม่ได้มีคนจองของแต่ละโรงพยาบาล แล้วก็เป็นเรื่องธรรมดาอีกเหมือนกันที่ค่าที่ว่าจะถูกแก้ เช่น พอมีคนจองวัคซีนไปแล้วก็ต้องไปแก้จำนวนวัคซีนทีเหลือให้มันลดลง

เวลาอ่านเจอว่าหลีกเลี่ยงโปรแกรมที่รันแล้วมี side effect มันชวนให้เขาใจผิดได้ ผมอยากจะเรียกว่าจัดโปรแกรมให้มีระเบียบก็พอ

จากความเข้าใจบ้าน ๆ ของผมในยุค 80s 90s คนก็ใช้ dBASE เป็นการบังคับจัดให้เป็นระเบียบระดับนึง ข้อมูลทั้งหมดเป็น table ที่เอาใส่ไฟล์ .dbf ไว้ โปรแกรมที่จะเขาไปอ่านไปแก้ข้อมูลอยู่ใน .prg แยกกันอย่างชัดเจน

ปลาย ๆ ยุค 90s คนเริ่มนิยมใช้ database ที่รันต่างหากเป็นอีกโปรแกรมเลย แล้วแอปก็รันแยกต่างหากแล้วสื่อสารส่งข้อมูลกันข้าม process หรือกระทั่งผ่านระบบเครือข่าย พวกนี้ใช้ Visual Basic หรือ Delphi หรือ Tcl/Tk อะไรก็ว่ากันไป ไป แล้วไปต่อกับ Oracle หรือ SQL Server อีกที จะว่าคู่ขนาดกับเครื่องมือพวกนี้ทางค่าย NeXT ซึ่งถูก Apple ซื้อไปทีหลังเขาก็เอาความคิดแบบ MVC มาใช้แล้ว ตั้งแต่ยุค 80s แต่ถ้าสืบสาวไปไปจริง ๆ จะไปถึง Smalltalk-79 จาก Xerox ในยุค 70s แต่ว่าแถวบ้านผมยังไม่ไม่ใช่เทคนิคที่เป็นที่นิยม NeXT computer ผมได้แค่ไปลูบ ๆ เครื่องตัวโชว์

ยุคต่อมาจาก Visual Basic ก็ทำเป็นเว็บจะเอา Perl เขียน หรือใช้ PHP หรือใช้ ASP เขียน โครงสร้างยังเหมือนเดิมคือแบ่ง database ไปไว้เป็นบริการนึง แอปอีกบริการนึง แล้ว code ในแอปก็จะเห็นได้เลยว่าส่วนไหนเอาไว้แก้ข้อมูล เพราะในยุคนี้จะเห็นเลยใช้ภาษา SQL อยากดูว่า code ส่วนไหนแก้ข้อมูลก็หาคำว่า INSERT หรือ UPDATE ก็เจอแล้ว มันก็เป็นระเบียบขึ้น ยุคนี้ข้อดีคือ data อยู่เป็นที่ ส่วนที่กระจัดกระจายออกไปก็มันจะเป็น data อายุสั้นที่อายุมักจะไม่เกิน 10 วินาที

mrbs สำหรับผมเป็นตัวแทนของยุค 2000s เลยนะสำหรับผม เป็นโปรแกรมจองห้องโรงแรมมันก็คล้าย ๆ จองวัคซีนนะ อาจจะซับซ้อนกว่าด้วยซ้ำ เขียนด้วย PHP

โปรแกรมจัดระเบียบส่วนที่แก้ค่าต่าง ๆ อย่างชัดเจนคือ พวกที่แก้ database อยู่ในไฟล์ที่ลงท้ายด้วย _handler.php ข้างในก็มี code สร้าง SQL มีคำสั่ง update ไม่ได้แสดงผลอะไรออกมา ส่วนไฟล์ที่ชื่อคล้าย ๆ กันแต่ไม่มี _handler ลงท้าย พวกนี้ก็เห็นคำสั่ง SELECT ข้างในแล้วก็ไปเรียก code ที่สร้าง HTML อีกที

ยุค 2000s ก็ไม่ใช่ว่าทุกคนทำแอปออกมาแล้วจะเป็นระเบียบแบบ mrbs ต้องอาศัยการวางโครงสร้างที่ดี กระทั่ง mrbs ยังจัดระเบียบให้มีสัดส่วนให้มากกว่านั้นได้อีก

ทำให้มี framework ที่ดังขึ้นมาในยุค 2010s คือ Ruby on Rails เอาแนวคิด MVC มาใช้ซึ่งเจ้าอื่นเขาก็ใช้มาตั้งแต่เริ่มยุค 80s หรือก่อนนั้นแล้ว

Ruby on Rails ตัวอย่างจาก code ของ Mastodon ส่วนที่แก้ค่าต่าง ๆ ก็อยู่ใน model แยกเป็นเรื่องต่าง ๆ มี user post แต่เรียก status เป็นต้น

นอกจากเรื่องประสิทธิภาพเวลารันแล้ว พวกแอปหลัง 2010 มีประเด็นที่ต้องจัดการคือ แอปบน smart phone; แอป web browser แทน server; โปรแกรมซับซ้อนแยกออกมา test เฉพาะส่วนยาก ไม่ว่าจะ automated test หรือ manual test

สามเรื่องนี้ผมมองว่าเกี่ยวกัน ยุคต้น ๆ 2000s ใช้ http เป็นหลัก ระเบียบในการเปลี่ยนค่าคือ ทั้งหมดอยู่ใน database กลาง กับ session db ซึ่งอาจจะเป็น file หรือ database อีกตัวที่เน้นใช้ RAM ส่วนตัวแปรยิบย่อยที่ใช้งานระหว่าง HTTP ทำงานมันก็จะถูกไปในการเรียกครั้งเดียว

แต่พอมาทำแอปบน smartphone และบน web browser ตัวแปรบางตัวอาจจะอยู่ไปเลยทั้งวันก็ได้ หรือหลายวันก็ได้ แต่ว่าตัวอย่างนี้คงใช้กับพวกเว็บลงทะเบียนไม่ได้แล้ว เพราะเว็บลงทะเบียนควรจะลงเสร็จใน 10 นาที ส่วนของเว็บลงทะเบียนที่ต้องมีโปรแกรมบน web browser บ้างเช่น ถ้าเลือกสัญชาติเป็นกัมพูชาหน้าจอก็จะให้กรอกเลข passport แทนเลขบัตรประชาชน validator ก็ต้องเปลี่ยนให้ใส่ตัวอักษรโรมันได้ แทนที่จะเป็นตัวเลขอย่างเดียวแบบเลขประชาชน แต่ถ้าแอปเป็นอย่างอื่น เช่น ตลาดหุ้น คลาดสินทรัพย์ดิจิทัลต่าง ๆ มีกราฟมีราคาพวกนี้บางคนก็เปิดทิ้งไว้เป็นวันเป็นสัปดาห์เลยก็ได้ และสถานะมันก็เปลี่ยนไปตลอด จะมาหวังทำแบบสมัยที่เว็บโหลดมาทีละหน้าแล้วล้างสถานะไปหมดไม่ได้แล้ว

ทางด้าน backend ก็มี business rule หลาย ๆ อย่างที่แยกออกมา test ได้โดยที่ไม่ต้องไปรันกับ database หรือเว็บ แต่ถ้าไม่แยกออกมาแต่แรกต้องเรียกจาก controller หรือใน http handler แบบแยกไม่ออกก็กลายเป็นยากไป

พอมีความจำเป็นต้องแยกส่วนที่ว่าไม่ต้องไปรันกับ database ก็ได้ อันนี้ functional programming ก็กลายเป็นสำคัญขึ้นมา จริง ๆ แล้วก็เป็นเรื่องเก่า บางคนบอกว่าเก่าจนถึง 1920s ด้วยซ้ำ

ความคิดส่วนหนึ่งของ function programming คือการแยก pure function ออกมา พอเป็น pure function นี่เรา test ได้เลยทั้ง auto ทั้ง manual ไม่ต้องเอา database ใส่

ทั้ง Lisp ทั้ง Clojure ส่วนเด่น ๆ คือมันแถม immutable data structure มาให้ด้วย อันนี้ก็ทำให้เขียน pure function ง่ายขึ้น ไม่ต้องแก้ data structure ก้อนเดิม สร้างก็ใหม่ได้ได้เรื่อย ๆ โดยที่ไม่ได้ช้าหรือกินที่มาก

แต่ถึงอย่างนั้น data structure ที่ว่าก็เอาใส่ภาษาอื่นก็ได้ เอา JavaScript ก็ได้ เอา Ruby ก็ได้

OCaml หรือภาษาตระกูล Meta-Language ทั้งหมด เท่าที่ผมเห็นคือทำได้หลาย ๆ อย่างแบบ Lisp มี data structure แบบเดียวกันเลย แต่ว่าใช้แบบ static-typing ได้ วงเล็บน้อยลง ผมเดาว่าคนคงจะบ่นเรื่องนี้เยอะ

ส่วน Haskell ส่วนที่พิเศษคือ ปกติ Lisp กับ OCaml นี่เวลาเขียน pure function นี่ programmer ก็ต้องดูเอาเองว่ามัน pure หรือเปล่า แต่ Haskell นี่บังคับ pure แต่ในความ pure ผมดูคร่าว ๆ มันก็จะมีสองแบบคือ function ที่รับค่าเข้าไปธรรมดา กับ function ที่มี monad อย่างหลังนี่ถ้าไปยุ่งกับ database หรือ IO หรือ State ก็จะกลายเป็นว่ามี monad ขึ้นมา

การแยก pure function ออกมาผมมองว่าพอเอาไปใช้กับ MVC แบบใน Ruby on Rails ผมเห็นว่ามันทำให้จัดระเบียบข้อมูลกับฟังก์ชันได้ดีขึ้นไปอีกขั้น ซึ่งตัวที่ผมเห็นว่าขนาดแนวคิดเรื่อง MVC และ pure function ให้เข้าได้ได้ดีเลยคือ re-frame บน Clojure ขึ้นมา ก็เป็นการบังคับหรือกึ่ง ๆ บังคับให้ใช้ pure function มากขึ้น และแยก data ออกมารวมศูนย์กันเป็นสัดส่วน ข้อดีนอกจากจะ test ง่ายแล้ว ก็ทำ hot reload ง่ายขึ้นด้วย นอกจาก re-frame แล้ว Elm ก็น่าจะคล้าย ๆ กันแต่ผมยังไม่เคยใช้

สรุปว่าที่เล่ามาทั้งหมดการเขียนโปรแกรมตั้งแต่ 30-40 ปีทีแล้วจนถึงเดี๋ยวนี้ สิ่งที่ผมเห็นหลัก ๆ เลยก็คือการจัดระเบียบให้ data กับ function มันแยกกันเป็นสัดส่วน

ป.ล. ตรงไหนงงหรือว่าผมเขียนผิด ถ้าหากแจ้งมาจะขอบคุณมากครับ

unix epoch in milliseconds is equal to milliseconds from 00:00:00.000 on 1st of January 1970 to a point of time. Obtaining unix epoch, for now, is obvious for GNU Coreutils' date. For example:

$ date +%s
1629132447

Obtaining unix epoch in milliseconds is a bit more complicated. I did this.

$ date +%s%N | awk '{ print substr($0,0,13) }'
1629132562414

I had to use awk to chop the string because %N is for nanoseconds, which is too fine-grained in this case.

In Common Lisp, it is even more complex, since Common Lisp uses Universal Time, which is not unix compatible.

First, we can obtain Universal Time in second for 00:00:00.000 on 1st of January 1970 by this command.

(encode-universal-time 0 0 0 1 1 1970 0)

In the rest of the code, I will use a Common Lisp package – local-time. Given I have a timestamp tm from local-time, which I may obtain it like this.

(setq tm (local-time:now))

To get unix epoch, my code converts local-time's timestamp to Universal Time and subtracts Universal Time of 00:00:00.000 on 1st of January 1970 from it.

(- (local-time:timestamp-to-universal tm)
   (encode-universal-time 0 0 0 1 1 1970 0))

Instead of using this long code, local-time has timestamp-to-unix, so we can use it instead.

(local-time:timestamp-to-unix tm)

Since we want timestamp in milliseconds, my program multiplies timestamp above, which is in second with 1000.

(* 1000 (local-time:timestamp-to-unix tm))

local-time can provide a timestamp in microseconds, and we need to add it to the timestamp above.

(+ (* 1000 (local-time:timestamp-to-unix tm))
   (local-time:timestamp-millisecond tm))

And that is it.

คนเขียน blog ทำวิดีโอ ทำ podcast บางทีก็ควรจะได้ค่าตอบแทน

รูปแบบเดิม ๆ บนเว็บเลยก็คือเราก็ดูโฆษณาไป แล้วคนลงโฆษณาก็เป็นคนจ่ายตัง แบบนี้ก็ดีเหมาะกับคนจน หรือคนมีตังแต่ไม่อยากจ่าย แต่ก็เสียเวลา น่ารำคาญได้ หรือที่แต่ที่สุดคือถูกสอดแนมอย่างหนัก

อีกวิธีหนึ่งคือจ่ายตังรายเดือน อันนี้ก็เสียตังนิด ๆ หน่อยไม่แพงมาก ตัดรำคาญ ไม่โดนบริษัทโฆษณาสอดแนม แต่ข้อเสียคือบริการพวกนี้มันมีหลายเจ้า เว็บหนึ่งก็จ่ายรายเดือนทีหนึ่ง สมมุติว่าจ่ายเว็บละ 100 บาทต่อเดือน ดูสัก 20 เว็บบางทีก็อาจจะจ่ายพุ่งไป 2000 บาท บางเว็บก็อาจจะไม่ได้ดูทุกเดือนด้วยซ้ำ แบบนี้ก็ไม่น่าจะดีเท่าไหร่

ครั้นจะให้เนื้อหาทุกสิ่งอย่างรวมมันเป็น platform เดียวทั้งหมดก็กลายเป็นผูกขาด ผู้บริโภค ผู้ผลิตไร้อำนาจต่อรอง

web monetization มาแก้ปัญหานี้เลย แทนที่จะต้องอยู่บน platform เดียวกัน แต่ละเว็บก็แค่ใส่ไปว่าจะให้จ่ายตังไปที่ไหนผ่าน tag นี้

<meta name="monetization" content="$interledger pointer" />

โดยที่ interledger pointer นี่ก็จะได้มาจากการที่เราไปสมัคร wallet ไว้

ส่วนคนที่ดูเว็บตอนนี้ผมเห็นสองทางเลือกคือ ใช้บริการ coil ซึ่งมี extension ให้ทั้ง Chrome และ Firefox เวลาเราเปิดเว็บมันก็จะค่อย ๆ โอน token ไปให้ หรืออีกทางหนึ่งคือใช้ Brave

ทีแรกผมว่านี้มันซับซ้อนหน่อย ๆ เหมือนกัน แต่ในยุคที่วัยรุ่นเล่น defi ผมว่าแค่นี้ง่ายมาก

แต่ทั้งหมดผมก็เพิ่งมาศึกษา ไม่ทราบว่าจ่ายแต่จะได้ตังจริง ๆ หรือเปล่า หรือจะมีปัญหาติดขัดอื่น ๆ หรือไม่

  • I use SBCL in my project but SBCL didn't work well on Ubuntu 20.04 in Docker 19.3 on CentOS 7, so I cannot use Docker.
  • I found that GCC 4.8.5 was too old for building VISL CG3, so I installed GCC 8.5.0.
  • Still it didn't work because VISL CG3 was linked with old libstdc++. So I avoid it by installing libicu using GCC 8.5.0. Then it works.
#!/bin/bash

set -x
mkdir -p apertium-src && \
mkdir -p $MTDIR 

cd apertium-src && \
    wget http://ftp.tsukuba.wide.ad.jp/software/gcc/releases/gcc-8.5.0/gcc-8.5.0.tar.gz -O - \
	| gzip -dc \
	| tar -xf - && \
    cd gcc-8.5.0 && \
    ./configure --prefix=$MTDIR --disable-multilib && \
    make -j $(nproc) && \
    make install && \
    cd .. || exit 1

cd apertium-src && \
    wget https://github.com/unicode-org/icu/releases/download/release-69-1/icu4c-69_1-src.tgz -O - \
    | gzip -dc \
    | tar -xf - \
    && cd icu/source \
    && CC=gcc CXX=g++ ./configure --prefix=$MTDIR \
    && CC=gcc CXX=g++ make -j $(nproc) \
    && CC=gcc CXX=g++ make install \
    && cd ../.. \
	|| exit 1

cd apertium-src && \
    svn checkout http://beta.visl.sdu.dk/svn/visl/tools/vislcg3/trunk vislcg3 && \
    cd vislcg3 && ./get-boost.sh \
    &&  ./cmake.sh -DCMAKE_INSTALL_PREFIX=$MTDIR \
		   -DICU_INCLUDE_DIR=$MTDIR/include \
		   -DICU_LIBRARY=$MTDIR/lib/libicuuc.so \
		   -DICU_IO_LIBRARY=$MTDIR/lib/libicuio.so \
		   -DICU_I18N_LIBRARY=$MTDIR/lib/libicui18n.so \
    && make -j$(nproc) && \
    make install && cd .. || exit 1

cd apertium-src && \
    git clone https://github.com/apertium/lttoolbox && \
    cd lttoolbox && ./autogen.sh --prefix=$MTDIR && make -j $(nproc) && make install && cd ../.. || exit 1

cd apertium-src && \
    git clone https://github.com/apertium/apertium && \
    cd apertium && ./autogen.sh --prefix=$MTDIR && make -j $(nproc) && make install && cd ../.. || exit 1

cd apertium-src && \
    git clone https://github.com/apertium/apertium-lex-tools && \
    cd apertium-lex-tools && ./autogen.sh --prefix=$MTDIR && make -j $(nproc) && make install && cd ../.. || exit 1

cd apertium-src && \
    git clone https://github.com/apertium/apertium-tha && \
    cd apertium-tha && ./autogen.sh --prefix=$MTDIR && make && make install && cd ../.. || exit 1

cd apertium-src && \
    git clone https://github.com/apertium/apertium-tha-eng && \
    cd apertium-tha-eng && ./autogen.sh --prefix=$MTDIR && make && make install && cd .. && \
    cd .. || exit 1

อันนี้ลองเขียน Clojure ดู ข้อดีคือ Clojure ก็ไม่ค่อยใช้สัญลักษณ์มากมายอยู่แล้วยกเว้นวงเล็บ แม้แต่ def ก็เขียน macro มาครอบได้

เอาภาษาไทยมาตั้งชื่อได้ทุกสิ่งทุกอย่างเลย

(defmacro กำหนดให้ [a b]
  `(def ~a ~b))

(กำหนดให้ แสดงผล println)
(กำหนดให้ เอามาคูณกัน *)

(กำหนดให้ จำนวนบ้าน 20)
(กำหนดให้ จำนวนหมู่บ้าน 10)

(แสดงผล (เอามาคูณกัน จำนวนบ้าน จำนวนหมู่บ้าน))

ปกติแล้วก็คงไม่มีใครทำแบบนี้ แต่ก็ไม่แน่บางทีอาจจะมีโอกาสได้ใช้ก็ได้

(cons “dog” “cat”)

+------------------+-----------------+
|                  |                 |
|                  |                 |
|                  |                 |
|        X         |        X        |
|        |         |        |        |
|        |         |        |        |
|        |         |        |        |
+--------|---------+--------|--------+
         |                  |
         |                  |
         V                  V
       "dog"              "cat"

(car (cons “dog” “cat”))

"dog"

(cdr (cons “dog” “cat”))

"cat"

Prerequisite

I use Ubuntu 20.04 and I have already install node.js.

Install neovim

apt-get install neovim

Install vimplug

sh -c 'curl -fLo "${XDG_DATA_HOME:-$HOME/.local/share}"/nvim/site/autoload/plug.vim --create-dirs \
       https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim'

(source: https://github.com/junegunn/vim-plug)

Setup vimplug

nvim $HOME/.config/nvim/init.vim

In $HOME/.config/nvim/init.vim

call plug#begin(stdpath("data") . '/plugged')

call plug#end()

Install coc.vim

In $HOME/.config/nvim/init.vim

call plug#begin(stdpath("data") . '/plugged')
Plug 'neoclide/coc.nvim', {'branch': 'release'}
call plug#end()

In nvim,

:PlugInstall

Install rust-analyzer

mkdir .local/bin
curl -L https://github.com/rust-analyzer/rust-analyzer/releases/latest/download/rust-analyzer-linux -o ~/.local/bin/rust-analyzer
chmod +x ~/.local/bin/rust-analyzer

(source: https://rust-analyzer.github.io/manual.html#rust-analyzer-language-server-binary)

Install coc-rust-analyzer

In nvim,

:CocInstall coc-rust-analyzer

Alt Text

Setup coc

In $HOME/.config/nvim/coc-settings.json

{"rust-analyzer.server.path": "/home/YOURNAME/.local/bin/rust-analyzer"}                                                                                                         

Please replace YOURNAME with your name.

Open a source code in Rust

It should work.

Alt Text

I didn't do anything fancy. I create C compatible wrapper in Rust, and call the wrapper from Common Lisp using CFFI.

My C compatible wrapper functions

#[no_mangle]
pub extern "C" fn zstd_line_read_new<'a>(zstd_file_path: *const c_char) -> *mut c_void {
    let r_zstd_file_path = unsafe { CStr::from_ptr(zstd_file_path) };
    let file = File::open(r_zstd_file_path.to_str().unwrap());
    if file.is_err() {
        eprintln!("Cannot open file {}", r_zstd_file_path.to_str().unwrap());
        return ptr::null::<c_void>() as *mut c_void;
    }
    let file = file.unwrap();
    let wrapper = DecoderWrapper::new(file);
    Box::into_raw(Box::new(wrapper)) as *mut c_void
}

#[no_mangle]
pub extern "C" fn zstd_line_read<'a>(reader: *mut c_void) -> *const c_char {
    let wrapper: *mut DecoderWrapper<'a> = reader as *mut DecoderWrapper<'a>;
    let mut line = Vec::with_capacity(BUF_SIZE);
    unsafe {
        match (*wrapper).read_line(&mut line) {
            Ok(len) => {
                if len == 0 {
                    return ptr::null();
                } else {
                    return CString::from_vec_unchecked(line).into_raw();
                }
            }
            Err(e) => {
                panic!(e)
            }
        }
    }
}

I added this to Carto.toml

[lib]
crate-type = ["cdylib"]

It makes Cargo create DLL on Windows or .so on Linux instead of Rust specific library format.

In my Common Lisp code:

(ql:quickload :cffi)
(defpackage :t1
  (:use :cl :cffi))
(in-package :t1)

(define-foreign-library zstd_read_line
  (:win32 (:default "./target/release/zstd_read_line"))
  (t (:default "./target/release/libzstd_read_line")))

(use-foreign-library zstd_read_line)

(defcfun ("zstd_line_read_new" create-zstd-reader) :pointer (zstd_archive_path :string))
(defcfun ("zstd_line_read" zstd-read-line) :string (reader :pointer))

(let ((reader (create-zstd-reader "test1.txt.zst")))
  (loop for line = (zstd-read-line reader)
    while line
    do (print line)))

It required Quicklisp for install CFFI. This process doesn't require cbindgen because I define C interface in Common Lisp manually, as you may see at defcfun.

And then it works even on Windows 10.

Alt Text

I try different to serialize my data before saving into RocksDB. #rustlang

serde_json

Time

real    4m11.713s
user    13m32.809s
sys     1m33.887s

Space

2.1GB

bincode

Time

real    2m13.772s
user    10m45.541s
sys     1m41.670s

Space

1.1GB

serde-lexpr (S-Expression)

Time

real    10m54.622s
user    22m11.570s
sys     1m10.663s

Space

2.2GB

serde_cbor

Time

real    2m52.010s
user    12m17.019s
sys     1m32.687s

Space

1.7GB

CBOR and bincode are obviously faster than JSON and S-Expression. CBOR is a less efficient than bincode. However, more programming languages support CBOR.