Clojurians
#clojure-italy
<
2018-04-09
>

This page is not created by, affiliated with, or supported by Slack Technologies, Inc.

gabriele.carrettoni09:04:46

giocato un po' con substratevm nel weekend, davvero interessante

gabriele.carrettoni09:04:00

avete qualche link/demo da condividere?

reborg09:04:53

hello people

reborg09:04:52

Direct linking peggiora le performance? Mi aspetterei l'esatto contrario da questi 2 bytecodes https://gist.github.com/reborg/2e60268d87c12e73e9460593f6440276 L'unica cosa che mi viene in mente e' se i call sites di quella funzione sono ottimizzati da hotspot durante il boot di Clojure e poi si rivelano non ottimali. Secondo: sbaglio i bench.

gabriele.carrettoni10:04:17

@reborg da dove arriva la slide che hai linkato su #clojure ?

bronsa10:04:19

@reborg sospetto fortemente errori nel benchmarking

reborg11:04:04

Essi', 90% sara' sicuramente quello. In sintesi: 1. cambio 2 linee in https://github.com/clojure/clojure/blob/master/src/clj/clojure/core.clj#L2744-L2745 con (.reduce (fn [b x] (chunk-append b (f x)) b) 2. mvn clean; mvn -Dmaven.test.skip=true install produce nuovo jar $M2_REPO/org/clojure/clojure/1.10.0-master-SNAPSHOT/clojure-1.10.0-master-SNAPSHOT.jar 3. faccio partire REPL con jar appena creato, aggiungendo criterium al classpath 4. eseguo bench tipo (let [lr (range 100000)] (bench (doall (map inc lr)))) 5. Creo (defn map* [f coll]) funzione locale con la stessa modifica di quella in core, ma solo arity-2 con la modifica, non il resto. 6. Eseguo stesso bench: 40% piu' veloce.

bronsa12:04:31

se mi ricordi sta sera ci butto un occhio

bronsa12:04:55

@reborg intuitivamente, fare multipli benchmark sulla stessa jvm non e` una grande idea

bronsa12:04:07

potresti aver scaldato alcuni metodi nel benchmark precedente

reborg12:04:51

credo di averli fatti alternativamente prima e dopo. Cmq, posso riprovare ancora

reborg12:04:17

Sembra the tu abbia ragione @bronsa, se tutte le volte ristarto la VM e bencho una cosa alla volta. Probabilmente poi ho confuso il tutto senza rendermene conto. Lavoro ingrato se ho una 50 di bench da runnare!

reborg12:04:34

ehm, pero' il risultato e' lo stesso: codice nuovo in core 3.77ms, stesso codice eval in REPL 3.25ms

bronsa14:04:46

interessante, dopo ci guardo sicuramente allora

reborg14:04:44

Grazie, sempre utile avere il tuo risconstro. Anche perche' ci vorrei andare a fondo e capire se c'e' uno sbaglio o che.

bronsa14:04:13

per escludere sta cosa delle arity, JITando esattamente la funzione identica i benchmark cambiano?

bronsa14:04:32

inoltre sarebbe interessante benchmarkare su una versione AOT ma non direct linked in questo caso

bronsa14:04:43

se hai tempo ti consiglerei di fare ste prove per vedere dove entra in gioco la varianza

bronsa14:04:53

altrimenti e` quel che penso faro` io sta sera

reborg14:04:02

si, lo faccio in background mo ci provo

reborg16:04:33

Allora: clj-head con modifica: 3.22ms copia REPL con modifica solo arity-2 REPL: 2.74ms copia REPL con modifica arity originali: 2.79ms Quindi no arity... andiamo di noDL: clj-head con modifica no DL: 3.65ms copia REPL con modifica solo arity-2 REPL no DL: 2.87ms copia REPL con modifica arity originali no DL: 2.88ms Quindi eliminiamo anche il fattore direct linking (che giustamente sembra peggiorare la cosa). Probabilmente ci sono altri fattori in gioco. Almeno ora so che evitare il compare versioni appena compilate e quello che viene dal jar.

bronsa16:04:45

bon quindi l’unica differenza sembra essere JIT vs AOT

reborg16:04:13

si rimane fuori quello

bronsa16:04:16

potrebbe essere un artifatto del classloader

bronsa16:04:29

che si deve scannare tutta la in memory cache prima di fallbackare su disco quando la funziona e` AOTd

