7
votes

6 Réponses :


1
votes

Mon hypothèse est que dans la version optimisée, car la référence à la fonction est stockée dans une variable locale, une traversée d'arbre ne doit pas être effectuée sur chaque itération de la boucle de la boucle (pour la recherche de Math .sin ).

Je ne suis pas sûr des références locales définies sur les noms de fonction, mais je suppose qu'il y a une sorte de recherche d'espace de noms global requis si une locale n'est pas trouvée.

Encore une fois, je pourrais être complètement hors base;)

Edit: Je suppose également que le compilateur Lua est idiot (qui est une hypothèse générale pour moi sur les compilateurs de toute façon;))


0 commentaires

3
votes

stockage des fonctions dans des variables locales supprime l'indexation de la table pour rechercher la touche de fonction Chaque itération de la boucle, les mathématiques sont évidentes, car elle doit rechercher le hachage dans la table mathématique, les autres ne sont pas, ils sont indexé dans le _g (tableau global), qui est maintenant _env (table d'environnement) à partir de 5.2.

En outre, il convient de pouvoir profiler Lua à l'aide de son API de crochets de débogage ou en utilisant les debuggers Lua allongé.


1 commentaires

Vous voulez dire indexation de la table. Les mathématiques, _g, ... ne sont que des tables normales.



11
votes

La raison pour laquelle il n'est pas fait par défaut, je ne sais pas. Pourquoi c'est plus rapide cependant, car les habitants sont écrits dans un registre, tandis qu'un moyen global la regardant dans une table (_g), qui est connu pour être un peu plus lent.

Quant à la visibilité (comme avec la fonction de format): un local obscurcisse le global. Donc, si vous déclarez une fonction locale avec le même nom qu'un global, la section locale sera utilisée à la place tant qu'elle est en portée. Si vous souhaitez utiliser la fonction globale à la place, utilisez _g.function.

Si vous voulez vraiment rapide Lua, vous pouvez essayer Luajit


5 commentaires

Je suppose que c'est ma question: pourquoi le compilateur ne l'écrivait pas à un registre? Si c'est toujours plus rapide, le compilateur devrait rechercher une fois le global, puis l'écrire à un registre. Ce n'est pas comme si la fonction peut changer lorsque ma fonction est en cours d'exécution.


@Ian Boyd, à Lua, la signification de math.sin peut changer changer lorsque votre fonction est en cours d'exécution. Dans certains cas, cette capacité est précieuse. Le cas spécifique de fonctions de bibliothèque bien connues est un cas où il ne serait pas nécessairement aussi précieux, mais il est toujours possible, le compilateur doit donc respecter le code que vous avez réellement écrit.


Un changement d'exécution de math.sin que pourrait être utile est de prétendre que le singe-patch elle avec une emballage de mémoisation. Cela ne serait toujours pas quelque chose que vous feriez pendant une boucle, mais c'est toujours un changement à la variable globale nommée math.sin qui se produirait au moment de l'exécution.


@Rberteig Mais il n'y a aucun moyen de la valeur math.sin pourrait changer pendant ma boucle ou ma fonction, fonctionne droite? Le seul moyen, je présume que cela pourrait changer est si i le changa - mais le compilateur sait que je l'ai changé, car c'est le compilateur. L'autre possibilité est si un autre thread a décidé de modifier l'état du système mondial sans aucune garantie - ce qui donne un comportement non défini de toute façon - Lua est donc libre d'effectuer quelles que soient les optimisations internes qu'il souhaite - car elle ne doit faire aucune promesse.


@Ian Boyd, la fonction stockée dans la variable globale nommée math.sin pourrait changer cette variable elle-même lorsqu'elle est appelée. La version stock fournie avec Lua ne fait pas cela, mais il pourrait . Le compilateur doit supposer que, comme cela est permis (et, rarement, même utile). Une chose délicate à s'habituer à Lua est que les fonctions sont vraiment stockées dans des variables.



35
votes

Pourquoi cela serait-il utile? C'est le travail du compilateur de le faire quand même. Pourquoi le programmeur doit-il avoir à faire le travail du compilateur?

Lua est une langue dynamique. Les compilateurs peuvent faire beaucoup de raisonnement dans des langues statiques, comme tirer des expressions constantes hors de la boucle. Dans les langues dynamiques, la situation est un peu différente.

La structure de données principale de Lua (et seulement) est la table. maths est également juste une table, même s'il est utilisé comme espace de noms ici. Personne ne peut vous empêcher de modifier la fonction math.sin quelque part dans la boucle (même pensé que ce serait une chose imprudente à faire), et le compilateur ne peut pas savoir que lors de la compilation du code. Par conséquent, le compilateur fait exactement ce que vous l'informez de faire: dans chaque itération de la boucle, recherchez la fonction sin dans la table math et appelez-la.

Maintenant, si vous savez que vous n'allez pas modifier math.sin (c'est-à-dire que vous allez appeler la même fonction), vous pouvez l'enregistrer dans une variable locale en dehors de la boucle. Parce qu'il n'y a pas de recherche de table, le code résultant est plus rapide.

