graalvm-mobile

dotemacs 2021-07-16T08:27:25.276700Z

@dotemacs has joined the channel

dotemacs 2021-07-16T08:43:25.283500Z

Just discovered this channel via borkdude’s tweet of the repo that is in the subject of the channel. Very cool! So since Asami https://github.com/threatgrid/asami has local storage capability, and can be graalvm compiled, any idea what would be needed for the following: - Asami graalvm compiled, but accessible from react-native ClojureScript app? I’m guessing that there would need to be some sort of binding so that Asami compiled could be interacted with from ClojureScript. Maybe somebody with a bit more graalvm experience can chime in on this? > Why would it be cool to have this? Just because we could have datalog database on mobile (with localstorage!). Now that I wrote the above, I guess the more succinct question is: what is needed to make graalvm compiled Clojure code accessible to react-native ClojureScript code? Thanks

raspasov 2021-07-16T09:14:02.286600Z

I am currently using DataScript + React Native; All running in a JS thread, no “native”.

raspasov 2021-07-16T09:14:39.286800Z

DataScript is alright, but there’s some gotchas around performance.

borkdude 2021-07-16T08:45:29.284500Z

@dotemacs Another graalvm compatible datalog db is datalevin btw. So moar options :) But isn't Asami Clojure_Script_ compatible as well, could that be used from React Native + CLJS directly?

raspasov 2021-07-16T09:11:52.286Z

@dotemacs that’s an interesting idea

👍 1
dotemacs 2021-07-16T09:11:53.286100Z

It could and it does work, but the local storage is not yet implemented on ClojureScript side. Only on Clojure side. This is a reply to @borkdude’s question above.

raspasov 2021-07-16T09:16:29.287400Z

@borkdude what about DataScript? Has that been tried with GraalVM?

borkdude 2021-07-16T09:16:51.287800Z

datalevin is based on datascript

borkdude 2021-07-16T09:17:05.288300Z

datascript doesn't have durable storage, but datalevin added this

raspasov 2021-07-16T09:17:31.289100Z

Right, I am using DataScript, hence my biased question… I rolled my naive DataScript durable storage.

borkdude 2021-07-16T09:17:45.289300Z

it uses lmdb, you might have to have that lib available on your phone, not sure

Huahai 2021-07-16T09:17:49.289500Z

@huahaiy has joined the channel

borkdude 2021-07-16T09:17:58.289900Z

@huahaiy might be able to give more insights here.

raspasov 2021-07-16T09:19:14.291100Z

Since we brought up this interesting question and because of some perf issues in DataScript, I am theoretically curious if running it under GraalVM can yield any perf. improvements.

raspasov 2021-07-16T09:20:21.291900Z

(I realize my question probably has no clear answer, it all needs to be benchmarked 🙂 )

➕ 2
dotemacs 2021-07-16T09:22:59.293800Z

From what I understand, Datalevin doesn’t retain the history of transactions. So it’s slightly different to DataScript & Asami in that sense.

raspasov 2021-07-16T09:23:36.294900Z

@dotemacs DataScript also does not retain history by design (you have to roll your own implementation)

dotemacs 2021-07-16T09:24:01.295300Z

@raspasov my bad, TIL

raspasov 2021-07-16T09:24:43.296700Z

No worries at all; I am no expert either; been using it for a few months;

dotemacs 2021-07-16T09:24:44.296800Z

When you say > I rolled my naive DataScript durable storage Do you mean that you read the data into DataScript from a file and the write the data to a file upon the app going into background?

raspasov 2021-07-16T09:25:09.297Z

On every transaction save to file;

raspasov 2021-07-16T09:25:15.297200Z

On app startup, read from file.

dotemacs 2021-07-16T09:25:22.297400Z

Aha, nice

dotemacs 2021-07-16T09:25:37.297600Z

And the overhead is what, in milliseconds?

raspasov 2021-07-16T09:25:41.297800Z

Currently, it’s very naive; every time it simply dumps all datoms to a file.

raspasov 2021-07-16T09:26:04.298Z

So it would linearly slow down; but I’ve had 1000s of datoms and it hasn’t been an issue.

dotemacs 2021-07-16T09:26:15.298200Z

Nice

dotemacs 2021-07-16T09:26:19.298400Z

Good to hear this

borkdude 2021-07-16T09:26:28.298600Z

Perhaps Asami offers a better story here

borkdude 2021-07-16T09:26:33.298800Z

if perf becomes an issue

raspasov 2021-07-16T09:26:34.299Z

It’s for data that the user generates on one device, so it would take years for it to grow super big.

