2010-06-22 12 views
4

J'essaie de réduire les coûts de performance/récupération de place des instructions de consignation. Je veux avoir beaucoup d'instructions de journalisation que je pourrais activer pour le débogage, mais aussi avoir un moyen de les désactiver pour une production rapide.Frais généraux (vitesse et ordures) des fonctions d'appel qui ne font rien

Je couru une référence à appeler les méthodes suivantes:

public static final isLogging = false; 

public static logObjs(Object[] params) { 
    if(isLogging) 
    System.out.println(params[0]); 
} 

public static log3Obj(Object a, Object b, Object c) { 
    if(isLogging) 
    System.out.println(a); 
} 

public static logInts(int a, int b, int c) { 
    if(isLogging) 
    System.out.println(a); 
} 

I étalonnées les fonctions d'un procédé d'attaque

long sum = 0; 
for(int i = 0; i < 100000000; ++i) { 
    int a = i; int b = i+1; int c = i+2; 
    logFoo(a,b,c); 
    sum += a; } 

logObjs (i, i + 1, i + 2) prend environ 2 secondes pour les itérations 1e8 et produit beaucoup d'ordures. Les sources sont, je suppose, autoboxing d'entiers et la création Object [] pour la variable # de paramètres. Log3Obj produit beaucoup (mais moins) de déchets et prend environ 1,2 secondes; encore une fois, je suppose que l'autoboxing se produit encore. LogInts est très rapide (0,2 sec), tout aussi rapide que la boucle sans appel de fonction. Donc, le problème est que même si la fonction ne fait rien de déterministe, l'autoboxing arrive toujours. Dans mon code, je préférerais que isLogging ne soit pas final, mais plutôt qu'il soit un paramètre d'exécution, mais pour ce faire, ce cas plus simple (où le compilateur peut prouver que la fonction ne fait rien) devrait courir. Bien sûr, je peux remplacer tous mes relevés d'enregistrement avec

if(isLogging) 
    logObjs(a, b, c); 

mais c'est très inélégant. Je pensais que c'est quelque chose que le JIT devrait prendre en charge. J'ai essayé pas mal de réglages du compilateur, mais il y a peut-être quelque chose qui me manque? Comment puis-je faire que le code ne génère pas autant de déchets sans rien faire?

+0

"Comment est-ce que je fais que le code ne génère pas autant de déchets sans rien faire?" arrête de faire un autoboxing –

+2

Je ne ferais pas confiance à un "benchmark" slapdash comme celui-ci. Lire la suite: http://code.google.com/p/caliper/wiki/JavaMicrobenchmarks –

Répondre

1

Ceci est similaire à cette extremly question: Will the Java optimizer remove parameter construction for empty method calls?

Comme je l'ai écrit là: le JIT pourrait très bien se rendre compte qu'il ne faut rien faire (un peu moins dans votre cas comme plus impliqués). Apparemment, il ne semble pas le faire.

Ce que je propose est de faire plusieurs méthodes de journaux, d'ordre général avec quelques paramètres de vararg et quelques surchargées qui prennent des entiers et empêchant autoboxing en premier lieu:

Log(Object... arguments) { /* do logging */ } 
Log(Object a, Object b, Object c} { /* special case for 3 objects */ } 
Log(int a, int b, int c} { /* special case for 3 ints */ } 

MISE À JOUR: Mieux encore, voir Péter Török 'ne réinventez pas la roue' réponse ...

4

Vous devriez vraiment préférer un cadre de diagraphie existant comme Log4J au lieu de réinventer la roue. Ces gars-là ont mis plus d'efforts dans l'optimisation de leur cadre de journalisation que vous ne pouvez jamais avoir de façon réaliste. Voir la section "Performance" à la fin de cette section Short introduction to log4j.

De plus, n'essayez pas d'optimiser prématurément. Les chances sont, en mettant un effort sérieux dans l'optimisation des déclarations de notation (ou d'ailleurs, quelle que soit la partie de votre application) sans mesures réelles et la preuve que cela améliore réellement les choses ne va pas payer.

+0

Merci pour votre réponse. Dans le vrai programme, le code que j'écris est un wrapper autour de log4j qui ajoute quelques fonctionnalités. Cependant, je crois comprendre que log4j souffre du même problème. En particulier, la section que vous indiquez, point 1, indique exactement le coût que j'essaie de minimiser. Cela suggère une alternative peu attrayante: jeter un message sur mon code de non-journalisation avec if (isLogging) {doLog(); } construit. J'espère trouver une manière plus propre. En C++, j'utiliserais des templates pour générer automatiquement tout un tas de méthodes de logger qui éviteraient la boxe ... – bsdfish

+0

J'appellerais difficilement using if (log.isDebugEnabled()) "détritus". C'est assez standard, et fait pour une bonne raison. Vous pouvez facilement configurer votre IDE pour qu'il utilise un modèle de code, de sorte que la saisie de quelques séquences de touches génère la ligne de code complète pour vous. –

+1

@bsdfish, oui, c'est vrai. Vous pourriez essayer d'améliorer votre API pour éviter l'autoboxing et aider l'optimiseur JIT, comme suggéré par d'autres. Mais n'allez pas trop loin - la conception de l'API ne doit pas être dirigée par des problèmes d'optimisation des performances. Alors, si vous avez une application où la journalisation est prouvée être un goulot d'étranglement, vous pouvez toujours ajouter ces si laids en dernier recours. –

1

Je pense que vous devriez vous connecter ce que vous devez vous connecter, plutôt que de convertir les entiers en objets.Appeler logObjs() ou log3Obj() pour consigner des entiers n'est qu'une perte de temps - vous allez créer des objets temporaires pour la boxe/unboxing et ils devront être éliminés. Rien que tu puisses faire à ce sujet.

Je soupçonne que la plupart du temps quand vous allez être appeler logObjs(), vous passerez des objets réels à la fonction, dans ce cas, le coût de GC est presque nul, que vous serez tout simplement passer des références à ceux objets dans logObjs() et aucune création ou élimination d'objet n'est requise.

Ecrivez plusieurs fonctions de journalisation qui prennent différentes listes d'arguments en fonction de ce que vous avez l'intention de journaliser à ce moment-là. C'est ce que je fais. Si j'ai besoin d'enregistrer une chaîne et une valeur de 32 bits, j'ai une API de journalisation avec ces paramètres, etc. Pas (ou peu) de création d'objet non-nécessaire/temporaire.

Si vous devez enregistrer 3 entiers, écrivez une API qui prend 3 entiers. Si vous devez enregistrer un objet weeble et un objet wobble et un objet qui ne tombe pas, alors écrivez une API pour cela aussi. Minimisez les effets box/unobx/GC en utilisant les API appropriées pour vos fonctions de journalisation.