vendredi 24 avril 2015

Test - The Talos Principle - Aussi loin que j'ai pu

Après 3 mondes complets et 20 étoiles, je jette l'éponge: les 10 dernières étoiles, j'irai chercher sur Internet comment les trouver.

J'avais déjà raconté ici à quel point j'étais impressionné par The Talos Principle après avoir joué à la démo. Je me le suis donc offert, et il est tout à fait à la hauteur de mes attentes. Il est difficile de ne pas le comparer à Portal puisqu'il s'agit d'un jeu de réflexion en 3d à la première personne où il s'agit de résoudre une série de problèmes situés dans des zones bien délimitées en utilisant divers objets tels des cubes pour appuyer sur des boutons ou pour accéder à des éléments en hauteur, des ventilateurs pour s'élever, des redirigeurs de lasers pour actionner des capteurs, ou encore une sorte d'enregistreur qui permet d'interagir avec son soi du passé.

Le principe est de récupérer dans chaque zone un sigil, c'est à dire un tetromino, qui, assemblés, permettent d'ouvrir de nouvelles zones. Il y a également 30 étoiles plus ou moins cachées, qui donneront parfois beaucoup de fil à retordre. L'idée des étoiles est excellente: autant récupérer un tetromino requiert d'assembler correctement les éléments disponibles, autant récupérer les étoiles demande à réfléchir un peu à l'envers: il s'agit souvent de subvertir le système, en assemblant les éléments de plusieurs zones, à distance, ou encore en s'arrangeant pour faire s'échapper des éléments de certaines zones autrement que par la porte, qui les bloque. Il y a donc vraiment deux familles de problèmes, et passer de l'un à l'autre est agréable.

L'autre bonne idée est que de nombreuses zones sont disponibles dès le début, il est ainsi possible de laisser un problème de côté temporairement et d'aller en résoudre d'autres.

Dans chaque monde, des terminaux, des enregistrements audio et autres informations permettent de comprendre petit à petit ce qu'est ce monde et ce que l'on y fait. Les thèmes abordés, la conscience, le libre arbitre, l'humanité, sont intéressants et s'intègrent complètement au jeu.

Au niveau des défauts, de nombreux joueurs se plaignent des trop nombreux puzzles de tétrominos à résoudre. Sans qu'ils apportent grand chose, cela ne m'a pas gêné outre mesure. J'ai trouvé la chasse aux étoiles un peu frustrante, étonnamment ce sont les étoiles du niveau 3, là où les problèmes sont les plus compliqués, où les étoiles sont finalement les plus faciles à obtenir: j'ai réussi à acquérir les 10 étoiles, alors qu'un coup d'oeil aux soluces m'ac confirmé que je n'aurais sans doute jamais trouvé mes étoiles manquantes du niveau 1 et 2.

The Talos Principle est également le premier jeu AAA auquel j'ai joué exclusivement sous Linux. Manifestement, les drivers libres ATI ne sont pas totalement à la hauteur, et j'ai donc souffert de bugs graphiques, qui dans mon cas n'ont pas été particulièrement gênants. Apparemment, les drivers propriétaires seraient pleinement supportés (mais il y a bien longtemps qu'AMD ne fournit plus les drivers proprios pour mon matériel, vive le libre!).

samedi 11 avril 2015

Boost - Temps de compilation par en-tête

La bibliothèque boost est LA bibliothèque du C++ (après la bibliothèque standard, bien entendu!), bourrée d'utilitaires utiles pour fournir une base solide à son développement. Utilisée dans nombre de projets C++, un reproche fréquent est que la bestiole prend de l'embonpoint, et que ses en-têtes à ralonge plombent rapidement les temps de compilation d'un projet.

J'ai donc écrit un petit script bash qui me compile chaque en-tête l'une après l'autre et me renvoie le temps qu'il a fallu pour compiler. J'utilise g++ 4.9.2, en mode C++11 et avec l'option d'optimisation -O3. Me voilà avec la liste complète des en-têtes les plus couteuses parmi celles disponibles directement sous le répertoire "boost" et "boost/algorithm". Pas vraiment de surprises, mais la confirmation qu'il faut faire un peu attention à ses inclusions.