dotemacs 2021-07-16T09:27:02.299200Z

Yea, that makes sense

raspasov 2021-07-16T09:27:03.299400Z

I don’t doubt it… I just wanted to stay as close to Datomic as possible, and DataScript more or less offers that.

👍 1
raspasov 2021-07-16T09:27:19.299700Z

(not fully)

dotemacs 2021-07-16T09:27:26.299900Z

Are you syncing with Datomic or …?

borkdude 2021-07-16T09:27:35.300100Z

it's nice to have that affordance for sure on mobile

raspasov 2021-07-16T09:27:36.300300Z

Not currently, potentially in the future…

raspasov 2021-07-16T09:28:01.300500Z

The biggest issue with DataScript is querying perf.

raspasov 2021-07-16T09:28:26.300700Z

Even with a few thousand datoms, complex queries can take 50-100ms (!!!) on iOS

raspasov 2021-07-16T09:29:46.300900Z

So you have to be careful; I didn’t expect this initially and tripped me up. The DataScript home page oversells/oversimplifies the product, to put it mildly…

raspasov 2021-07-16T09:31:37.301100Z

It was my own ignorance and lack of benchmarking. Other than that, I have had no issues so far.

dotemacs 2021-07-16T09:32:43.301300Z

I did look at mentat aka datomish which was kind of a re-write of DataScript with a Sqlite backend: https://github.com/mozilla/mentat/tree/clojure (note the branch there clojure). And then a subsequent re-write in Rust… which seems to compile but haven’t got it running on device yet. Not that I tried very hard.

borkdude 2021-07-16T09:32:44.301500Z

Perhaps a little bit of caching could help mitigate that

raspasov 2021-07-16T09:34:20.301700Z

@borkdude Apparently it’s complicated; There’s a number of issues on github brining up performance; To do it well, you need to get into the weeds of “database science”; I’ve dabbled in general database design and it gets deep 🙂

raspasov 2021-07-16T09:35:21.301900Z

Query planning, etc. The datalog queries are very expressive and it’s not trivial to optimize them; the fact that you’re doing everything in one thread (JS) doesn’t help either, I think.

borkdude 2021-07-16T09:35:32.302100Z

I mean caching on the side of the app

borkdude 2021-07-16T09:35:35.302300Z

not in DataScript

borkdude 2021-07-16T09:35:44.302500Z

e.g. when you need to execute the same query over and over while the data hasn't changed

raspasov 2021-07-16T09:35:47.302700Z

Ah, yes; That’s what I ended up doing;

raspasov 2021-07-16T09:36:46.303Z

It’s a bit tedious but it mostly works; Basically only re-query things that you know have changed based on the most recent transaction;

raspasov 2021-07-16T09:38:42.303200Z

One of the “proper” ways to achieve better perf. would be some version of differential dataflow (not trivial to add, afaik) https://timelydataflow.github.io/differential-dataflow/

borkdude 2021-07-16T09:39:16.303400Z

So are you using using datascript in native?

raspasov 2021-07-16T09:39:23.303600Z

React Native

borkdude 2021-07-16T09:39:29.303800Z

so it's CLJS

raspasov 2021-07-16T09:39:32.304Z

Yes

borkdude 2021-07-16T09:39:45.304200Z

using a graalvm version might already speed up things considerably

raspasov 2021-07-16T09:40:54.304400Z

You think? That’s probably worth a shot…

dotemacs 2021-07-16T09:41:39.304900Z

I’ve seen that. CLJS is slower, as expected.

raspasov 2021-07-16T09:41:54.305100Z

And then iOS, afact, is like 10x slower (no idea why)

dotemacs 2021-07-16T09:42:18.305300Z

But how would you go about using Graalvm compiled db, from cljs?

raspasov 2021-07-16T09:42:45.305500Z

I think the RN runtime of JavaScript core is a bit restricted as opposed to running in Safari… (just speculating as to the reasons)

borkdude 2021-07-16T09:43:05.305700Z

@dotemacs mobiletest compiles the graalvm binary to a shared library. This shared library is then accessed from the mobile UI code. Not sure how to do this from React Native/CLJS. This probably relies on iOS specific APIs.

raspasov 2021-07-16T09:43:06.305900Z

@borkdude So when you say “graalvm version”… Is that running in like a real native thread, or something like Sci? I apologize, still not very well versed in all the differences.

borkdude 2021-07-16T09:44:24.306200Z

@raspasov what mobiletest does: it compiles everything to "real" native and then exposes hooks to SCI, so you can have dynamic behavior when you eval stuff in SCI.

