Archive for the '.NET' Category

Build your own service bus – par ou commencer ?

Julien on nov 29th 2010

Quelle meilleure façon de réveiller ce blog que de parler de la dernière soirée Alt.net ? Soirée sur le thème de « Build your own service bus » et co-présentée par Romain Verdier et moi-même.

Comme faut pas pousser mémé, je me contenterai d’inclure les slides et je me concentrerai sur un point précis. D’autres personnes plus littéraires que moi auront probablement l'occasion de vous résumer la soirée ! :-)

 

Hey Julien, c’est marrant ton histoire, mais il est ou l’arrêt le plus proche pour monter dans ton bus?

Tout d’abord, il faut bien comprendre qu’un service bus n’est qu’un framework pour simplifier la communication dans un système distribué. Voici donc quelques idées à creuser pour assimiler les concepts…

 

1. Acquérir des notions de messaging

Je vous suggère notamment de regarder MSMQ (ou son pendant portable et open source : ActiveMQ) et RabbitMQ. Ce dernier à la particularité d’utiliser un broker (les queues sont donc distantes) tandis que MSMQ permet la communication point à point mais rend l’écriture de scénarios publish-subscribe plus complexe. Pourquoi ne pas développer un petit chat pour se faire la main ? Cet exercice vous forcera a explorer les différents modèles sur le PubSub ou encore à étudier un minimum la serialization (astuce : n’essayez pas de recoder un protocole de communication a base de commandes en texte comme le FTP, on peut faire plus sympa ! :-)) .

En bonus, je vous conseille vivement d’acheter ce livre pour noel :


Enterprise Integration Patterns
Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions

This book provides a consistent vocabulary and visual notation framework to describe large-scale integration solutions across many technologies. It also explores in detail the advantages and limitations of asynchronous messaging architectures. The authors present practical advice on designing code that connects an application to a messaging system, and provide extensive information to help you determine when to send a message, how to route it to the proper destination, and how to monitor the health of a messaging system. If you want to know how to manage, monitor, and maintain a messaging system once it is in use, get this book

 

2. Acquérir des notions d’architecture sur les systèmes distribués et la SOA

Comme expliqué plus haut, un service bus n’est qu’un framework. Avant de se lancer tête baissée, il faut comprendre les principes sous-jacents. Mon auteur préféré sur la question est Udi Dahan, l'homme derrière NServiceBus...
Udi étant un conférencier assez actif, il est facile de trouver nombre de ses présentations sur le net. En voici quelques une :

Et pour les plus curieux d'entre vous (disclaimer : on sort du cadre des service bus !) :

Enfin, si vous avez beaucoup de transport en commun, vous pouvez également trouver un paquet de podcasts :

 

3. Explorer les framework open sources

Et notamment leurs samples !

 

4. La méthode du Sioux !

Si vous ne connaissez rien a tout ça mais que vous déchirez quand même en développement, jetez un œil à notre dernière offre d'emploi et contactez-mois si ça vous intéresse ! On se fera un plaisir de partager avec vous tout ce qu'on sait ! :-)

Bon courage !

Filed in .NET, Alt.net, Command Query Responsibility Segregation, Rencontres Alt.net | One response so far

Resharper 4: la beta est disponible.

Julien on mai 22nd 2008

Après une longue période en EAP (Early Access program), la beta de reshaper 4 est enfin disponible!
(a priori la stabilité à l'air au rendez-vous, du moins je n'ai pas eu de problème significatif pour le moment).

On trouvera notamment au menu du jour le support du C# 3 et de Linq, de nouveaux refactorings, de nouvelles suggestions, etc. En résumé, que du bon si vous travaillez déjà sous Visual Studio 2008 et .NET 3.5.

Pour ceux qui ne connaissent pas Resharper, je vous suggère vivement de regarder ce site: 31 days of resharper et de télécharger la version d'essai. Je vous garanti que l'essayer c'est l'adopter!

Filed in .NET | 2 responses so far

Comment vérifier qu’une chaine de caractère est vide de façon optimale?

Julien on avr 25th 2008

J'ai eu l'occasion de m'apercevoir qu'il y avait un certain besoin autour de trucs et astuces sur .NET. Je vais donc dorénavant essayer de poster ce qui me passera par la tête :). A noter que ces billets ont vocation à être court et couvrir des points très précis!

Comment vérifier qu'une chaine de caractère est vide de façon optimale?