5.41boost/geometry.hpp
4.73boost/phoenix.hpp
3.86boost/wave.hpp
3.77boost/thread.hpp
3.24boost/random.hpp
2.92boost/signals2.hpp
2.68boost/algorithm/string_regex.hpp
2.65boost/date_time.hpp
2.57boost/program_options.hpp
2.24boost/bimap.hpp
2.21boost/flyweight.hpp
2.18boost/algorithm/string.hpp
2.15boost/algorithm/hex.hpp
2.05boost/exception_ptr.hpp
1.99boost/local_function.hpp
1.86boost/lexical_cast.hpp
1.85boost/asio.hpp
1.81boost/multi_array.hpp
1.71boost/locale.hpp
1.68boost/multi_index_container.hpp
1.68boost/assign.hpp
1.61boost/variant.hpp
1.59boost/regex.hpp
1.50boost/unordered_map.hpp
1.48boost/chrono.hpp
1.46boost/circular_buffer.hpp
1.45boost/unordered_set.hpp
1.45boost/scope_exit.hpp
1.45boost/function.hpp
1.45boost/filesystem.hpp
1.41boost/format.hpp
1.39boost/range.hpp
1.26boost/make_shared.hpp
1.24boost/optional.hpp
1.22boost/smart_ptr.hpp
1.16boost/shared_container_iterator.hpp
1.10boost/tokenizer.hpp
1.08boost/token_iterator.hpp
1.08boost/algorithm/gather.hpp
1.02boost/dynamic_bitset.hpp
1.01boost/weak_ptr.hpp
1.00boost/shared_ptr.hpp
1.00boost/enable_shared_from_this.hpp
0.99boost/shared_array.hpp
0.88boost/array.hpp
0.84boost/compressed_pair.hpp
0.82boost/any.hpp
0.80boost/algorithm/minmax.hpp
0.79boost/swap.hpp
0.68boost/type_traits.hpp
0.60boost/parameter.hpp
0.58boost/multi_index_container_fwd.hpp
0.58boost/concept_check.hpp
0.45boost/cast.hpp
0.43boost/foreach.hpp
0.41boost/iterator_adaptors.hpp
0.41boost/generator_iterator.hpp
0.38boost/ratio.hpp
0.35boost/scoped_ptr.hpp
0.35boost/rational.hpp
0.35boost/concept_archetype.hpp
0.35boost/algorithm/clamp.hpp
0.33boost/bind.hpp
0.32boost/progress.hpp
0.32boost/intrusive_ptr.hpp
0.31boost/circular_buffer_fwd.hpp
0.29boost/aligned_storage.hpp
0.28boost/utility.hpp
0.28boost/pointee.hpp
0.28boost/mem_fn.hpp
0.28boost/indirect_reference.hpp
0.27boost/operators.hpp
0.27boost/functional.hpp
0.26boost/strong_typedef.hpp
0.26boost/scoped_array.hpp
0.26boost/get_pointer.hpp
0.25boost/iterator.hpp
0.25boost/dynamic_bitset_fwd.hpp
0.24boost/next_prior.hpp
0.24boost/function_output_iterator.hpp
0.24boost/assert.hpp
0.22boost/cregex.hpp
0.15boost/nondet_random.hpp
0.10boost/blank.hpp
0.08boost/crc.hpp
0.08boost/call_traits.hpp
0.08boost/atomic.hpp
0.07boost/integer_traits.hpp
0.07boost/integer.hpp
0.06boost/timer.hpp
0.06boost/regex_fwd.hpp
0.06boost/limits.hpp
0.06boost/integer_fwd.hpp
0.06boost/implicit_cast.hpp
0.05boost/throw_exception.hpp
0.05boost/ref.hpp
0.05boost/last_value.hpp
0.04boost/visit_each.hpp
0.04boost/static_assert.hpp
0.04boost/preprocessor.hpp
0.04boost/noncopyable.hpp
0.04boost/math_fwd.hpp
0.04boost/cstdint.hpp
0.04boost/config.hpp
0.02boost/io_fwd.hpp
0.02boost/cstdlib.hpp
0.02boost/algorithm/minmax_element.hpp
0.01boost/version.hpp
0.01boost/type.hpp
0.01boost/pointer_cast.hpp
0.01boost/non_type.hpp
0.01boost/none_t.hpp
0.01boost/none.hpp
0.01boost/memory_order.hpp
0.01boost/is_placeholder.hpp
0.01boost/current_function.hpp
0.01boost/checked_delete.hpp
0.01boost/cerrno.hpp
0.01boost/blank_fwd.hpp
0.00boost/pointer_to_other.hpp
0.00boost/function_equal.hpp
0.00boost/foreach_fwd.hpp

