1. Pourquoi l'I/O est-elle si lente ?
La « lenteur » des entrées/sorties (I/O) est une question récurrente. Notre code « attend » souvent les données plus longtemps qu'il ne « réfléchit ». Voyons pourquoi aller chercher des choses dans la « réserve » est lent comparé à travailler sur la « cuisine ».
Limitations physiques du matériel (Hardware Limitations)
Disques durs (HDD). Mécanique : les plateaux tournent, la tête bouge. Il faut du temps pour le déplacement (seek time) et la rotation — ça crée une latence élevée.
Disques à état solide (SSD). Plus rapides que les HDD, sans mécanique, mais l'écriture et la gestion de l'usure des cellules rendent les opérations non instantanées.
Réseau. Dépend de la bande passante et des latences, des routeurs, etc. Même avec un lien gigabit, le aller‑retour vers un serveur distant se compte en millisecondes, pas en nanosecondes comme pour le CPU.
Surcoûts de l'OS (OS Overhead)
- Vérification des droits d'accès. Le processus a-t-il la permission de lire/écrire le fichier ?
- Localisation des données du fichier. Le système de fichiers assemble les fragments.
- Buffering et cache. L'OS gère des buffers pour être efficace.
- Changement de contexte. Pendant que le processus attend l'I/O, le CPU bascule — ça coûte aussi du temps.
La grande séparation : vitesse du CPU vs vitesse de l'I/O
- Opération CPU : 0.2 – 0.5 nanosecondes
- Lecture depuis la RAM : 10 – 100 nanosecondes
- Lecture depuis un SSD : 50 – 100 microsecondes
- Lecture depuis un HDD : 5 – 10 millisecondes
- Requête réseau : 10 – 100 millisecondes et plus
L'écart est énorme. Si vous « appelez le coursier » pour chaque lettre (I/O), vous taperez lentement, peu importe la vitesse de votre « dactylo » (CPU). Il est bien plus efficace de récupérer les données par blocs — phrases et paragraphes.
2. À l'intérieur d'un fichier : que se passe-t-il vraiment ?
La chaîne d'appels lors du travail avec un fichier ressemble à ceci :
flowchart TD
A[Votre code C#] --> B[.NET FileStream]
B --> C[OS Windows / Linux / Mac]
C --> D[Système de fichiers : NTFS, ext4, APFS]
D --> E[Driver du périphérique]
E --> F[Support physique : HDD / SSD]
- Votre code appelle par exemple File.ReadAllText(path).
- .NET utilise sous le capot FileStream, des buffers et des appels système.
- L'OS gère le caching et les files d'attente.
- Le système de fichiers localise les blocs de données du fichier.
- Le driver communique avec le périphérique.
- Le support exécute l'opération physique.
Chaque couche ajoute des surcoûts. Le goulot d'étranglement est le plus souvent le support physique.
3. Exemple : code lent en pratique
Anti-pattern : lire un fichier octet par octet avec ReadByte().
// ❌ Lecture inefficace du fichier octet par octet
using FileStream fs = new FileStream("bigfile.txt", FileMode.Open);
int currentByte;
while ((currentByte = fs.ReadByte()) != -1)
{
// On fait quelque chose avec l'octet
}
Pourquoi c'est mauvais ? Chaque appel à ReadByte() est un accès distinct au flux. Sur de gros fichiers il y a des millions d'appels, et le système dépense du temps en overhead au lieu de faire le travail utile.
La bonne approche — lire par blocs :
// ✅ Lecture efficace du fichier en gros blocs
byte[] buffer = new byte[4096]; // 4 Ko — taille de buffer standard
int bytesRead;
using FileStream fs = new FileStream("bigfile.txt", FileMode.Open);
while ((bytesRead = fs.Read(buffer, 0, buffer.Length)) > 0)
{
// On traite le bloc de données obtenu
}
Lire en grosses portions permet à l'OS et au disque d'utiliser le cache et les files d'attente de façon plus efficace — le temps d'exécution diminue de plusieurs fois.
4. Impact sur les applications réelles
Interface utilisateur (UI). Une I/O bloquante « gèle » la fenêtre. Il est important de déporter les opérations en arrière-plan/async et de ne pas bloquer le thread principal.
Serveurs web et DB. Les serveurs lisent/écrivent constamment ; un disque ou réseau lent ralentit tout le service. Buffering, pool de connexions et I/O asynchrone sont la clé du débit.
Big Data. À l'échelle de gigaoctets/téraoctets, toute inefficacité se multiplie. La taille des blocs, l'accès séquentiel et le traitement en streaming font la différence.
Jeux. Les longs temps de chargement de niveaux/ressources sont de l'I/O. Un packaging correct des assets et la lecture en gros chunks réduisent les temps de chargement.
5. Erreurs typiques des débutants
Erreur fréquente — lire ligne par ligne ou octet par octet de gros fichiers via ReadByte ou utiliser un buffer trop petit (par exemple 256 octets). Le nombre d'appels système augmente et les performances chutent.
Il y a aussi l'excès inverse : essayer de lire intégralement un fichier énorme avec File.ReadAllBytes — et recevoir logiquement un OutOfMemoryException. Mieux vaut choisir le « juste milieu » : des blocs raisonnables (souvent 4–8 Ko et plus, selon le profil de charge) et un traitement en flux.
GO TO FULL VERSION