mercredi 27 mai 2015

Ubuntu Touch - Premières impressions

Voilà, enfin, j'ai mon nouvel Ubuntu Touch (un BQ Aquaris 4.5 Ubuntu Edition) dans les mains! Bon, je ne peux pas encore téléphoner avec parce que je n'ai pas reçu ma nouvelle SIM, mais les débuts sont prometteurs. En particulier, l'application "terminal" qui m'a permis de faire un "df -h" (sur un téléphone!!!) m'a rendu tout fou. Je sens qu'il va y avoir beaucoup de bidouillage à faire, mais c'est justement ça qui me plait.

dimanche 17 mai 2015

Il est la!

g++-5 -O3 -flto hello.cpp -o hello -fuse-ld=gold

Alors certes, il va falloir que j'écrive un programme un poil plus intéressant qu'un bête Hello World, mais c'est un début. En particulier, je veux tester le changement d'optimisation des chaînes de caractères de COW à SSO. En faisant lire des listes de chaines de plus en plus grandes, l'on devrait observer un seuil (à 24 caractères, si j'en crois Stack Overflow) où l'on commence à devoir faire des allocations. Nos amis std::chrono et Valgrind confirmeront.

lundi 4 mai 2015

Vivement gcc 5.1

Les petits gars de Debian sont en train de bosser d'arrache pied sur le paquet gcc 5.1. Déjà disponible en experimental, il ne devrait pas tarder à atterrir en Testing, ce qui va me donner plein d'occasions pour poster, car il va y en avoir à expérimenter. En particulier, le support de C++14 sera complet, et l'on commencera à voir des fonctionnalités de c++17 (encore appelé c++1z par les pessimistes).

Supporté depuis un moment déjà, mais amélioré à chaque release, c'est la "link time optimization" avec laquelle il va me falloir sérieusement expérimenter. Pour faire simple, au lieu de générer du code machine lors de la transformation de chaque fichier source en fichier objet (.o), gcc génère un fichier au format "Gimple", qui est la représentation intermédiaire du code. Lors de la phase d'éditions de liens, il y a donc beaucoup plus d'informations disponibles pour permettre une optimisation entre les modules, par exemple passer certaines fonctions inline bien que leur corps soit dans le fichier source plutôt que l'en-tête. La contrepartie, c'est que cette phase d'édition de lien peut prendre un temps considérable. C'est là qu'intervient le Gold Linker, avec son mode incrémental et son support du parallélisme.

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.