mardi 24 mars 2015

Boost - And the winner is...

<boost/date_time/posix_time/ptime.hpp>! Ce gros fichier mange quand même 0.9 secondes de pré-compilation. Pas très loin derrière, il y a <boost/foreach.hpp>, avantageusement remplacé par le range-based for de C++11, et <boost/regex.hpp>, qui sera plus difficile à remplacer car non encore implémenté dans g++ 4.8 (je crois que c'est dans 4.9, mais je n'ai pas encore testé).

Tout cela est quand même rudement non-scientifique. Je prévois donc, lorsque j'aurai un moment, d'écrire un petit script qui me pré-compilera chaque en-tête boost une par une, pour trouver les plus coûteux.

mardi 17 mars 2015

Temps de (pré) compilation et boost::lexical_cast

Je garde un œil vigilant sur mes temps de compilation. En particulier, pour optimiser la compilation distribuée par distcc (je n'arrive pas à faire fonctionner le mode pump avec ma solution), il faut diminuer autant que faire ce peut le temps de pré-compilation, c'est à dire la phase où le compilateur va remplacer les directives #include par leur contenu, et évaluer les #if, #ifdef et autres. En effet, distcc agrège les différents en-têtes localement (car tous les fichiers sont disponibles), et distribue le (gros) fichier ainsi généré pour la phase de compilation proprement dite. L'édition de liens se fait également localement.

Or, je me suis vite rendu compte que la phase de pré-compilation prenait un temps considérable. Jusqu'à 30 secondes par fichier! Le client local passait donc sa vie à pré-compiler, alors que les CPUs distants se tournaient les pouces. Au final, la compilation entièrement locale, c'est à dire sans distcc, prenait à peu près autant de temps, car le compilateur peut optimiser la phase de pré-compilation et la phase de compilation lorsqu'elles sont faites ensemble.

Je n'ai pas encore résolu le problème plus général, mais en fouillant pour comprendre ce qui prenait autant de temps, j'ai découvert qu'un fichier d'en-tête était particulièrement coûteux: <boost/lexical_cast.hpp>.

Testez ce programme tout bête:

// lex.cpp
#include <boost/lexical_cast.hpp>

Compilez le:

time g++ -E lex.cpp -o lex.E

Sur ma machine, c'est 0.7 secondes passées juste à pré-compiler le fichier! Par comparaison, si je remplace l'inclusion par l'inclusion de l'en-tête standard pour les chaines de caractères, <string>, la phase de pré-compilation prend juste 0.025s. Alors, 0.7 secondes, ce n'est pas énorme, mais multiplié par des dizaines de milliers de fichiers, c'est non-négligeable. À noter que le temps a triplé entre boost 1.42 et boost 1.55. Cela se voit également à la taille du fichier ainsi généré: 20KLOC pour boost 1.42 contre 60KLOC pour boost 1.55.

Pour tenter de récupérer ce temps, il faut donc éliminer le plus possible les inclusions de boost::lexical_cast dans les en-têtes. Pour ce faire, il convient de remplacer les utilisations triviales par leurs équivalents moins flexibles mais souvent plus performants, et nettement plus rapides à compiler: std::to_string pour transformer les nombres en chaines, et std::stoi, std::stol, std::stod et autres pour transformer les chaines en nombres.

Pour les utilisations non-triviales, il faudra soit écrire un peu de code template pour remplacer le boost::lexical_cast par un équivalent plus simple, soit le pousser, si possible, dans le .cpp, où il n'embêtera personne d'autre.

Une fois débarassé de mes lexical_cast, je partirai à la recherche des autres mammouths, et je vous tiens au jus.

mercredi 11 mars 2015

Clang - Prémices

Je me suis mis à compiler une grosse solution C++ sous clang, le compilateur C/C++ du projet llvm, avec pour but principal de trouver des erreurs potentielles dans le code que gcc aurait laissé passer. Eh beh, c'est sport!

La partie qui m'a posé souci a été de pointer clang vers la bonne libstdc++, car mon setup était un peu bizarre. Le compilo de base sur la machine était trop vieux, et j'avais donc un g++ récent dans un répertoire non standard, que clang ne trouvait pas.

J'ai d'abord essayé d'utiliser la libc++ développée au sein du projet llvm, et me suis retrouvé avec des crash cyclopéens, potentiellement à cause de fonctionnalités manquantes dans la bibliothèque. Finalement, en jouant avec l'option "--with-gcc-toolchain", j'ai réussi à le faire pointer vers ma libstdc++ récente, et tout s'est beaucoup mieux passé.

La bonne nouvelle est qu'il n'y a presque rien à changer en termes d'options de compilation, c'est pareil que gcc. L'autre bonne nouvelle est qu'en effet, les warnings levés par clang sont tout à fait intéressants, et trouvent de vrais bugs, dont je vous parlerait dans un prochain post!

vendredi 30 janvier 2015

Bonne année!

Les stats ne sont pas belles, le nombre de posts de 2014 a encore diminué par rapport à l'année précédente... J'espère que je maintiens au moins en qualité ce que je n'arrive pas à fournir en quantité. J'ai manqué d'inspiration, pendant un temps, mais là, j'ai plein de choses à raconter. En particulier, il va falloir que je mentionne les frameworks web orientés composants, et en particulier Vaadin, en Java, et Wt, en C++. Faisant plutôt pas mal de Web, ces temps-ci, je trouve ces outils particulièrement adaptés à mes usages, en me permettant des pages au look moderne sans avoir à écrire une seule ligne de HTML ou de Javascript, dans un langage que je peux facilement interfacer avec le reste du monde.

samedi 29 novembre 2014

Test - Only If

L'on se réveille après une fête, dans une maison inconnue, sans se souvenir de rien. Partant à la découverte de la maison, l'on se retrouve dans une série d'endroits complètement surréalistes, avec pour guide un psychopathe nommé Vinny qui nous cause via des radios et qui n'arrête pas de vouloir nous tuer. Savant mix entre Dear Esther pour les décors et la faible interactivité, Portal pour la présence du guide fou, avec un petit air d'Alone In the Dark pour l'atmosphère mystérieuse, Only If est disponible gratuitement sur Steam, et vaut tout à fait le détour.

Le jeu n'est pas exempt de défauts: la difficulté est très mal dosée, et certaines séquences se passent sans presque réfléchir alors que d'autres sont ridiculement difficiles, principalement par manque d'indications sur ce que l'on est censé faire. Ainsi, il est possible de mourir de multiples fois (et de recommencer le niveau) sans vraiment avoir compris là où l'on s'est trompé. Cette approche très "old school" peut s'avérer rapidement lassante, gratuit ou pas. De plus, le scénario m'a plutôt laissé sur ma faim. J'ai trouvé l'explication finale franchement faiblarde (heureusement que quelqu'un sur Youtube a eu la bonté de rejouer la scène en ajoutant les sous-titres, parce que je n'avais vraiment pas compris grand chose au premier abord), et il n'y a pas vraiment de progression à travers les niveaux: l'on va de délire en délire, et à la fin tout est expliqué d'un coup.

Mais le soft tire son épingle du jeu via l'atmosphère tout à fait intéressante et les niveaux délirants, entre réalisme, rêve et cauchemar. Les décors sont de toute beauté, et il est agréable simplement de s'y promener. La logique, bien que franchement tordue, se tient généralement. Manquant de temps et de patience, je me suis parfois laissé à regarder les soluces, mais souvent les solutions étaient de fait trouvables, à condition de se creuser un peu les méninges.

Ah, et ça tourne sous Linux! Beaucoup de bugs graphiques, cependant, dus sans doute une fois de plus à ma config: une vieille ATI tournant avec les pilotes libres. Ça ne m'a pas empêché d'apprécier!