bronsa16:04:56

potrebbe esserci un’ottimizzazione per DynamicClassLoader da fare fosse quello il caso

bronsa16:04:16

static Class<?> findInMemoryClass(String name) {
    Reference<Class> cr = classCache.get(name);
	if(cr != null)
		{
		Class c = cr.get();
        if(c != null)
            return c;
		else
	        classCache.remove(name, cr);
		}
	return null;
}

protected Class<?>findClass(String name) throws ClassNotFoundException {
	Class c = findInMemoryClass(name);
	if (c != null)
		return c;
	else
		return super.findClass(name);
}

reborg16:04:24

ma una volta letta dal disco non e' cachata per sempre?

bronsa16:04:24

ma il lookup devi farlo comunque e prima di poterlo fare se e` AOT, hai una cache miss sulla cache in memory

bronsa16:04:28

non ho idea se sia questo il motivo

bronsa16:04:30

ma potrebbe esserlo

bronsa16:04:08

fa na cosa

bronsa16:04:12

protected Class<?>findClass(String name) throws ClassNotFoundException {
	Class c = findInMemoryClass(name);
	if (c == null) {
      c = super.findClass(name);
      if (c != null)
        classCache.put(name, new SoftReference (c, rq)); 
    }
    return c;
}

bronsa16:04:23

prova a cambiare findClass di DynamicClassLoader con sta versione

bronsa16:04:43

e lancia di nuovo i benchmark JIT vs AOT

bronsa16:04:02

io parto per tornare a casa, mal che vada provo io tra 1 oretta

bronsa16:04:26

possibile che ci siano altri fattori in gioco ma intuitivamente questo e` l’unico che mi viene in mente

reborg16:04:23

No figurati provo. Ma quindi prima non veniva messa in cache in findClass?

bronsa17:04:00

no, c'è un altra cache nell'urlclassloader ma che viene hittata solo dopo una miss a quella di dynamicclassloader

bronsa17:04:37

quindi boh, quel millisecondo in più potrebbe essere quella cache miss

bronsa17:04:02

anche se mi sembra una possibilità remota

bronsa17:04:56

tanto vale provare

reborg17:04:19

No cambiamenti apprezzabili qui

gabriele.carrettoni13:04:56

@reborg che strada consigli di seguire per esplorare il codice di clojure? (btw molto interessante il talk, grazie per averlo tenuto e per avermi condiviso il link) se hai links o pdf da condividere sarei molto interessato

reborg13:04:28

@gabriele.carrettoni Personalmente ho trovato utile caricarlo su IntelliJ e cominciare ad andare in giro al codice, ad esempio come nel video prendendo una form e seguendone l'evaluation coi breakpoints in debug. Putroppo non c'e' una guida agli internals ma tante persone che nel tempo si sono cimentate nell'impresa e ne hanno tirato fuori blogs e presentazioni.

bronsa13:04:50

@gabriele.carrettoni sottoscrivo al consiglio di renzo, apriti una repl, attaccati un debugger, metti breakpoint su clojure.lang.Compiler.*/`Clojure.lang.LispReader.*`, evalua prima 1, poi (+ 1 2), poi (fn [x] (+ 1 x)) e segui l’evaluation

bronsa13:04:16

e non fare come me che da idiota s’e` imparato tutto seguendo a mano il sorgente invece di usare un debugger ;)

bronsa13:04:55

non imparerai tutti i dettagli di come e` implementato clojure in questo modo, ma ti farai una buona idea di come funzioni

bronsa13:04:39

l’alternativa se sei un masochista come me, apriti clojure.lang.RT e inizia a leggere sequenzialmente, seguendo tutte le reference

bronsa13:04:06

di codice ce n’e` sorprendentemente poco per un linguaggio

reborg13:04:23

Oi, ferma, credo che all'inizio inizio, mi ero stampato compiler.java in pdf

reborg13:04:42

per "comoda" lettura dall'ipad

reborg13:04:54

non ha funzionato molto bene :grin:

bronsa13:04:55

imparare l’implementazione non e` un’impresa cosi` erculea come si possa credere

bronsa13:04:45

@reborg haha, io c’avevo tutto il sorgente nel mio kindle 7" e me lo leggevo da li`, andando e tornando da scuola in bus in mancanza di un laptop

bronsa14:04:20

non lo rifarei manco mi pagassero

gabriele.carrettoni14:04:09

grazie dei consigli