samedi 19 septembre 2015

std::make_shared et allocations

J'avais rapidement et indirectement parlé de std::make_shared ici, et en particulier l'optimisation qui consiste à allouer la structure de contrôle avec l'objet lui même, économisant ainsi une allocation. Prouvons donc que la bibliothèque C++ standard fournie avec g++ le fait effectivement. Prenons un programme qui alloue 100 pointeurs partagés:

#include <memory>
#include <vector>

int main()
{
  std::vector<std::shared_ptr<int> > v;

  for(int i = 0; i < 100; ++i)
  {
    //v.push_back(std::make_shared<int>(5));
    v.push_back(std::shared_ptr<int>(new int(5)));
  }
  
  return 0;
}

Faisons tourner la chose avec valgrind. Quand la première ligne à l'intérieur de la boucle est commentée, c'est à dire que l'on alloue un entier, puis qu'on le passe à un pointeur partagé, valgrind nous indique ceci:

==4484== HEAP SUMMARY:
==4484==     in use at exit: 72,704 bytes in 1 blocks
==4484==   total heap usage: 209 allocs, 208 frees, 79,584 bytes allocated

Le chiffre important, ici, c'est les 209 allocations: 100 allocations d'entiers, 100 allocations de structure de contrôle, et j'aurais envie de dire 8 allocations pour le vecteur et 1 allocation en dehors de notre contrôle. Maintenant, commentons la deuxième ligne et décommentons la première. Valgrind nous dit alors:

==4491== HEAP SUMMARY:
==4491==     in use at exit: 72,704 bytes in 1 blocks
==4491==   total heap usage: 109 allocs, 108 frees, 79,184 bytes allocated

Yay! Nous avons effectivement une seule allocation dans la boucle au lieu de 2, le make_shared a donc bien alloué la structure de contrôle avec l'objet. À noter que la taille totale allouée est légèrement plus basse, peut-être grâce à des histoires d'alignement?

vendredi 21 août 2015

Gcc et link time optimisation

Comme promis, je vous parle un peu de cette récente fonctionnalité de gcc, qui permet au compilateur d'optimiser à travers les unités de compilation. Le compilateur de Microsoft avait cette fonctionnalité depuis bien longtemps, et gcc était donc un petit peu à la traine, mais à chaque release, de nouvelles améliorations sont apportées à ce qui pourrait bien donner un sérieux coup de fouet aux performances (à l'exécution, parce qu'en revanche, les temps de compilation vont exploser...).

L'idée est que plutôt que de compiler chaque unité de compilation vers du code machine, le compilo s'arrête à une représentation moins bas niveau. C'est cette représentation qui est passée à l'éditeur de liens, qui peut alors refaire une passe d'optimisation en ayant l'ensemble des unités de compilation disponibles. La plus évidente des optimisations possibles est d'inliner les fonctions d'une unité de compilation vers une autre unité de compilation.

Pour comprendre ce qui se passe, je vous propose deux programmes. Voici le premier :

// p1.c
int f(int a, int b)
{
  return a + b;
}

int main()
{
  return f(3, 5);
}

Voici le deuxième, divisé en 2 fichiers

// p2.c
int f(int a, int b);

int main()
{
  return f(3, 5);
}

// p2l.c
int f(int a, int b)
{
  return a + b;
}
Compilons le premier programme avec l'optimisation à fond : "gcc -O3 p1.c -o p1". Puis désassemblons le programme avec "objdump -d -S p1". La partie intéressante est :
00000000004003c0 <main>:
  4003c0:       b8 08 00 00 00          mov    $0x8,%eax
  4003c5:       c3                      retq   

Sans surprises, le compilo a bien inliné le tout, et renvoie simplement la valeur calculée 8.

Maintenant, le deuxième programme: "g++ -O3 p2.c p2l.c -o p2". Le code désassemblé montre bien le passage des deux paramètres 5 et 3 suivi d'un appel de fonction, puisque la fonction "main" ne peut pas voir le contenu de la fonction f.

