This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-09-20
Channels
- # aleph (1)
- # announcements (1)
- # aws (11)
- # babashka (117)
- # beginners (34)
- # calva (13)
- # cider (3)
- # clj-commons (8)
- # clj-kondo (24)
- # clj-yaml (36)
- # cljsrn (46)
- # clojure (50)
- # clojure-australia (5)
- # clojure-europe (239)
- # clojure-nl (3)
- # clojure-norway (3)
- # clojure-spec (16)
- # clojurescript (25)
- # core-typed (20)
- # cursive (41)
- # datahike (1)
- # datalevin (1)
- # datomic (17)
- # fulcro (27)
- # hyperfiddle (35)
- # introduce-yourself (1)
- # jobs (4)
- # lsp (20)
- # malli (8)
- # meander (8)
- # nbb (1)
- # off-topic (31)
- # parinfer (9)
- # pathom (3)
- # portal (2)
- # re-frame (20)
- # react (2)
- # reagent (8)
- # releases (1)
- # remote-jobs (4)
- # scittle (2)
- # shadow-cljs (8)
- # slack-help (4)
- # sql (30)
- # squint (3)
- # tools-deps (34)
- # xtdb (21)
JDK19 virtual threads + bb: https://twitter.com/borkdude/status/1572222344684531717

