J'ai une table de hachage où les clés sont des listes assez complexes, avec des sublistes de symboles et d'entiers, et la valeur doit être modifiée en fonction de la valeur déjà existante. La table est créée avec Je fais quelque chose de similaire à celui-ci: p> profilage indique que : test # 'égale code>. EQUAL CODE> Les tests prennent beaucoup de temps. J'ai une idée d'optimisation que la quantité de recherche gethash code> pourrait être réduite de deux à une. Cela peut être fait en C ++ en réutilisant l'itérateur, mais je ne sais pas comment cela serait fait dans Lisp. Des idées? P> p>
6 Réponses :
Certaines solutions de contournement peuvent être:
Si le modèle commun est RECHERCHE -> Recherche-it -> écrasé-it, vous pouvez remplacer le type de valeur à une liste contenant le type de valeur. Ensuite, après avoir trouvé l'objet de valeur pour la clé, remplacez simplement son premier élément, par exemple P>
* (defparameter *my-hash* (make-hash-table))
*MY-HASH*
* (setf (gethash :my-key *my-hash*) (list "old-value"))
("old-value")
* (gethash :my-key *my-hash*)
("old-value")
T
* (defparameter old-value-container (gethash :my-key *my-hash*))
OLD-VALUE-CONTAINER
* (setf (first old-value-container) "new value")
"new value"
* (gethash :my-key *my-hash*)
("new value")
T
J'ai essayé quelque chose de similaire au code source que vous avez posté, mais lorsque vous faites la (Setf (première vieille liste) ...), il n'allume que l'ancienne liste et la modification ne se reflète pas dans la valeur de la table de hachage. Suis-je mal comprendre quelque chose de fondamental?
@ Kotlinski: Si vous avez fait que la valeur initiale de l'ancienne liste est nulle, alors oui, cela ne sera pas reflété dans la valeur de la table de hachage. Cependant, si vous avez déjà une liste, alors Gethash renvoie la liste et vous pouvez la muter de la manière dont vous pensez. Remarque, "Push" ne fonctionnera pas car cela affecte la variable que vous appuyez sur, en ajoutant une nouvelle tête et définissez la variable à pointer sur cette nouvelle valeur. Il partagera ensuite une partie de la liste avec la valeur Hashtable (en supposant que cela ne soit pas nul), mais pas la même chose.
"Les structures de données intégrées de LISP communes sont notoirement opaques." -- que veux-tu dire?
Une chose que vous pourriez faire est d'utiliser Devtructeur pour créer une valeur que chaque entrée de votre table de hachage pointe. Votre liste de valeurs (que vous appuyez sur dans votre exemple actuel) pourrait être stockée à l'intérieur. La création de struct pourrait être effectuée dans cet appel Gethash initial (en tant que valeur par défaut) ou pourraient être effectués manuellement si vous observez qu'il n'y a pas de valeur là-bas. Ensuite, l'objet peut être effectué de manière intérieure de la manière dont vous faites. P>
(Cela ignore la question de savoir si vous voulez vraiment utiliser des valeurs aussi complexes telles que vos touches de hashtable, ou s'il y a un moyen de contourner cela. Par exemple, vous pouvez utiliser des structures / des objets rapprochés au lieu de complexes. Liste comme vos clés, puis vous pouvez utiliser une haquetable EQ à la place. Mais cela dépend beaucoup de ce que vous faites.) p>
"Le profilage montre que des tests égaux prennent une longue durée." P>
Oui, mais avez-vous vérifié que # 'Equal Les recherches de table de hasch em> prennent aussi beaucoup de temps? P>
Avez-vous compilé ceci pour une vitesse sur un compilateur d'optimisation comme SBCL et regardé les notes du compilateur? P>
Après avoir résolu ces deux questions, vous pouvez également essayer une table de hachage imbriquée pour chaque niveau de vos clés de liste. Il ne devrait pas être difficile d'écrire une macro pour des tables de hachage imbriquées arbitrairement. P>
Ne faites rien de spécial, car la mise en œuvre le fait pour vous. P>
Bien entendu, cette approche est spécifique à la mise en œuvre et les performances de la table de hachage varient de la mise en œuvre. (Mais ensuite les questions d'optimisation sont toujours spécifiques à la mise en œuvre.) P>
La réponse suivante est pour SBCL. Je recommande de vérifier si les tables de hachage de votre LISP effectuent la même optimisation. Se plaindre à votre fournisseur s'ils ne le font pas! P>
Que se passe-t-il dans SBCL, c'est que la table de hachage met en cache la dernière table indice de table B> accessible par Gehash. P>
Lorsque Puthash (ou équivalent, (Setf Gethash)) est appelé, il vérifie d'abord si la clé de cet indice mis en cache est EQ à la touche que vous passez. P>
Si tel est le cas, toute la routine de recherche de la table de hachage est passée et puhash stocke directement à l'indice mis en cache. P>
Notez que EQ n'est qu'une comparaison de pointeur et donc extrêmement rapide - il n'est pas nécessaire de traverser la liste du tout. P>
Donc, dans votre exemple de code, il n'ya pas de frais généraux du tout. p>
Il semble que nous puissions remercier Paul F. Dietz: git.boinkor.net/gitweb/ sbcl.git / commitdiff / ...
Peut-être que je manque quelque chose d'évident, mais: depuis: p> EDIT: Oups, j'étais: j'ai raté le cas où l'ancien-i est nul. Mais si ce n'est pas le cas commun, alors, cela pourrait toujours être une victoire, car vous n'avez besoin que de la recherche dans ce cas: p> hmm, cela fonctionne-t-il? < / p> p>
Non, ça ne le fait pas. Vous appuyez sur les articles sur le lieu Old-i CODE> Lieu qui n'a aucun effet sur ce qui est stocké dans le (gethash ...) code> place, puisque les listes LISP non vides sont des pointeurs à un nœud de tête et non de conteneurs.
Vous pouvez accéder à la table de hachage trois fois. Pourquoi? Parce que la macro code> push code> peut développer le code qui fait un Dans ce problème, vous inspectez la valeur d'un lieu, qui est une liste. Si cette liste satisfait un test de prédicat, vous appuyez sur quelque chose sur cet endroit. P> Ce problème peut être attaqué en créant un opérateur spécial qui capture cette sémantique: P> gethash code> pour obtenir la liste, puis certains System :: SemeHash CODE> Opération pour stocker la valeur . > (macroexpand '(push-if new test (gethash a b)))
(LET*
((#:VALUES-12736 (MULTIPLE-VALUE-LIST (VALUES A B)))
(#:G12732 (POP #:VALUES-12736)) (#:G12733 (POP #:VALUES-12736)))
(LET ((#:G12735 (GETHASH #:G12732 #:G12733)))
(WHEN (FUNCALL TEST #:G12735) (SETF #:G12734 (CONS NEW #:G12735))
(SYSTEM::PUTHASH #:G12732 #:G12733 #:G12734)))) ;