Il existe plusieurs façon de faire la comparaison:
1) myString == ""
2) myString == String.Empty
3) myString.Length == 0
4) String.IsNullOrEmpty(myString)

La façon la plus performante est la solution 3, myString.Lengh == 0, à condition d'être sur que la chaine de caractère n'est pas nulle. Si la chaine de caractère peut potentiellement être nulle, String.IsNullOrEmpty(myString) est idéal. A noter d'ailleurs que String.IsNullOrEmpty(myString) utilise lui même string.Length == 0. En voici l'implémentation en guise de référence:

  1. public static bool IsNullOrEmpty(string value)
  2. {
  3. if (value != null)
  4. {
  5. return (value.Length == 0);
  6. }
  7. return true;
  8. }

Filed in .NET, Trucs et Astuces | No responses yet

Impact du mot cléf readonly sur les performances

Julien on avr 22nd 2008

Un collègue de travail m'a dit que le mot clef readonly permettait d'optimiser les accès mémoire et par conséquent l'accès se faisait plus rapidement qu'un membre classique (sans le mot clef readonly). Je dois avouer que cette affirmation m'a un peu surpris. J'aurai d'abord parié sur des performances identiques avec ou sans ce mot clef (Ce serait juste une vérification du compileur, qui ne modifie en rien l'exécution); puis, si cette première théorie s'avérait fausse, sur un accès mémoire plus lent (La CLR pourrait faire des vérifications lorsqu'elle essaye d'accéder au membre). Voyons donc si je me trompe!

Malheureusement pour moi, je n'ai trouvé aucune source pour confirmer ou non mes théories... J'ai donc décidé de réaliser mon propre micro-benchmark.

J'ai mesuré le temps nécessaire pour exécuter 3 principaux scénarios, avec un membre en lecture seule (readonly) puis avec un champs en lecture écriture (donc sans readonly):
- Scénario 1: Création d'un objet
- Scénario 2: Accès à un membre d'un objet
- Scénario 3: Création d'un objet et accès à un membre

