Frage

Ich brauche Math.exp() von Java berechnen sehr häufig, ist es möglich, eine native Version zu bekommen, um schneller zu laufen als java 's Math.exp() ??

Ich habe gerade versucht jni + C, aber es ist langsamer als einfach nur java .

War es hilfreich?

Lösung

1 zu Ihrer eigenen exp schriftlich () -Implementierung. Das heißt, wenn dies ist wirklich ein Flaschenhals in Ihrer Anwendung. Wenn Sie mit einer kleinen Ungenauigkeit umgehen können, gibt es eine Reihe von äußerst effizienten Exponent Schätzung gibt Algorithmen aus, von denen einige Jahrhunderte zurückgeht. Wie ich es, Java exp () Implementierung verstehen ist ziemlich langsam, auch für Algorithmen, die „exakte“ Ergebnisse zurückgeben muss.

Oh, und haben Sie keine Angst, dass exp () Implementierung in reiner Java zu schreiben. JNI hat eine Menge Aufwand und die JVM ist in der Lage Bytecode zur Laufzeit optimieren manchmal sogar darüber hinaus, was C / C ++ ist in der Lage zu erreichen.

Andere Tipps

Dies wurde bereits angefordert mehrmals (siehe zum Beispiel hier ). Hier ist eine Annäherung an Math.exp (), kopiert von

Die Nutzung von Java.

Auch Cache Ergebnisse der exp und dann können Sie die Antwort aussehen schneller als sie wieder zu berechnen.

Sie würden wollen auch was Schleife Berufung Math.exp() in C wickeln. Andernfalls wird überwältigt der Aufwand für Rangier- zwischen Java und C jeden Performance-Vorteil.

Das könnte Sie der Lage sein, sie laufen zu lernen schneller, wenn man sie in den Reihen zu tun. einen JNI Anruf tätigen fügt Overhead, so dass Sie wollen nicht, dass es für jeden exp () tun Sie berechnen müssen. Ich würde versuchen, eine Reihe von 100 Werten und bekommen die Ergebnisse vorbei, um zu sehen, ob es hilft Leistung.

Die eigentliche Frage ist, hat dies für Sie einen Flaschenhals werden? Haben Sie Ihre Anwendung profiliert und fanden diese eine Hauptursache für Verlangsamungs sein?
Wenn nicht, würde ich mit Hilfe von Java-Version empfehlen. Versuchen Sie nicht, wie diese im Voraus zu optimieren nur dazu führen, Entwicklung verlangsamen. Sie können eine längere Zeit an einem Problem verbringen ist das kein Problem sein.

Aber sagen, dass ich glaube, Ihre Test ergab Sie Ihre Antwort. Wenn jni + C langsamer ist, verwenden Sie Java-Version.

Commons Math3 Schiffe mit einer optimierten Version: FastMath.exp(double x). Es hat meinen Code erheblich beschleunigen.

Fabien lief einige Tests und fand heraus, dass es fast doppelt so schnell wie Math.exp() war:

 0.75s for Math.exp     sum=1.7182816693332244E7
 0.40s for FastMath.exp sum=1.7182816693332244E7

Hier ist die javadoc:

Ermittelt exp (x), Funktionsergebnis ist fast abgerundet. Es wird für 99,9% der Eingangswerte auf den theoretischen Wert richtig gerundet werden, sonst wird es einen 1 UPL Fehler hat.

Methode:

    Lookup intVal = exp(int(x))
    Lookup fracVal = exp(int(x-int(x) / 1024.0) * 1024.0 );
    Compute z as the exponential of the remaining bits by a polynomial minus one
    exp(x) = intVal * fracVal * (1 + z)

Genauigkeit:. Berechnung mit 63 Bit Genauigkeit durchgeführt wird, so Ergebnis richtig für 99,9% der Eingangswerte gerundet werden soll, mit weniger als 1 ULP Fehlern sonst

Da der Java-Code wird mit den Just-in-Time-Compiler (JIT), gibt es wirklich keinen Grund zu nativen Code kompiliert bekommen JNI zu verwenden nativen Code zu nennen.

Außerdem sollten Sie nicht die Ergebnisse einer Methode Cache, in dem die Eingangsparameter Gleitkommazahlen reelle Zahlen sind. Die in der Zeit erhaltenen Gewinne werden sehr viel in viel Platz gebraucht verloren.

Das Problem mit JNI ist der Aufwand bei der Herstellung der Aufruf an JNI beteiligt. Die Java Virtual Machine ist ziemlich in diesen Tagen optimiert und fordert die eingebaute in Math.exp () sind gerade bis zur C exp () Funktion automatisch optimiert nennen, und sie vielleicht sogar in gerade x87 optimiert werden Gleitkommazahlen Montage Anweisungen.