0000000000400480 <main>:
  400480:       be 05 00 00 00          mov    $0x5,%esi
  400485:       bf 03 00 00 00          mov    $0x3,%edi
  40048a:       e9 01 01 00 00          jmpq   400590 <_Z1fii>

0000000000400590 <_Z1fii>:
  400590:       8d 04 37                lea    (%rdi,%rsi,1),%eax
  400593:       c3                      retq   
  400594:       66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
  40059b:       00 00 00 
  40059e:       66 90                   xchg   %ax,%ax

Et maintenant, voyons avec la lto: g++ -flto -O3 p2.c p2l.c -o p2_lto. Ah ah! Nous retrouvons quelque chose d'identique au tout premier programme. Le compilateur a en effet été capable d'inliner la fonction f dans main, quand bien même sa définition n'était pas accessible. Ça marche!

0000000000400480 <main>:
  400480:       b8 08 00 00 00          mov    $0x8,%eax
  400485:       c3                      retq   

mardi 4 août 2015

Ubuntu Phone - Top des applications

Bon, des applications sur Ubuntu Phone, y'en a pas tellement. Raison de plus pour savoir quelles sont celles qui sortent du lot. Voici mon top 5, tous logiciels libres:

Shorts

Jusqu'à présent, je ne me servais pas d'un aggrégateur de flux RSS. Mais, sur un téléphone, lire des articles sur un navigateur est assez pénible, surtout quand la bestiole n'est pas très véloce. Je m'y suis donc mis, et ça change la vie. Shorts permet de chercher des flux RSS et de les catégoriser, et affiche les articles dans une interface claire et efficace. Tout est mis en cache lors du rafraichissement, ce qui permet de se garder des choses à lire pour les endroits où il n'y a pas de réseau. J’agrège donc LinuxFR, Portail Aviation, l'Odieux Connard et Xkcd.

Dekko

Dekko est une très bonne application de courrier électronique, qui me permet de garder un œil sur mes boites en IMAP. C'est assez véloce, et ça marche vraiment très bien. Un petit bug m'empêchait de faire fonctionner l'IMAP sur une boite OVH, mais les forums expliquent quel paramètre changer dans quel fichier de configuration pour que tout tourne sans histoires.

OSMTouch

OSMTouch, c'est l'application qui permet d'accéder aux cartes d'OpenStreetMap. C'est une application native plutôt qu'un conteneur Web, et c'est redoutablement efficace. Attention cependant, ne fonctionne qu'en mode connecté et ne permet pas de mettre des cartes en cache. À noter que je n'ai pas réussi à faire fonctionner le GPS sur mon BQ Aquarius 4.5, mais les autres applications n'ont pas l'air de le voir non plus, donc ce n'est pas la faute d'OSMTouch

Twitter

Alors d'accord, ce n'est qu'un conteneur Web, et la qualité de l'application est plus un testament du talent des développeurs Twitter. Mais il est bon à savoir que ça marche particulièrement bien sur Ubuntu Touch. À noter que je ne suis que consommateur de Tweets, je n'ai donc aucune idée s'il est aisé d'en écrire via cette interface.

2048

C'est un petit jeu qui rappelle vaguement le taquin, dans le sens où il faut pousser des carrés sur une grille. Mais le principe de 2048 est de combiner les carrés de même valeur pour la faire doubler. Donc combiner deux 2 devient 4, deux 4 devient 8, et ainsi de suite, jusqu'au mythique 2048. Rudement addictif, et raisonnablement stratégique.

mercredi 15 juillet 2015

The Talos Principle - Fini!

Il m'a quand même fallu 27 heures pour compléter le jeu, et voir une de ses fins (à priori, la plus intéressante) en ayant résolu tous les problèmes. Pfiou! À noter que j'ai quand même raté beaucoup d'Easter Eggs, lesquels sont recensés dans de nombreuses vidéos sur Youtube. Je demeure très impressionné par l'intelligence de ce jeu, aussi bien dans le gameplay que dans l'histoire. Une très bonne expérience, entièrement sous Linux!