j'ai exécuté ce code 1 000 000 000 de fois pour chaque scénario, et j'ai lancé chaque scénario 10 fois de suite sur 4 PC différents afin d'avoir une moyenne représentative. Pour chaque test, j'ai exclus le résultat le plus rapide et le plus lent.
Le code de mes tests (en C# et .NET 2.0) est disponible ici (Le code est dupliqué pour chaque test. Je sais que c'est horrible, toutes mes excuses :)), et les résultats sont .

Chaque test ressemble à cela (avec quelques modifications évidemment):

  1. _streamWriter.WriteLine();
  2. _streamWriter.WriteLine("Obj creation & access in loop");
  3. for (int j = 0; j < _numberOfTestExecutions; j++)
  4. {
  5. Poco poco = new Poco();
  6.  
  7. Stopwatch watch = new Stopwatch();
  8. watch.Start();
  9. for (int i = 0; i < _numberOfIterationPerTest; i++)
  10. {
  11. ReadOnlyField objReadOnlyField = new ReadOnlyField(poco);
  12. Poco myCopyOfPoco = objReadOnlyField.Poco;
  13. }
  14. watch.Stop();
  15. _streamWriter.Write(watch.ElapsedMilliseconds + ";");
  16.  
  17. watch.Reset();
  18. watch.Start();
  19. for (int i = 0; i < _numberOfIterationPerTest; i++)
  20. {
  21. ReadWriteField objReadWriteField = new ReadWriteField(poco);
  22. Poco myCopyOfPoco = objReadOnlyField.Poco;
  23. }
  24. watch.Stop();
  25. _streamWriter.WriteLine(watch.ElapsedMilliseconds);
  26. }

Voici les chiffres bruts:

Scénario 1: Création d'un objet
- lecture seule: 25172ms
- lecture écriture: 24983ms
La création d'un objet avec un membre en lecture seule est en moyenne 0.76% plus lent.

Scénario 2: Accès à un membre d'un objet
- lecture seule: 6437ms
- lecture écriture: 6449ms
L'accès à un membre en lecture seule est 0.19% plus rapide

Scénario 3: Création d'un objet et accès à un membre
- lecture seule: 19929ms
- lecture écriture: 19761ms
La création avec un membre en lecture seule puis l'accès au membre est en moyenne 0.85% plus lent

Cependant, Il est possible de voir des différences plus significatives en regardant les résultats pour chaque PC. Par exemple, sur le premier PC sur lequel j'ai fait le test (un core 2 duo, 2,8Ghz, DDR2 800Mhz), J'ai les résultats suivants:

- Scénario 1: Création d'un objet
La création d'un objet avec un membre en lecture seule est en moyenne 5.72% plus rapide qu'avec un membre en lecture écriture.
- Scenario 2: Accès à un membre d'un objet
L'accès à un membre en lecture seule est 0.94% plus rapide.
- Scenario 3: Création d'un objet et accès à un membre
La création avec un membre en lecture seule puis l'accès au membre est en moyenne 2.28% plus rapide.

Et sur un portable (centrino 1,7ghz, DDR):
- Scenario 1: Création d'un objet
La création d'un objet avec un membre en lecture seule est en moyenne 5.72% plus lent qu'avec un membre en lecture écriture.
- Scenario 2: Acces à un membre d'un objet
L'accès à un membre en lecture seule est 0.21% plus rapide.
- Scenario 3: Création d'un objet et accès à un membre
La création avec un membre en lecture seule puis l'accès au membre est en moyenne 3.01% plus lent.

Mes (propres) conclusions:
Il semblerait qu'il y ait une différence de performances non négligeable lorsque l'on utilise le mot clef readonly. Le problème est que cet impact est très dépendant du matériel sur lequel le logiciel tourne. Sur un PC avec du matériel décent, accéder à un membre en lecture seule est entre 1% et 2.5% plus rapide qu'accéder à un champs en lecture écriture. Quand à la création d'un objet, elle peut être jusqu'à 6% plus rapide. Sur du matériel plus ancien, les résultats sont inversés.
Cependant, les tests ont été exécutés avec une classe très simple, gardez donc en mémoire que dans le réalité, les différences constatées pour la création d'objets seraient probablement beaucoup moins importantes avec des classes qui ont plus de membres.

A ce stade, j'exclurai donc d'utiliser le mot clef readonly à des fins d'optimisation, sauf peut être dans des cas de figures très précis, et seulement après avoir vérifié qu'il y a effectivement un gain de performance sur les machines qui feront tourner le logiciel. A moins que votre application soit:
- installé que sur un nombre limité de machines, dans un environnement contrôlé et homogène
- pseudo temps réel ou extrêmement sensible aux performances
je ne pense pas que vous devriez considérer l'impact du mot clef readonly sur les performances. Il y a probablement des centaines d'optimisations qui seront plus efficaces!

Enfin, si vous avez constaté des résultats différents, n'hésitez pas à me le faire savoir et j'éditerai ce billet en conséquence :).
Je suis particulièrement intéressé par des benchmark avec des processeurs Xeon (malheureusement, je n'en ai pas sous la main).

Filed in .NET | No responses yet

AutoResetEvent vs ManualResetEvent: Attention!

Julien on avr 7th 2008

J'ai effectué quelques tests de montée en charge il y a quelques jours sur le composant sur lequel je travaille. Malheureusement, je me suis rapidement rendu compte que les performances étaient simplement misérables... Le soft était à peine capable de traiter 60 messages par secondes, ce qui était: 1) mauvais et 2) surprenant! (En résumé, c'est une petite brique logiciel qui fait une sorte de transformation/redirection/cache de messages en temps réel)

J'ai donc passé quelques minutes dans DotTrace afin d'avoir un aperçu de l'exécution. J'ai rapidement trouvé que dans le thread qui observait la queue et envoyait les messages, j'avais utilisé une instance de ManualResetEvent à la place de AutoResetEvent.

Si vous ne les avez jamais utilisé, ces 2 classes permettent d'envoyer des signaux entre 2 threads. De cette façon, le thread qui observe la queue ne consomme pas de ressources bêtement tant que le thread qui remplit la queue n'envoie pas un signal pour prévenir qu'il y a quelque chose à récupérer. Par exemple, le thread 1 attendra que la queue soit remplie de la façon suivante:

  1.  
  2. private AutoResetEvent mySignal = new AutoResetEvent(false);
  3.  
  4. private void MonitorQueue()
  5. {
  6. while(!monitorQueue)
  7. {
  8. mySignal.WaitOne();
  9. GetItemsInTheQueueAndDoStuff();
  10. }
  11. }

Et le thread 2 injectera des données avec:

  1.  
  2. public void EnqueueItem(Item myItem)
  3. {
  4. InsertInQueue(myItem);
  5. mySignal.Set();
  6. }
  7.  

