Cosa rende le chiamate JNI lente?

So che i ‘confini di attraversamento’ quando si effettua una chiamata JNI in Java è lento.

Tuttavia voglio sapere cos’è che lo rende lento? Cosa fa l’implementazione di jvm sottostante quando si effettua una chiamata JNI che lo rende così lento?

Innanzitutto, vale la pena notare che con “slow” stiamo parlando di qualcosa che può richiedere decine di nanosecondi. Per i banali metodi nativi, nel 2010 ho misurato le chiamate a una media di 40 ns sul mio desktop di Windows e 11 ns sul mio desktop Mac. A meno che tu non stia facendo molte telefonate, non te ne accorgi.

Detto questo, chiamare un metodo nativo può essere più lento di una normale chiamata al metodo Java. Le cause includono:

  • I metodi nativi non verranno delineati dalla JVM. Né saranno compilati just-in-time per questa specifica macchina – sono già compilati.
  • Un array Java può essere copiato per l’accesso nel codice nativo e successivamente copiato. Il costo può essere lineare nella dimensione dell’array. Ho misurato la copia JNI di un array da 100.000 a una media di circa 75 microsecondi sul mio desktop Windows e 82 microsecondi su Mac. Fortunatamente, l’accesso diretto può essere ottenuto tramite GetPrimitiveArrayCritical o NewDirectByteBuffer .
  • Se il metodo viene passato a un object o deve effettuare una richiamata, il metodo nativo probabilmente effettuerà le proprie chiamate alla JVM. L’accesso ai campi, ai metodi e ai tipi Java dal codice nativo richiede qualcosa di simile alla riflessione. Le firme sono specificate in stringhe e richieste dalla JVM. Questo è sia lento che sobject a errori.
  • Le stringhe Java sono oggetti, hanno lunghezza e sono codificati. L’accesso o la creazione di una stringa potrebbe richiedere una copia O (n).

Alcune discussioni aggiuntive, possibilmente datate, possono essere trovate in “Java¿ Platform Performance: Strategies and Tactics”, 2000, di Steve Wilson e Jeff Kesselman, nella sezione “9.2: Esame dei costi JNI”. È circa un terzo del modo in giù in questa pagina , fornito nel commento di @Philip qui sotto.

Il documento IBM developerWorks 2009 “Best practice per l’utilizzo di Java Native Interface” fornisce alcuni suggerimenti su come evitare le insidie ​​delle prestazioni con JNI.

Fondamentalmente la JVM costruisce in modo interpretativo i parametri C per ogni chiamata JNI e il codice non è ottimizzato.

Ci sono molti altri dettagli delineati in questo documento

Se sei interessato al benchmarking di JNI rispetto al codice nativo, questo progetto ha il codice per eseguire benchmark.

Vale la pena ricordare che non tutti i metodi Java contrassegnati con native sono “lenti”. Alcuni di loro sono intrinsechi che li rendono estremamente veloci. Per verificare quali sono intrinseci e quali no, puoi cercare do_intrinsic su vmSymbols.hpp .