Je vais donc pouvoir revenir à une activité normale, comme on dit. En particulier, je n'ai vraiment plus qu'à trouver 3 heures de libre d'un seul tenant pour enfin charger mes vieux SMS dans mon Ubuntu Phone. Faut pas se laisser aller.

jeudi 18 juin 2015

Ubuntu Touch - Transférer ses SMS - Phase 1: reconnaissance (suite)

Reprenons, et regardons de plus près les deux tables restantes, thread_participants et text_events.

thread_participants

ChampTypeCommentaire
accountIdchaîneClé vers la table "threads"
threadIdchaîneClé vers la table "threads"
typeentierClé vers la table "threads"
participantIdchaîneLe nom du participant à ce fil de discussions. Pour les SMS simples, c'est simplement notre correspondant. De ce que je vois des données, utiliser la même valeur que le threadId fonctionne

text_events

ChampTypeCommentaire
accountIdchaîneClé vers la table "threads"
threadIdchaîneClé vers la table "threads"
eventIdchaîneProbablement un identifiant unique pour le message. Dans mes données, c'est à une valeur qui ressemble à "/ril_0/message_" suivi d'une chaîne qui ressemble furieusement à un hash pour les messages envoyés, et à une date au format ISO (par exemple 2015-06-01T22:35:52+0100-1) pour les messages reçus
senderIdchaîneL'envoyeur, à "self" pour les messages envoyés, et au numéro de tel de l'envoyeur pour les messages reçus
timestampdatetimeLa date et l'heure auxquelles le message a été reçu
newEventbooléenAucune idée. Toujours à faux dans mes données.
messagechaîneLe message proprement dit
messageTypeentierLe type de message, manifestement toujours à 0
messageStatusentierManifestement là pour indiquer si le message a été lu, envoyé, etc. Les bonnes valeurs semblent être 4 pour les messages envoyés, et 0 pour les messages reçus
readTimestampdatetimeLa date et l'heure auxquelles le message a été lu. Peut probablement être mis à la même valeur que timestamp pour les messages reçus, et à null pour les messages envoyés.
subjectchaîneLe sujet du message, manifestement non-existant pour les SMS (peut-être là pour les MMS?). Doit probablement être mis à null pour les messages envoyés et à la chaîne vide pour les messages reçus

Maintenant, il va me falloir passer à la deuxième phase: massage de mes données en CSV pour pouvoir les insérer dans la base et retrouver mes SMS.

mercredi 10 juin 2015

Ubuntu Touch - Transférer ses SMS - Phase 1: reconnaissance

Les SMS sur un Ubuntu Phone sont situés dans une base de données sqlite à l'emplacement ~/.local/share/history-service/history.sqlite. En théorie, pour importer ses SMS, il suffit de les ajouter à la base dans le format qui va bien. Téléchargeons donc la base et jetons un coup d'oeil:

$ sqlite3 history.sqlite
sqlite> .schema
CREATE TABLE schema_version (
    version int
);
CREATE TABLE threads (
    accountId varchar(255),
    threadId varchar(255),
    type tinyint,
    lastEventId varchar(255),
    lastEventTimestamp datetime,
    count int,
    unreadCount int
);
CREATE TABLE thread_participants (
    accountId varchar(255),
    threadId varchar(255),
    type tinyint,
    participantId varchar(255)
);
CREATE TABLE text_events (
    accountId varchar(255),
    threadId varchar(255),
    eventId varchar(255),
    senderId varchar(255),
    timestamp datetime,
    newEvent bool,
    message varchar(512),
    messageType tinyint,
    messageStatus tinyint,
    readTimestamp datetime,
    subject varchar(256)
);