La situation est un peu différente avec Luajit - il utilise le traçage et une magie avancée pour voir ce que votre code fait dans l'exécution , il peut donc optimiser la boucle en déplaçant l'expression en dehors de la Boucle et d'autres optimisations, à part en réellement la compilant au code de la machine, rendant folle rapide.

En ce qui concerne les "variables redéclairant comme local" - plusieurs fois lors de la définition d'un module, vous souhaitez travailler avec la fonction d'origine. Lorsque vous accédez à paires , max ou quoi que ce soit en utilisant leurs variables globales, personne ne peut vous assurer que ce sera la même fonction chaque appel. Par exemple, stdlib redéfinit de nombreuses fonctions globales.

En créant une variable locale avec le même nom que le global, vous stockez essentiellement la fonction dans une variable locale, et parce que les variables locales (qui sont lexiquement scopées, ce qui signifie qu'elles sont également visibles dans la portée actuelle et toutes les étendues imbriquées. Amener la priorité avant les globaux, vous vous assurez de toujours appeler la même fonction. Si quelqu'un doit modifier le global plus tard, cela n'affectera pas votre module. Sans parler de cela est également plus rapide, car les globaux sont levés dans une table globale ( _g ).

Mise à jour : Je viens de lire Conseils de performance Lua par Roberto Ierusalimschy, l'un des auteurs de Lua, et il explique à peu près tout ce que vous devez savoir sur Lua, performance et optimisation. IMO Les règles les plus importantes sont:

règle n ° 1 : ne le faites pas.

règle n ° 2 : ne le fais pas encore. (Pour des experts uniquement)


2 commentaires

Cela n'explique toujours pas pourquoi le compilateur ne peut pas le comprendre - mais les gens semblent se sentir assez fortement à ce sujet. Donc, je suppose que je dois l'accepter.


Le compilateur ne peut pas le comprendre, car le code ne reste pas à Lua tout le temps - vous pouvez appeler les fonctions C enregistrées de LUA, qui peuvent à leur tour modifier l'environnement LUA. Ces fonctions proviennent généralement de modules (bibliothèques partagées). Vous attendez-vous vraiment au compilateur Lua (qui est tout à fait à 200 kb) pour décompiler les bibliothèques pour "comprendre"?



9
votes

Je le vois à plusieurs reprises dans l'environnement Lua que je joue dans; Les personnes redécligant des variables de redécration comme locale:

Faire ce fait par défaut est un problème clair.

Il est sans doute utile d'utiliser des références locales au lieu des accès de table lorsqu'une fonction est utilisée encore et encore, comme dans votre exemple de boucle: XXX

Cependant, les boucles extérieures, la surcharge d'ajout d'un accès de table est complètement négligeable.

Que se passe-t-il ici que les gens doivent faire le travail du compilateur?

Parce que les deux échantillons de code que vous avez faits ci-dessus ne signifient pas exactement la même chose.

Ce n'est pas comme si la fonction peut changer lorsque ma fonction est en marche.

Lua est une langue très dynamique et vous ne pouvez pas faire les mêmes hypothèses que dans d'autres langues plus restrictives, telles que C. La fonction peut changer lorsque votre boucle est en cours d'exécution. Compte tenu de la nature dynamique de la langue, le compilateur ne peut supposer que la fonction ne changera pas. Ou du moins pas sans analyse complexe de votre code et ses ramifications.

L'astuce est que, même si vos deux pièces de code semblent équivalents, à Lua, ils ne le sont pas. Sur le premier, vous lui indiquez explicitement de "obtenir la fonction de péché à l'intérieur de la table mathématique de chaque itération". Sur la seconde, vous utilisez une seule référence à la même fonction encore et encore.

considère ceci: xxx


0 commentaires

1
votes

Ce n'est pas simplement un bug / une fonctionnalité de Lua , de nombreuses langues, y compris Java et C effectuera plus vite si vous accédez aux valeurs locales. Au lieu de valeurs hors de portée, par exemple à partir d'une classe ou d'une matrice.

in C ++ par exemple, il est plus rapide d'accéder à un membre local qu'il ne serait d'accéder aux membres variables de certaines catégories. < / p>

Cela comptait à 10 000 plus rapidement: xxx

que: xxx

la raison LUA détient des valeurs globales à l'intérieur d'une table est qu'elle permet au programmeur de sauvegarder et de modifier rapidement l'environnement global simplement en modifiant le tableau des références. Je conviens que ce serait bien d'avoir un "sucre syntatique" qui a traité la table mondiale _g comme cas particulier; Réécrivez-les tous en tant que variables locales dans la portée du fichier (ou quelque chose de similaire), bien sûr, il n'y a pas rien d'empêcher de faire cela nous-mêmes; Peut-être une fonction optglobalenv (...) qui "localise" la table _g et ses membres / valeurs à la "portée du fichier" à l'aide de Débat () ou de quelque chose.


0 commentaires