Fork me on GitHub
#clojure-norway
<
2022-06-29
>
cjohansen06:06:23

God morgen!

partyparrot 1
kolstae06:06:38

God morgen 🙂

Ivar Refsdal07:06:39

God morgon 🙂

msolli07:06:12

Følger opp gårsdagens kollokvie med en alternativ løsning på @slipsets problem:

(defn sideffecting-expensive-operation
  [label]
  (let [x (rand-int 2000)]
    (println "Computing" label)
    (Thread/sleep 2000)
    (println "Done computing" label)
    x))

(defn setup
  [some-atom label]
  (let [r (locking label
            (sideffecting-expensive-operation label))]
    (swap! some-atom assoc label r)))

(def a (atom {}))

(do
  (future (setup a "foo"))
  (future (setup a "foo"))
  (future (setup a "bar")))

slipset07:06:17

Morn, morn!

slipset07:06:20

Ah, kollokvie, et ord jeg ikke har hørt på lenge!

msolli07:06:23

Denne vil kjøre sideffecting-expensive-operation flere ganger for samme label. Men hvis vi memoizer den blir det bra:

(def sideffecting-expensive-operation-memo (memoize sideffecting-expensive-operation))

(defn setup
  [some-atom label]
  (let [r (locking label
            (sideffecting-expensive-operation-memo label))]
    (swap! some-atom assoc label r)))

Jakub Holý (HolyJak)07:06:06

gir (locking label mening når label er string/keyword som ikke nødvendigvis er samme instance selv om det er samme verdi når vi kaller funksjonen?? Jeg tror locking er bare meningsfylt når du har samme ting (dvs identical, samme plass i minne) du prøver å låse fra froskjelige tråder

msolli07:06:02

Uten memo:

Computing foo
Computing bar
Done computing bar
Done computing foo
Computing foo
Done computing foo
Med memo:
Computing foo
Computing bar
Done computing foo
Done computing bar

Jakub Holý (HolyJak)07:06:06

gir (locking label mening når label er string/keyword som ikke nødvendigvis er samme instance selv om det er samme verdi når vi kaller funksjonen?? Jeg tror locking er bare meningsfylt når du har samme ting (dvs identical, samme plass i minne) du prøver å låse fra froskjelige tråder

msolli07:06:02

Men to strenger er identical:

(identical? "foo" "foo")
=> true

kolstae07:06:19

Literals vil være identical men ikke f.eks

(identical? "foo" (str "f" "oo"))
=> false

msolli07:06:45

Ah, det forandrer saken! Da funker ikke løsningen min: :thinking_face:

(let [foo-1 "foo"
      foo-2 (str "f" "oo")]
    (future (setup a foo-1))
    (future (setup a foo-2))
    (future (setup a "bar")))
Computing foo
Computing bar
Computing foo
Done computing foo
Done computing foo
Done computing bar

kolstae08:06:25

Det er mulig å fikse da om det skulle bli nødvendig en dag, men bare for String:

(identical? "foo" (.intern (str "f" "oo")))
=> true

msolli08:06:11

labelhøres ut som String. 🙂 Men hvis det er et keyword vil problemet allerede være løst, siden de er intern -ert (?) automatisk.

slipset08:06:25

label er i dette tilfellet en string.

msolli08:06:10

(locking (.intern label) ...)

msolli08:06:36

Da funker det igjen:

(let [foo-1 "foo"
      foo-2 (str "f" "oo")]
    (future (setup a foo-1))
    (future (setup a foo-2))
    (future (setup a "bar")))
Computing foo
Computing bar
Done computing foo
Done computing bar
Computing foo
Done computing foo
(Uten memoize)

Ivar Refsdal09:06:59

Relatert: https://clojuredocs.org/clojure.core/compare-and-set! frå notatane:

The note that compare-and-set! uses equality comparison is wrong, compare-and-set! really does use identity comparison (Java ==). Internally, Clojure uses the AtomicReference.compareAndSet(old, new) method.

The reason his example works is due to interning of the a symbol: in his example, each a is the same (identical) object.

But as you can see from the example below, even numeric autoboxing can lead to surprising results:

(def a (atom 0))
;=> #'user/a
(compare-and-set! a 0 100)
;=> true
;(compare-and-set! a 100 200)
;=> true
;; Fails?! (on Oracle JVM 8 with default settings)
(compare-and-set! a 200 300)
;=> false
@a
;=> 200 ; WAT?

Clojure almost always uses boxed numbers (via Java autoboxing) unless you take special steps to avoid it. compare-and-set! only accepts Objects, so numbers are autoboxed to Longs.

Java JVMs will usually intern small integers; by default Oracle/OpenJDK will intern -127 to 128 (the byte range) so that all such boxed numbers are identical. This can be altered with the -XX:AutoBoxCacheMax= command line flag. This may vary by JVM implementation, too.

👍 1
🙏 1
kolstae13:06:15

This is due to the impl of Long/valueOf which caches some smaller values: (I guess)

public static Long valueOf(long l) {
        final int offset = 128;
        if (l >= -128 && l <= 127) { // will cache
            return LongCache.cache[(int)l + offset];
        }
        return new Long(l);
    }

kolstae13:06:08

Hm, kunne vært på norsk denne 😊

1
msolli07:06:38

Det kan hende det er nyanser med locking jeg ikke er klar over, altså. Du kan evt. lage en wrapper-`record` for verdien du vil låse hvis du vil unngå å låse en bestemt streng for hele JVM-en.

cjohansen10:06:01

Dette var en lærerik utveksling. Keep it coming 👍 🙂

👍 2
slipset12:06:12

Bare fordi vi sier god morgen og blir kompiser lissom 🙂

mariuene12:06:01

God morgen 🙂