Rien de très méchant à priori (j'ai laissé la partie du schéma qui décrit les "triggers" qui mettent à jour certaines dates, ainsi que les voice_events, correspondant aux conversations téléphoniques, et aux text_event_attachments, correspondant aux pièces jointes, j'imagine pour les MMS). L'on comprend qu'il y a un certain nombre de "threads", qui regroupent un certain nombre de "thread_participants", et que l'on peut ensuite ajouter chaque message dans text_event. Voici ce que j'ai réussi à en comprendre de la première table d'après les données déjà présentes après quelques appels et quelques SMS:

threads

ChampTypeCommentaire
accountIdchaîneUn nom de compte, probablement une constante pour tous les téléphones. Chez moi, c'est systématiquement la valeur "ofono/ofono/account0"
threadIdchaîneL'identifiant du thread. C'est apparemment le numéro de téléphone de son correspondant, sauf quand il s'agit de correspondants nommés, comme "Ikea" ou "Orange"
typeentierUne valeur qui peut être 0 ou 1. En regardant les numéros de téléphone associés, je pense que cela indique s'il s'agit d'un thread de message, avec le code 0, ou d'un historique d'appel, avec un 1. Pour l'import des SMS, on mettra 0 partout.
lastEventIdchaîneC'est un identifiant assez moche, qui contient soit un numéro de téléphone, soit une date, suivi d'une date et d'une heure. J'imagine que c'est le dernier eventId de la table text_events, et qu'il est mis à jour via une trigger
lastEventTimestampdate et heurela date et l'heure du dernier message
countentierLe nombre de messages dans le thread
unreadCountentierLe nombre de messages non lus. Pour l'import, un 0 sera bien suffisant.

Suite au prochain numéro, car il est temps d'aller se coucher!

mardi 2 juin 2015

Et en plus, on peut téléphoner avec!

Ayant enfin reçu ma carte SIM, j'ai pu tester mon BQ Aquarius Ubuntu Edition en "conditions réelles". La bonne nouvelle, c'est qu'effectivement, on peut téléphoner avec. Ça a l'air bête, comme ça, mais j'avais en tête un collègue et son Nokia N900 qui, semble-t-il, était particulièrement peu coopératif sur ce sujet là.

C'est qu'on sent particulièrement que sur un Ubuntu Touch, la fonction téléphone est une application comme une autre, ni plus, ni moins. On a donc droit à 4 secondes de changement, et téléphoner à un contact qui ne soit pas dans sa liste d'appels récents est assez peu ergonomique, puisqu'il faut repasser par l'application "contacts".

Mais trêve de pessimisme, donner un appel comme en recevoir fonctionne finalement bien, et l'application SMS est satisfaisante.

samedi 30 mai 2015

Ubuntu Touch - Transférer ses contacts

La première chose à faire pour que mon Aquarius soit utilisable, c'est de transférer mes contacts. C'est là que l'on se rend compte que le téléphone n'est absolument pas prêt pour monsieur et madame tout le monde: la méthode la plus efficace requiert de s'intéresser de près à la ligne de commande.

Tout d'abord, il faut ouvrir un accès SSH sur le téléphone. J'aime beaucoup la solution de Robie Basak qui consiste à transférer sa clé SSH via netcat (nc), et évite d'avoir à se fader adb.

Une fois que l'on peut ouvrir une session SSH sur son téléphone, tout devient plus simple, parce que l'application "Terminal", aussi bien qu'elle soit, est quand même sérieusement pénible à utiliser avec un clavier tactile. Il faut donc copier ses contacts quelque part sur le téléphone, puis utiliser syncevolution pour les importer, comme indiqué par Nicolas Delvaux.

Une petite remarque en passant: il n'est pas rare de trouver des solutions qui s'appliquent aux versions beta d'Ubuntu Touch. Avec une version officielle pré-installée, c'est souvent beaucoup plus simple!

La prochaine étape sera d'importer tous mes SMS, que j'ai pu exporter de mon Nokia au format CSV. L'Ubuntu Touch gère les SMS via une base SQLite, j'espère donc pouvoir faire un peu de reverse engineering et comprendre comment pousser mes données là dedans.

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.

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.