Comme je l'ai dis, mon problème venait du fait que j'avais utilisé par erreur une instance de ManualResetEvent à la place de AutoResetEvent. Ces 2 classes sont quasiment identiques, excepté que lorsque vous utilisez ManualResetEvent, vous devez reseter le signal manuellement avec mySignal.Reset(). Dans mon cas, à la place de bloquer sur mySignal.WaitOne(), le code bouclait en permanence et consommait une quantité de CPU très importante.

J'ai corrigé le problème et relancé mon test de performance: je suis maintenant à 5000 messages par secondes et à moins de 40% de CPU. Bien mieux!

Filed in .NET | No responses yet

lock(this): A ne pas faire!

Julien on avr 5th 2008

J'ai vu ce genre de choses dans plusieurs bouts de code récemment:

  1. lock(this)
  2. {
  3. // Do stuff...
  4. }

Même si cela fonctionne, c'est une très mauvaise idée! Vous ne devriez jamais (ou du moins je n'ai aucune bonne raison en tête) faire un lock sur un type public, ce qui inclue évidemment le "this". Il y a une raison très simple à cela: vous n'avez aucun moyen de maitriser ce que le code appelant va essayer de faire.

Prenons un exemple simple. Dans le code suivant, nous avons une class qui utilise un lock sur "this" (On l'appellera ClassThatLocksItself). Nous avons aussi une seconde class (CallerClass) qui va appeler la première. CallerClass va alors utiliser un lock sur l'instance de ClassThatLocksItself lors de cet appel.

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. ClassThatLocksItself myObj = new ClassThatLocksItself();
  6.  
  7. CallerClass caller = new CallerClass(myObj);
  8. caller.LockTheObjectInAThread();
  9. Thread.Sleep(500);
  10.  
  11. myObj.LockMe();
  12. }
  13. }
  14.  
  15. class CallerClass
  16. {
  17. private ClassThatLocksItself _myObj;
  18. public CallerClass(ClassThatLocksItself myObj)
  19. {
  20. _myObj = myObj;
  21. }
  22.  
  23. public void LockTheObjectInAThread()
  24. {
  25. ThreadPool.QueueUserWorkItem(LockTheObject);
  26. }
  27.  
  28. public void LockTheObject(object state)
  29. {
  30. Console.WriteLine("Acquiring lock on the object");
  31. lock (_myObj)
  32. {
  33. Thread.Sleep(100000); // Do a long computation
  34. }
  35. Console.WriteLine("Releasing lock on the object");
  36. }
  37. }
  38.  
  39. public class ClassThatLocksItself
  40. {
  41. public void LockMe()
  42. {
  43. Console.WriteLine("ClassThatLocksItself -- Trying to acquire lock on this");
  44. lock (this)
  45. {
  46. Console.WriteLine("ClassThatLocksItself -- lock on this acquired");
  47. }
  48. Console.WriteLine("ClassThatLocksItself -- lock on this released");
  49. }

Si vous essayez d'exécuter ce code, vous remarquerez que Console.WriteLine("ClassThatLocksItself -- lock on this acquired"); est seulement executé lorsque la fonction LockTheObject() retourne (dans notre cas, après 100 secondes!). En utilisant lock(this), vous êtes devenu dépendant de facteurs externes inconnus (dans notre exemple, CallerClass a décidé d'utiliser l'instance de CLassThatLocksItself pour la synchronisation, ce que ClassThatLocksItself n'avait pas moyen de savoir). La situation est même pire pour les développeurs qui veulent réutiliser votre ClassThatLocksItself: a moi de parcourir la source de la classe, ils n'ont aucun moyen de savoir qu'il peut y avoir de grave problèmes de synchronisation.

Maintenant, essayez de deviner ce qui va se passer si l'on modifie l'implémentation de LockTheObject() par la suivante:

  1. public void LockTheObject(object state)
  2. {
  3. Console.WriteLine("Acquiring lock on the object");
  4. lock (_myObj)
  5. {
  6. _myObj.LockMe(); // will not lock!
  7. }
  8. Console.WriteLine("Releasing lock on the object");
  9. }

Un certain nombre d'entre vous devrait parier sur le deadlock je suppose. Mais non! En fait, la CLR est assez intélligente pour détecter que vous avez déjà acquis le lock sur le même objet lorsque LockMe execute lock(this). N'oubliez pas, nous sommes dans le même thread! Cela rend lock(this) très subtile: vous n'aurez des problèmes que dans des cas très spécifiques!.

Filed in .NET | No responses yet