Es gibt einfach einen Overhead im Zusammenhang mit dem JNI, siehe dazu auch: http://java.sun.com/docs/ Bücher / Leistung / 1st_edition / html / JPNativeCode.fm.html

So wie andere vorgeschlagen haben try Operationen zu sammeln, die die JNI bedeuten würde verwenden.

Ihre eigenen Schreiben, auf Ihre Bedürfnisse zugeschnitten.

Zum Beispiel, wenn alle Ihre Exponenten der Potenz von zwei sind, können Sie Bitverschiebung verwenden. Wenn Sie mit einem begrenzten Bereich oder einen Satz von Werten arbeiten, können Sie Look-up-Tabellen verwenden. Wenn Sie nicht Pin-Point-Präzision benötigen, können Sie eine ungenaue verwenden, aber schneller Algorithmus.

Es gibt eine Kosten im Zusammenhang mit über die JNI Grenze aufrufen.

Wenn Sie könnten die Schleife bewegen, die auch exp () in den nativen Code aufruft, so dass es nur einen nativen Aufruf ist, dann könnten Sie bessere Ergebnisse zu erzielen, aber ich bezweifle es deutlich schneller sein als die reine Java-Lösung .

Ich weiß nicht, die Details Ihrer Anwendung, aber wenn Sie eine ziemlich begrenzte Anzahl von möglichen Argumenten für den Anruf haben, können Sie eine vorausberechnete Verweistabelle verwenden, um Java-Code schneller zu machen.

Es gibt schnellere Algorithmen für exp je nachdem, was your'e zu erreichen versuchen. Ist das Problem Raum auf einen bestimmten Bereich beschränkt, brauchen Sie nur eine bestimmte Auflösung, Präzision und Genauigkeit, etc.

Wenn Sie Ihr Problem sehr gut definieren, können Sie feststellen, dass Sie eine Tabelle mit Interpolation verwenden können, zum Beispiel, was fast alle anderen Algorithmus aus dem Wasser blasen wird.

Welche Einschränkungen können Sie gelten für exp, dass die Leistung Kompromiss zu gewinnen?

-Adam

  

Ich betreibe einen Anpassungsalgorithmus und der minimale Fehler des Beschlags Ergebnis ist viel größer   als die Genauigkeit des Math.exp ().

transzendentale Funktionen sind immer viel langsamer als Addition oder Multiplikation und ein bekannten Engpass. Wenn Sie wissen, dass Ihre Werte in einem engen Bereich sind, können Sie einfach eine Lookup-Tabelle (zwei sortierten Array; ein Eingang, ein Ausgang) bauen. Verwenden Sie Arrays.binarySearch den richtigen Index zu finden und zu interpolieren Werte mit den Elementen in [Index] und [Index + 1].

Eine andere Methode ist es, die Zahl zu teilen. Lets nehmen z.B. 3,81 und Spaltung, die in 3 + 0,81. Nun multiplizieren Sie e = 2,718 dreimal und 20.08 erhalten.

Jetzt auf 0,81. Alle Werte zwischen 0 und 1 konvergieren schnell mit der bekannten Exponentialreihe

1 + x + x ^ 2/2 + x ^ 3/6 + x ^ 4/24 .... usw.

Nehmen Sie so viele Begriffe, wie Sie für Präzision benötigen; leider ist es langsamer, wenn x gegen 1. Lassen Sie uns sagen Sie zu x ^ 4 gehen, dann bekommen Sie 2,2445 statt der korrekten 2,2448

multiplizieren dann das Ergebnis 2,781 ^ 3 = 20,08 mit 2,781 ^ 0,81 = 2,2445, und Sie haben das Ergebnis 45.07 mit einem Fehler von einem Teil von zweitausend (richtig: 45,15).

Es ist vielleicht nicht relevant sein, nicht mehr, aber nur damit Sie wissen, in den neuesten Versionen der OpenJDK (siehe hier ) sollte Math.exp eine intrinsische gemacht werden (wenn Sie nicht wissen, was das ist, überprüfen hier ).

Dadurch wird die Leistung unschlagbar auf den meisten Architekturen machen, weil es die Hotspot VM bedeutet, wird der Anruf an Math.exp durch einen prozessorspezifischen Implementierung von exp zur Laufzeit ersetzen. Sie können nie diese Anrufe schlagen, da sie für die Architektur optimiert werden ...

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top