borkdude 2021-07-16T09:44:54.306400Z

SCI is an interpreter which you can use as glue code between your "real" compiled functions. Not everything is interpreted by SCI, only the glue code, let's say.

raspasov 2021-07-16T09:45:19.306600Z

Got it… so some Clojure is compiled to “real” native.

borkdude 2021-07-16T09:45:50.306800Z

so if you have {:namespaces {'foobar {'foobar foobar}}} and then eval (require 'foobar) (foobar/foobar) in SCI, then the foobar call is exactly the same as outside SCI

raspasov 2021-07-16T09:45:50.307Z

Some is interpreted, to help provide REPL-like functionality?

borkdude 2021-07-16T09:46:32.307400Z

yes

raspasov 2021-07-16T09:46:35.307600Z

I have to go through the mobiletest example again with the latest updates; thank you 🙂

raspasov 2021-07-16T09:47:41.307800Z

When you compile DataScript/etc database to “real native”, I assume you’d have to communicate with it via async function calls with the React Native JS thread.

borkdude 2021-07-16T09:48:14.308Z

sure, but the shared library can be sync. The async stuff is a JS problem

raspasov 2021-07-16T09:48:30.308200Z

Yes.

borkdude 2021-07-16T09:48:32.308400Z

similar to how a REST endpoint is usually synchronous

raspasov 2021-07-16T09:53:01.308600Z

Right… when you say “shared library” that’s basically a regular method/fn call for other native code. Basically a “real” native library?

Huahai 2021-07-16T17:45:41.320800Z

I don’t think compile Datascript with GraalVM will help much with performance

borkdude 2021-07-16T17:46:45.321Z

no, but JVM-based vs JS-based probably does: https://twitter.com/nikitonsky/status/1398080743025356805

Huahai 2021-07-16T17:54:12.321300Z

The poor query performance of Datascript is mostly due to its lack of query optimization. I did some trivial optimization in Datalevin and the performance increases about 30% to 50% depending on dataset. I also have a plan to do a rewrite of the query engine in Datalevin. In any case, the slow performance of triple-store (compared with relational DB) is a well known problem, but that should not excuse Datomic and Datascript from doing any optimization at all. Currently, Asami does a decent job in query optimization, Crux claims to do that as well, but the effect may not be obvious from what I heard. Of course, this is just my opinion and I am obviously biased.

👍 2
phronmophobic 2021-07-16T16:10:44.316500Z

@dotemacs > what is needed to make graalvm compiled Clojure code accessible to react-native ClojureScript code? TLDR: You would need to make a native module, https://reactnative.dev/docs/native-modules-ios Roughly, the steps required would be: 1. Compile your clojure code to a shared library with native-image You need to explicitly mark functions that are available as part of the shared library API. Further, the API can only receive and return C data structures like ints, chars, pointers, structs, etc. 2. Create a native module that exposes the shared library API to your js code. Not sure how it would affect performance or memory usage, but I assume there is some overhead in the bridge between JS and native code. I think an interesting alternative would be to compile all your clojure code to native and talk to react native code directly. I've been looking at some of the UI options for mobile and I think one of the major complaints against react native is that the JS/native bridge is slow.

👍 1
dotemacs 2021-07-16T16:25:32.318100Z

I figured that those would be the steps @smith.adriane but what I’m unsure of is this part: > You need to explicitly mark functions that are available as part of the shared library API How would something like that be done for Clojure functions that would then be compiled via GraalVM?

phronmophobic 2021-07-16T16:27:15.319400Z

It looks like:

(defn compile-interface-class
  ([]
   (compile-interface-class nil))
  ([opts]
   (with-bindings {#'*compile-path* "library/classes"}
     ((requiring-resolve 'tech.v3.datatype.ffi.graalvm/expose-clojure-functions)
      {
       #'clj_print {:rettype :void
                    :argtypes [['bs :pointer]]}

       #'clj_prn {:rettype :void
                  :argtypes [['bs :int64]]}

       #'clj_eval {:rettype :int64
                   :argtypes [['bs :pointer]]}
       #'clj_start_server {:rettype :void
                           :argtypes []}

       #'clj_print_hi {:rettype :void
                       :argtypes []}
       }
      'com.phronemophobic.mobiletest.interface nil)))
  )
where clj_print, clj_prn, etc are just normal clojure functions that receive and return the arg types listed

dotemacs 2021-07-16T16:28:18.320Z

Oh, I’m with you, that’s from your project. Sorry for being slow on the uptake 🙂

1