Faut-il tester la complexité algorithmique? Si c'est le cas, comment?


14

Supposons que j'implémente quelque chose de simple comme la recherche dans une liste / un tableau trié. La fonction (en c #) ressemblerait à:

static int FindIndex(int[] sortedList, int i);

Je pourrais implémenter et tester cela en termes de fonctionnalités, mais pour des raisons évidentes, je préférerais généralement une recherche binaire à une recherche linéaire ou quelque chose d'intentionnellement stupide.

Ma question est donc la suivante: faut-il tenter d'écrire des tests qui garantissent des performances en termes de complexité algorithmique et si oui, comment?

J'ai commencé à faire des arguments des deux côtés de la partie «devriez-vous» de cette question, mais j'aimerais voir ce que les gens disent sans mes arguments pour les inciter.

En termes de "comment", cela devient très intéressant :) Vous pouvez voir paramétrer l'opérateur de comparaison et avoir un test dont l'opérateur de comparaison compte les comparaisons ou quelque chose comme ça. Mais ce n'est pas parce que vous le pouvez que vous devriez ...

Quelqu'un d'autre a-t-il envisagé cela (probablement)? Merci.


@ steenhulthin - Je vais laisser mijoter ici et vérifier cela. Je n'y étais jamais allé.
SirPentor

btw, belle question. +1
Rafael Colucci

Réponses:


13

La complexité algorithmique est une construction théorique et en tant que telle, elle est mieux «testée» avec un crayon et du papier. Il ne peut pas être utilement testé mécaniquement.

Les performances absolues peuvent être testées mécaniquement et peuvent faire des tests unitaires utiles. Si les performances sont importantes, vous pouvez spécifier un seuil: "cette requête ne devrait pas prendre plus de 50 ms pour s'exécuter sur 10 6 numéros, et pas plus de 60 ms sur 10 7 numéros". Pour lequel vous pouvez créer un test unitaire.

L'utilisateur final ne se soucie pas de savoir si votre algorithme est linéaire ou logarithmique; ils se soucient de savoir si leur interface utilisateur répond toujours instantanément, même s'ils disposent d'un an de données dans l'application.


C'est aussi mon instinct. Mais ce qui m'a fait réfléchir, c'est lorsque les performances sont garanties sur les frameworks. Exemple: si je me souviens bien, le stl a quelques garanties autour de la complexité algorithmique (pourrait être désactivée ici).
SirPentor

Ce n'est pas parce qu'une bibliothèque fournit des garanties qu'elles doivent être testables à l'unité.
svick

@Tobias Brick: Tester quelque chose et définir quelque chose sont deux choses différentes.
DeadMG

Démontrer la performance est difficile. Il implique de nombreux points d'échantillonnage pour différents paramètres. C'est plus facile lorsque les fonctions individuelles sont triviales, mais au-delà de cela ... Votre charge, votre RAM, la vitesse du bus avant, le CPU, le nombre de cœurs, l'agressivité du compilateur, le degré de pollution du registre affecteront tous le temps d'exécution d'un particulier échantillon.
Job

3

Bien que je ne sois pas sûr que cet outil soit particulièrement utile pour les tests unitaires, l'article "Empirical Computational Complexity" par Goldsmith, Aiken et Wilkerson décrit une méthode pour instrumenter le code et observer son comportement dynamique sur un ensemble de diverses entrées à empiriquement dériver sa complexité asymptotique. Le programme décrit dans l'article est open-source et est disponible ici .

J'espère que cela t'aides!


0

L'essentiel est de l'essayer avec des données volumineuses et de voir si cela prend trop de temps.

Dans mon expérience de réglage des performances, comme dans cet exemple , ce qui se passe, c'est que si un algorithme est (par exemple) O (n ^ 2), cela peut être très bien tant que le pourcentage de temps qu'il faut ne parvient jamais au radar.

Cependant, quand on lui donne un ensemble de données d'une taille qui pourrait ne pas être vue mais une fois par an, la fraction du temps total aspiré par cet algorithme peut devenir catastrophiquement dominante.

Si vous pouvez y arriver en test, c'est une très bonne chose, car il est extrêmement facile de trouver le problème, comme s'il s'agissait d'une boucle infinie réelle.


0

Je ne pense pas que ce que vous voulez faire soit des tests unitaires.

AFAIK, les tests unitaires visent uniquement à s'assurer que le code fait ce qu'il doit faire et qu'il ne se concentre pas sur les performances.

D'après Wikipédia : Les tests ne peuvent pas détecter toutes les erreurs du programme: il est impossible d'évaluer chaque chemin d'exécution dans tous les programmes sauf les plus triviaux. Il en va de même pour les tests unitaires. De plus, les tests unitaires par définition ne testent que la fonctionnalité des unités elles-mêmes. Par conséquent, il ne détectera pas les erreurs d'intégration ou les erreurs de niveau système plus larges (telles que les fonctions exécutées sur plusieurs unités ou les zones de test non fonctionnelles telles que les performances). Les tests unitaires doivent être effectués conjointement avec d'autres activités de test de logiciels. Comme toutes les formes de tests de logiciels, les tests unitaires ne peuvent montrer que la présence d'erreurs; ils ne peuvent pas montrer l'absence d'erreurs.

Il existe d'autres types d'outils et de modèles pour mesurer les performances. Je me souviens maintenant de l'un des tests d'acceptation axés sur les exigences non fonctionnelles.

Il y en a peu d'autres comme les tests de performances (qui utilisent des tests de stress, des tests de charge, etc.).

Vous pouvez également utiliser certains outils de stress avec un outil de construction (fourmi, studio de construction automatisé) dans le cadre de vos étapes de déploiement (c'est ce que je fais).

Donc, la réponse courte est non, vous ne devriez pas vous en soucier lorsque vous testez un code à l'unité.


0

Passer le comparateur et garder cette trace du nombre d'appels fonctionnera à des fins simples telles que vérifier que le nombre de comparaisons lors d'une recherche dans une entrée fixe (disons new int[] { 1,2,3, ... , 1024 }) reste inférieur à 10. Cela va au moins expliquez clairement vos intentions sur le comportement de l'algorithme.

Je ne pense pas que les tests unitaires soient la bonne voie à suivre pour vérifier que votre algorithme est O (log n); cela nécessiterait beaucoup de génération de données aléatoires, un ajustement de courbe et probablement des statistiques noueuses pour déterminer si un groupe de points de données correspond à l'exécution attendue. (Pour cet algorithme, c'est probablement faisable, mais si vous commencez à trier, etc., il deviendra difficile de frapper de manière répétée les pires scénarios).

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.