The first thing I'll probably do is make the threads in the core.async
go macro use this, currently it defaults to 1 normal thread
> # whoopps lol
Great demo! Are you aware of any studies that compare the performance/scaling capabilities of Loom threads and virtual threads in other languages, e.g. goroutines?
good you're experimenting with this @U04V15CAJ but I'd be very careful
if you have Vars with thread-bounded global namespace are you sure this will work as intended?
If you remember few months back I wrote an optimistic article about Virtual thread and structured concurrency, but during night I have actually nightmares about this
how def
and defn
work with thread-local? btw you're wildly overestimating my Clojure capabilities 🙂
@ales.najmann def
and defn
create globally mutable things. many things are mutable in Java. What is the problem? ;)
well if def
and defn
create entries in thread-local and just by referring to these things they got duplicated per thread, you have a problem. if that's the case solutions based around idea that you can launch millions of cheap virtual threads simply falls apart, because they're no longer cheap. You said that only dynamic vars follow thread semantics and use thread local - this is where I'm losing it because I didn't have time to follow white rabbit down this hole.
But you're probably right, there is no reason why Vars in general should use thread locals. I'm almost certainly exaggerating here.
@ales.najmann I tried this with both normal threads and virtual threads and they yielded the same result:
(def ^:dynamic *foo* 10)
(defn lots-of-tasks
[concurrency]
(binding [*foo* 111]
(let [executor #_(Executors/newFixedThreadPool concurrency)
(Executors/newVirtualThreadPerTaskExecutor)
tasks (mapv (fn [_]
(bound-fn []
(Thread/sleep 1000)
*foo*))
(range concurrency))
start-time (System/currentTimeMillis)
sum (->> (.invokeAll ^ExecutorService executor tasks)
(map #(.get ^Future %))
(reduce +))
end-time (System/currentTimeMillis)]
{:sum sum
:time-ms (- end-time start-time)})))
You are right that one should be mindful about conveying bindings to virtual threads, that doesn't happen automatically, but it doesn't happen automatically with normal threads either
This is quite convincing. You're smart man. I've to do some exercise during the weekend using Clojure (or bb) around this topic. It seems like a great thing to do.
From the docs:
Virtual threads support thread-local variables, synchronized blocks, and thread interruption; therefore, code working with Thread and currentThread won't have to change. Indeed, this means that existing Java code will easily run in a virtual thread without any changes or even recompilation!
I suspect a virtual thread will always be tied to the thread it started to run on, else you can't have the above reliably OR you will have to do a lot of magic
There is a concept of pinning and my bet is thread-locals can be one cause for pinning virtual thread to an OS thread
But let me tell you why I'm so scared. I've seen video where Oracle engineer created a loop over collection of more than 10 million items launching thread for each individual one of them. If you do this and you hit pinning,your VM is toast
The above example (in tweet) does something similar (but for 100k threads, which would normally also toast your running program)
Follow-up question to an old thread: https://clojurians.slack.com/archives/CLX41ASCS/p1655276723468789.
Is the motivation for providing a single function fs/modified-since
rather than a whole makefile.edn
thing that a single function is a better abstraction than inventing a different data format? So that with fs/modified-since
, you can essentially get the makefile stuff easily, as bb tasks, a separate CLI, or something else?
And to be able to have dynamically computed dependencies rather than static ones?
@teodorlu Providing a function and be able to code is more flexible than having a whatever.edn
right? This is why tools.build is a library as well
Though it took me three months of writing code around makefile generation to see your point of view.
came across https://andydote.co.uk/2022/09/19/make-content-hash/ on the orange site today.
yeah, we've spoken about this @mkvlr that it could be interesting to do more with content-hashing in a bb lib
is the blog post sort of "in the absence of bb and bb tasks and fs/modified-since
we can do lots of fancy stuff to get dynamic make-like behavior"?
For nextjournal I've already done some content-hashing stuff in bb, but it isn't public
I also recently spoke about this with @robert-stuttaford who has similar problems with slow cljs builds and he asked if it was ok if I showed that code to him @mkvlr
yes pleeeeeaassse 🙂
sure, we can also open it up though probably a bit early to commit to a stable api maybe?
The idea is basically to calculate all namespaces that are reachable from the main CLJS namespace(s) including macro namespaces and then content-hash those
forgot that 😅 /cc @robert-stuttaford
i assume the 'compiled cljs' is the :none optimised file? how do you integrate this with shadow?
ok it looks like clerk's use is not for app cljs but for something else
to be clear, we have a 6 minute shadow-cljs :release build that we'd like to speed up, which builds 7 shadow builds. right now we're not doing anything in terms of conditional compilation; we simply build everything always. seems like this solution is about hashing the source tree, and then checking if that hash is on <storage>. if not found, build, otherwise, continue to reference existing assets in <storage>, and bypass building any cljs.
so if even one cljs or required cljc file changes, you're running your full build
Does using sci from babashka usually work? I am getting
Could not resolve symbol: sci/eval-string
from:
bb -e "(require '[sci.core :as sci]) (sci/eval-string \"32\")"
There are some SCI things in there, but not all. sci.core/stacktrace
was added to get a stacktrace from an exception
I’m writing a tool in nbb that takes a description of questions from stdin or a file, asks the questions, and outputs the result to a file. (Sort of re-awakening a pet project).
One feature I want to add is something like:
{:type :number,
:name :phone,
:message "What is your phone number?",
:validate '(fn [value]
(or (= 10 (count (str value)))
(str "10 digits please, I got: " value " which is " (count (str value)) " digits.")))}
you might want to wrap this in (js/Promise.resolve ...)
- in the future this might change to a promise so then you will be prepared in case it changes
It appears that eval returns a string. But I want the datastructure 🙂
user=> (type (eval "3"))
java.lang.String
If anyone want to play around with JDK 19 + virtual threads:
https://github.com/graalvm/graalvm-ce-dev-builds/releases/tag/22.3.0-dev-20220915_2039
Bb branch: jdk19-loom
Dev docs: https://github.com/babashka/babashka/blob/master/doc/dev.md
/cc @ales.najmann
I will post an example program in the thread
(import '[java.util.concurrent ExecutorService Executors Future])
(defn lots-of-tasks
[concurrency]
(let [executor #_(Executors/newFixedThreadPool concurrency)
;; this uses one thread per concurrent operation... too many when you
;; want to create 100.000!!!! now let's swap the executor out with
;; virtual threads aka loom on JDK19!!!!
(Executors/newVirtualThreadPerTaskExecutor)
tasks (mapv #(fn []
(Thread/sleep 1000)
%)
(range concurrency))
start-time (System/currentTimeMillis)
sum (->> (.invokeAll ^ExecutorService executor tasks)
(map #(.get ^Future %))
(reduce +))
end-time (System/currentTimeMillis)]
{:sum sum
:time-ms (- end-time start-time)}))
(lots-of-tasks 100000)
thanks @U04V15CAJ
I'm really enjoying unix pipes + lines of EDN + bb -I -O ...
. (https://book.babashka.org/#_input_and_output_flags)
❤️
Here's an example:
$ seq 8 | bb -IO '(map (fn [n] {:n n :double (* 2 n)}) *input*)'
{:n 1, :double 2}
{:n 2, :double 4}
{:n 3, :double 6}
{:n 4, :double 8}
{:n 5, :double 10}
{:n 6, :double 12}
{:n 7, :double 14}
{:n 8, :double 16}
... and here's the (single line) I just used to set "language english" for 65 files that didn't have a specified language (https://github.com/teodorlu/play.teod.eu/commit/28d21f9f284a28a7a9f8315e0e831f88d656a4e7), all because I could work with "lines of EDN":
$ ./play.clj relations :from files :to lines | bb -I -O '(map #(assoc % :lang :en) *input*)' | ./play.clj relations :from lines :to files
has smth changed?
(babashka.tasks/shell {:out :string} (babashka.process/tokenize "ls -la"))
[java.lang.IllegalArgumentException
"No implementation of method: :as-file of protocol: #' found for class: clojure.lang.PersistentVector"
nil]
@dennisa babashka.tasks/shell never received a vector, you can just write:
(babashka.tasks/shell {:out :string} "ls -la")
or
(babashka.tasks/shell {:out :string} "ls" "-la")
This has always been the case. Many babashka.process
functions take a vector thoughIs there are way to read the error in bb shell without throwing an exception ? smth like
(shell {:out :string :err :string } "ls err") ; fails now
Read :out when success and error in caseHi, how can I access the "this" object in SCI (skittle) ? Ist there any macro for it?
(defn Foo [z]
#js {:x 1
:y 2
:z z
:test #(. (js* "this") -z)})
(set! (. js/window -Foo) Foo)