🔹 1. Comment mesurer et optimiser les performances d’une application en C# ?
✅ Mesure :
-
Profiler : Visual Studio Profiler, JetBrains dotTrace, PerfView
-
Benchmarking : BenchmarkDotNet pour mesurer des micro-benchmarks
-
EventCounters / Diagnostics pour mesurer l’usage CPU, GC, allocations mémoire
-
Stopwatch pour tests simples :
var sw = Stopwatch.StartNew();
// code à mesurer
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
✅ Optimisation :
-
Réduire les allocations GC inutiles (
stringimmutables →StringBuilder,Span<T>/Memory<T>) -
Préférer ValueTuple à Tuple pour éviter les heap allocations
-
Minimiser l’usage de boxing/unboxing
-
Optimiser LINQ critiques (éviter
Select().ToList()répétitif) -
Profiler et cibler les hot paths
-
Utiliser async/await pour IO bound et
Parallel/Taskpour CPU bound
🔹 2. Avantages et inconvénients des structs par rapport aux classes
| Critère | Struct | Classe |
|---|---|---|
| Type | Value type | Reference type |
| Allocation | Stack (rapide) | Heap (GC) |
| Taille | Petit (<16 octets recommandé) | Illimitée |
| Mutation | Copie sur assignation | Référence partagée |
| Héritage | Pas de polymorphisme | Supporté |
| Avantage | Pas de GC, performances | Flexibilité, héritage |
| Inconvénient | Copie fréquente peut être coûteuse | Allocations sur heap |
✅ Bon usage : types petits et immuables (ex: Point, Color, DateTime)
🔹 3. Pourquoi faut-il éviter les boxing et unboxing ?
-
Boxing : conversion d’un value type en object/interface → allocation sur heap
-
Unboxing : cast inverse → vérification type → overhead CPU
🧩 Exemple :
int x = 5;
object obj = x; // boxing
int y = (int)obj; // unboxing
-
Impacts : GC supplémentaire, perte performance
-
Solution : utiliser generics (
List<int>au lieu deArrayList)
🔹 4. Différence entre static class et sealed class
| Critère | static class | sealed class |
|---|---|---|
| Instanciation | Impossible | Possible |
| Héritage | Impossible | Impossible à hériter |
| Membres | Tous statiques | Membres normaux ou statiques |
| Usage | Helper, extension methods | Classe normale qu’on veut empêcher d’hériter |
🔹 5. Fonctionnement du pattern Decorator en C#
✅ Objectif :
Ajouter des fonctionnalités dynamiquement à un objet sans modifier sa classe.
🧩 Exemple :
public interface INotifier { void Send(string message); }
public class EmailNotifier : INotifier { public void Send(string msg) => Console.WriteLine("Email: " + msg); }
public class SmsDecorator : INotifier
{
private readonly INotifier _inner;
public SmsDecorator(INotifier inner) => _inner = inner;
public void Send(string msg)
{
_inner.Send(msg);
Console.WriteLine("SMS: " + msg);
}
}
// Usage
INotifier notifier = new SmsDecorator(new EmailNotifier());
notifier.Send("Hello");
🔹 6. Différences entre abstract class et interface
| Critère | Abstract class | Interface |
|---|---|---|
| Héritage | Simple/multiple avec interface | Multiple |
| Membres | Méthodes, propriétés, champs, constructeurs | Méthodes/propriétés/events seulement |
| Implémentation | Partielle possible | Par défaut depuis C# 8 avec default impl. |
| Usage | Base commune avec comportement partagé | Contrat pur, flexible |
🔹 7. Fonctionnement du pattern Singleton et pièges en C#
✅ Objectif :
Assurer une unique instance globale
🧩 Exemple thread-safe :
public class Singleton
{
private static readonly Lazy<Singleton> _instance = new(() => new Singleton());
public static Singleton Instance => _instance.Value;
private Singleton() { }
}
⚠️ Pièges :
-
Non thread-safe si
staticsansLazy -
Serialization peut créer de nouvelles instances
-
Reflection peut bypasser le constructeur privé
🔹 8. Pourquoi utiliser lock(obj) plutôt que Monitor.Enter(obj) ?
-
lock(obj)= syntactic sugar pourMonitor.Enter/Exitavec gestion automatique des exceptions -
Monitor.Enter/Exit= plus flexible, mais risque de deadlock siExitoublié
lock(_lockObj) { /* code thread-safe */ }
✅
lockest recommandé pour la plupart des cas simples.
🔹 9. Fonctionnement du pattern Repository et utilité
✅ Objectif :
Abstraction de la couche accès données pour séparer logique métier et DB
🧩 Exemple :
public interface IProductRepository
{
Product GetById(int id);
void Add(Product p);
}
public class ProductRepository : IProductRepository
{
private readonly DbContext _context;
public ProductRepository(DbContext context) => _context = context;
public Product GetById(int id) => _context.Products.Find(id);
public void Add(Product p) => _context.Products.Add(p);
}
-
Facilite le unit testing
-
Permet de changer facilement la source de données
🔹 10. Fonctionnement de yield return et utilité
✅ Objectif :
Créer des itérateurs paresseux (lazy evaluation)
🧩 Exemple :
IEnumerable<int> GetNumbers()
{
for (int i = 0; i < 5; i++)
yield return i;
}
foreach(var n in GetNumbers()) Console.WriteLine(n);
✅ Avantages :
-
Mémoire optimisée (pas de liste complète)
-
Evaluation à la demande
-
Idéal pour gros jeux de données ou flux infinis
📘 Synthèse des concepts avancés
| Concept | Utilité / Avantage | Astuces / Pièges |
|---|---|---|
| Performance | Profiling, BenchmarkDotNet, Span/Memory | Eviter allocations, LINQ sur-alloué |
| Struct vs Class | Stack allocation, pas de GC | Copier grands structs coûteux |
| Boxing/Unboxing | Éviter allocations heap | Utiliser Generics |
| static vs sealed | Helper vs classe finale | Comprendre héritage |
| Decorator | Extensibilité dynamique | Pas toucher code existant |
| abstract vs interface | Base avec comportement vs contrat pur | Choisir selon réutilisation |
| Singleton | Unique instance | Thread-safe + Lazy + attention serialization/reflection |
| lock vs Monitor | Thread safety simple | lock recommandé |
| Repository | Isolation DB layer | Testabilité, flexibilité |
| yield return | Lazy iteration | Evite création complète de liste |
🔹 1. Gérer la latence et les performances dans une application de trading en temps réel
✅ Bonnes pratiques :
-
Minimiser les allocations mémoire :
-
Utiliser
Span<T>,Memory<T>,ArrayPool<T>pour éviter la pression sur le GC.
-
-
Eviter le boxing/unboxing :
-
Utiliser des
structet desValueTuplepour manipuler des données financières.
-
-
Optimiser les structures de données :
-
ConcurrentDictionary,ImmutableDictionary,PriorityQueuepour accès rapide et thread-safe.
-
-
Parallélisme contrôlé :
-
Distribuer les calculs sur plusieurs threads sans saturer le CPU.
-
-
Priorité thread :
-
Utiliser
ThreadPriority.Highestpour threads critiques (ex: matching engine).
-
-
Low-latency networking :
-
Préférer
Span<byte>pour buffers réseau,SocketAsyncEventArgspour IO non-bloquant.
-
🔹 2. Meilleures pratiques pour développer des applications temps réel en C#
-
Éviter la GC sur hot path (pré-allocation, objets immuables)
-
Traitement parallèle : Task, Parallel.ForEach, mais attention à la contention
-
Thread affinity : conserver les threads critiques pour réduire les migrations
-
Lock-free structures :
ConcurrentQueue<T>,ConcurrentDictionary<T>,Interlockedpour éviter les locks coûteux -
Profiling et benchmarks : mesurer la latence et le throughput en continu
-
Logging asynchrone : ne pas bloquer le thread critique
🔹 3. Garbage Collector et impact sur le pricing temps réel
-
GC générationnel :
-
Gen0 : objets temporaires (allocation rapide)
-
Gen1 : objets intermédiaires
-
Gen2 : objets long-lived
-
-
Impact :
-
Une pause GC peut introduire des latences inacceptables pour le trading haute fréquence.
-
-
Solutions :
-
Utiliser structs,
Span<T>, pools (ArrayPool<T>), éviter allocations sur hot path -
GC.TryStartNoGCRegion()pour sections critiques
-
🔹 4. Différences : Task.Run(), Parallel.ForEach(), ThreadPool.QueueUserWorkItem()
| Méthode | Description | Usage |
|---|---|---|
Task.Run() |
Crée une Task sur le thread pool | Pour async/await et calcul CPU-bound |
Parallel.ForEach() |
Boucle parallèle avec partitioning | Traitement batch de données de marché |
ThreadPool.QueueUserWorkItem() |
Exécute un delegate sur un thread pool | Très bas niveau, sans Task |
✅ Choix dépend du niveau d’abstraction et du contrôle sur la synchronisation.
🔹 5. Optimiser les accès concurrents aux données financières
-
Concurrent collections :
ConcurrentDictionary,ConcurrentQueue,ConcurrentBag -
ReaderWriterLockSlim : lecture fréquente, écriture rare
-
Lock-free programming :
Interlocked.Increment,Volatile.Read/Write -
Immutable structures : lecture thread-safe sans verrou
🔹 6. Communication efficace entre moteur de risque et UI en temps réel
-
Event-driven architecture :
-
Le moteur de risque émet des événements (
PriceChanged,RiskUpdated) -
UI s’abonne via
IObservable<T>ouReactive Extensions (Rx.NET)
-
-
Queues :
ConcurrentQueue<T>ouBlockingCollection<T>pour bufferiser les updates -
WebSocket / SignalR : pour diffusion asynchrone et bidirectionnelle
🔹 7. Utilisation des WebSockets en C#
-
Rôle : diffusion continue de flux de marché (bid/ask, trades)
-
Avantages : faible latence, full-duplex, léger
-
Exemple serveur C# :
var webSocket = await context.WebSockets.AcceptWebSocketAsync();
while(webSocket.State == WebSocketState.Open)
{
var buffer = new byte[1024];
var result = await webSocket.ReceiveAsync(buffer, CancellationToken.None);
// traiter message
}
-
Exemple client :
ClientWebSocketpour s’abonner aux flux de prix
🔹 8. Assurer la fiabilité et haute disponibilité
-
Redondance : clusters pour le moteur de trading
-
Retry/Failover : circuit breaker et backoff
-
Snapshots / persistence : journaux des transactions et state recovery
-
Monitoring & alerting : Latency, GC pause, exceptions
🔹 9. Memory-Mapped Files pour stocker des données de marché
-
Permet d’accéder à un fichier comme une mémoire partagée
-
Avantages :
-
Très faible latence (pas de parsing répétitif)
-
Partage entre processus
-
Lecture/écriture rapide sur gros volumes
-
-
Exemple :
using (var mmf = MemoryMappedFile.CreateFromFile("market.dat", FileMode.OpenOrCreate, "map", 1024))
{
using var accessor = mmf.CreateViewAccessor();
accessor.Write(0, 42);
int value = accessor.ReadInt32(0);
}
🔹 10. Implémenter un cache performant pour l’accès aux données de marché
-
Objectif : éviter d’interroger les sources externes à chaque requête
-
Techniques :
-
MemoryCacheavecMemoryCacheEntryOptions -
ConcurrentDictionaryavec expiration personnalisée -
LRU Cache / cache basé sur
LinkedList + Dictionarypour taille fixe -
Préférer readonly structures pour thread-safety
-
-
Exemple simple avec MemoryCache :
var cache = new MemoryCache(new MemoryCacheOptions());
cache.Set("AAPL", 150.23, TimeSpan.FromSeconds(1));
double price = (double)cache.Get("AAPL");
📘 Résumé des meilleures pratiques pour C# temps réel (Trading / Pricing)
| Concept | Astuce |
|---|---|
| Allocation mémoire | Span<T>, Memory<T>, ArrayPool<T> |
| Threading | Task.Run, Parallel.ForEach, ThreadPool, lock-free |
| Concurrence | ConcurrentDictionary, ReaderWriterLockSlim, Immutable structures |
| UI communication | Rx.NET, ObservableCollection, WebSockets |
| Haute disponibilité | Clustering, snapshots, failover, monitoring |
| Cache | MemoryCache, LRU, readonly structures |
| Low-latency I/O | MemoryMappedFile, SocketAsyncEventArgs |
| Garbage Collector | Minimiser allocations, utiliser NoGCRegion si critique |
Aucun commentaire:
Enregistrer un commentaire