Que faire avec Java BigDecimal la performance?
J'écris le trading de devises applications pour la vie, donc je dois travailler avec des valeurs monétaires (c'est une honte que Java n'ai toujours pas decimal float type et n'a rien à soutien de précision arbitraire des calculs monétaires). "L'Utilisation BigDecimal!" — pourrait-on dire. Je ne. Mais maintenant j'ai un code où les performances est un problème, et BigDecimal est plus de 1000 fois (!) plus lent que double
primitives.
Les calculs sont très simples: ce que le système ne calcule a = (1/b) * c
beaucoup de beaucoup de fois (où a
, b
et c
sont point fixe de valeurs). Le problème, cependant, réside avec cette (1/b)
. Je ne peux pas utiliser de point fixe de l'arithmétique, car il n'y a pas de point fixe. Et BigDecimal result = a.multiply(BigDecimal.ONE.divide(b).multiply(c)
n'est pas seulement laid, mais lentement lentement.
Que puis-je utiliser pour remplacer BigDecimal? J'ai besoin d'au moins 10x augmentation de la performance. J'ai trouvé par ailleurs un excellent JScience bibliothèque qui a une précision arbitraire arithmétique, mais il est encore plus lent que BigDecimal.
Des suggestions?
- si les valeurs de a, b et c ont peu de variation, vous pouvez memoize les valeurs.
- Curieusement, c'était quelque chose qui était plus facile à C. Juste un lien à l'encontre d'une BCD bibliothèque et que vous avez été fait!
- pas bizarre du tout, Java facilite les tâches courantes, tandis que les grandes décimal n'est pas tellement commun.
- Ne riez pas, mais une solution est d'utiliser PHP. Je viens de trouver cette annonce alors qu'il enquête sur la raison pour laquelle un petit programme que je me suis converti à partir de PHP Java a été beaucoup plus lent en Java qu'en PHP.
- Comment avez-vous atteint? N'PHP vous donner accès à AVX ou avez-vous eu recours au GPU? Plaisir à côté, je suis vraiment curieux. Peut-être poser une question...
- Je me rends compte que je suis quelques années de retard à la fête, mais quelle est la valeur de
b
? Saufb
est spécial, le codeONE.divide(b)
en panne. Donc, vous êtes en laissant quelques détails de votre question.
Vous devez vous connecter pour publier un commentaire.
Peut-être vous devriez commencer avec le remplacement d'un = (1/b) * c = c/b ? Ce n'est pas 10x, mais toujours quelque chose.
Si j'étais vous, je voudrais créer ma propre classe de l'Argent, ce qui permettrait de garder à long dollars et à long cents, et de faire des maths en elle.
Donc ma réponse était juste à plat de mal, parce que mon test a été écrit mal. Je suppose que je suis le seul qui doit avoir été critiqué, pas OP 😉 Cela peut avoir été l'un des premiers benchmarks que j'ai jamais écrit... eh bien, c'est la façon dont vous apprenez. Plutôt que de supprimer la réponse, voici les résultats, si je ne suis pas en mesure de quelque chose de mal. Quelques remarques:
BigDecimal.doubleValue()
, comme il est extrêmement lentBigDecimal
s. Il suffit de retourner une valeur, et d'utiliser une instruction if pour empêcher l'optimisation du compilateur. Assurez-vous de faire travailler la plupart du temps pour permettre à la branche de prédiction afin d'éliminer la partie du code.Tests:
Voici le résultat:
Voici le code:
BigDecimal
... ou plus exactement, la création de surcharge est présente dans tous les points de repère et peut les dominer. Sauf si c'est ce que tu voulais (mais pourquoi?), vous devez créer au préalable les valeurs et les stocker dans un tableau.if
. Il n'obtiendrez probablement pas optimisé, mais il peut. J'ai utilisé pour faire quelque chose commeresult += System.identityHashCode(o)
mais ensuite j'ai découvert le JMHBlackHole
.Plus du double des opérations vous donner plus de suffisamment de précision. Vous pouvez représenter de 10 billions de dollars avec cent de précision avec double qui peut être plus que suffisant pour vous.
Dans tous les systèmes de trading, j'ai travaillé sur (quatre banques différentes), ils ont utilisé le double appropriée de l'arrondissement. Je ne vois pas de raison d'être à l'aide de BigDecimal.
double
pour les prix oulong
cents.En supposant que vous pouvez fonctionner jusqu'à un certain arbitraire, mais connu de précision (un milliardième de cent) et une valeur maximale, vous avez besoin de la poignée (un de trillions de trillions de dollars?) vous pouvez écrire une classe qui stocke cette valeur comme un nombre entier de milliardièmes de cent. Vous aurez besoin de deux longs pour le représenter. Qui devrait être peut-être dix fois plus lent que l'utilisation de double; une centaine de fois plus rapide que BigDecimal.
La plupart des opérations sont la simple exécution de l'opération sur chaque partie et renormer. La Division est un peu plus compliqué, mais pas beaucoup.
EDIT:En réponse à l'observation. Vous devrez mettre en place un bitshift fonctionnement de votre classe (facile que de le long que le multiplicateur pour le haut de long est une puissance de deux). Faire de décalage de la division, le diviseur jusqu'à ce qu'il n'est pas beaucoup plus grand que le dividende; soustraire décalé diviseur dividende et incrémenter le résultat (avec les maj). Répétez.
MODIFIER à NOUVEAU:Vous pouvez trouver BigInteger fait ce que vous avez besoin ici.
Magasin longs que le nombre de centimes. Par exemple,
BigDecimal money = new BigDecimal ("4.20")
devientlong money = 420
. Vous avez juste à vous rappeler de mod par 100 pour obtenir des dollars et des cents pour la sortie. Si vous avez besoin de suivre, de dire, dixièmes de cent, ça deviendraitlong money = 4200
à la place.Vous pouvez déplacer de point fixe de mathématiques. Juste à la recherche de quelques bibliothèques de la droite maintenant.
sur le site de sourceforge point fixe je n'ai pas regardé en profondeur encore. beartonics
Avez-vous testé avec org.jscience.de l'économie.de l'argent? depuis qu'il a assuré de l'exactitude. Le point fixe ne seront plus précis que le nombre de bits attribués à chaque morceau, mais il est rapide.
Je me souviens d'assister à une présentation de vente d'IBM pour une accélération matérielle de la mise en œuvre de BigDecimal. Donc, si votre plate-forme cible est IBM System z, ou d'un Système p, vous pouvez exploiter cette façon transparente.
Le lien suivant pourrait être de quelque utilité.
http://www-03.ibm.com/servers/enable/site/education/wp/181ee/181ee.pdf
Mise à jour: le Lien ne fonctionne plus.
Quelle est la version du JDK/JRE utilisez-vous?
Aussi, vous pouvez essayer ArciMath BigDecimal pour voir si leurs vitesses pour vous.
Edit:
Je me souviens avoir lu quelque part (je crois que c'était Efficace Java) que le BigDecmal classe a changé de se JNI appelé à une bibliothèque C pour tous les produits Java à un certain point... et il a obtenu plus rapidement à partir de cela. Il se pourrait donc que toute précision arbitraire de la bibliothèque que vous utilisez ne va pas vous obtenir la vitesse dont vous avez besoin.
Personnellement, je ne pense pas que BigDecimal est idéal pour cela.
Vous voulez vraiment mettre en œuvre votre propre Argent de la classe à l'aide de longs en interne pour représenter la plus petite unité (c'est à dire cent, 10 cent). Il y a certains travaux, la mise en œuvre de
add()
etdivide()
etc, mais ce n'est pas vraiment dur.Jetant un peu plus de matériel à ce qui pourrait être moins cher (en tenant compte de la probabilité d'avoir une monnaie erreur de calcul).
1/b n'est pas exactement représentable avec BigDecimal soit. Voir les docs de l'API à la manière dont le résultat est arrondi.
Il ne devrait pas être trop difficile d'écrire votre propre décimal fixe de classe basée autour d'un long champ ou deux. Je ne sais pas tout sur les étagères des bibliothèques.
facile... de votre tour de résultats souvent permettra d'éliminer le type de données double de l'erreur.
si vous faites le calcul du bilan, vous devez également envisager qui sera propriétaire de la plus/moins sou causés par l'arrondissement.
bigdeciaml calcul a produit plus de/moins de penny trop, songez à 100/3 cas.
double
valeurs sont mises à l'échelle, de telle manière que n'importe quel domaine-requis arrondi est toujours un nombre entier, alors toutes les valeurs arrondies sera "exact" sauf s'ils sont vraiment gros. Par exemple, si les choses qui rond le plus proche de 0,01 $sont stockés comme un certain nombre de pièces de monnaie plutôt que de dollars,double
sera peut-penny-montants arrondis précisément, à moins qu'ils exceeed $45,035,996,273,704.96.double
a près de 16 chiffres. Supposons 9 d'entre eux avant la virgule, 2 après, et vous êtes de gauche avec 5 à graver... dans le pire des cas, vous pourriez faire 105 fonctionnement avant de souffle, en supposant aléatoire de l'erreur d'arrondi de la distribution moyenne des cas, il serait 1010.double
est trop imprécis, mais ils sont plutôt rares.Je sais que je vais l'afficher dans de très vieux sujet, mais c'était la première rubrique trouvé par google.
Envisager de déplacer vos calculs à la base de données à partir de laquelle vous êtes probablement prendre les données pour le traitement. Aussi je suis d'accord avec Gareth Davis, qui a écrit:
Dans la plupart des cas de mal de requêtes ont plus d'incidence sur les performances de la bibliothèque math.
Pouvez-vous fournir plus de perspicacité que le but de ce calcul?
Ce que votre affaire est un compromis entre la vitesse et la précision. Combien grande sera la perte de précision si vous êtes passé à un primitif?
Je pense que dans certains cas l'utilisateur peut être à l'aise avec moins de précision en échange de vitesse, tant qu'ils peuvent examiner le calcul précis en cas de besoin. Cela dépend vraiment de ce que vous allez utiliser ce calcul pour.
Peut-être vous pouvez permettre à l'utilisateur de prévisualiser le résultat rapidement en utilisant un double, puis demande la plus précise de la valeur à l'aide de BigDecimal si elles le désirent?
Est JNI une possibilité? Vous pouvez peut-être récupérer un peu de vitesse et potentiellement exploiter les indigènes point fixe bibliothèques (peut-être même de l'ESS* la bonté trop)
Peut-être http://gmplib.org/
Peut-être que vous devriez regarder dans l'obtention de l'accélération matérielle arithmétique décimale?
http://speleotrove.com/decimal/
A eu un problème similaire dans un système de négociation d'actions en 99. Au tout début de la conception, nous choisir pour chaque nombre dans le système représenté comme un long multiplié par 1000000 ainsi 1.3423 était 1342300L. Mais le facteur principal de ce mémoire a été empreinte de pieds plutôt qu'en ligne droite sur la performance.
Un mot sur la prudence, je ne le referais pas aujourd'hui, si j'étais vraiment assurer que la performance en mathématiques a été super critique. Dans la plupart des tourbières standard webapps la surcharge de jdbc pour l'accès et l'accès à d'autres ressources du réseau marais de tout avantage d'avoir vraiment rapide mathématiques.
Il semble que la solution la plus simple est d'utiliser BigInteger plutôt longue à mettre en place au pesto de solution. Si il semble désordonné, il serait facile d'écrire une classe qui encapsule BigInteger pour masquer le réglage de précision.
Communes Mathématiques, L'Apache Commons Bibliothèque De Mathématiques
http://mvnrepository.com/artifact/org.apache.commons/commons-math3/3.2
Selon ma propre analyse comparative pour mon cas d'utilisation spécifiques c'est 10 à 20 fois plus lent que le double (bien mieux que 1000x) - essentiellement pour l'addition /multiplication. Après évaluation, un algorithme qui a eu une suite d'additions suivie par une élévation à la puissance de la baisse de la performance était un peu pire: 200x - 400x. Il semble donc assez rapide pour le + et *, mais pas d'exp et log.
Remarque: L'API protège les constructeurs de forcer une usine modèle de nommage de l'usine DfpField (plutôt que le un peu plus intuitive DfpFac ou DfpFactory). Donc, vous avez à utiliser
pour instancier un Dfp, vous pouvez appeler
.multiply
ou quoi que ce soit sur ce. J'ai pensé que je le mentionne parce que c'est un peu déroutant.Sur une JVM 64 bits de la création de votre BigDecimal comme ci-dessous rend environ 5x plus rapide: