1. Niyə giriş-çıxış bu qədər yavaşdır?
Giriş-çıxışın (I/O) "yavaşlığı" həmişə maraq doğuran məsələdir. Kodumuz tez-tez məlumatları "gözləyir", hesablamadan daha çox vaxt sərf edir. Gəlin niyə "anbar"a gedib-gəlməyin mətbəxdə işləməyə nisbətən daha ləng olduğunu başa düşək.
Hardware məhdudiyyətləri (Hardware Limitations)
Sərt disklər (HDD). Mexanika: plitələr fırlanır, ox hərəkət edir. Axtarış vaxtı və fırlanma üçün vaxt lazımdır — bu yüksək latenziya yaradır.
Sərt vəziyyət yaddaşları (SSD). HDD-dən daha sürətlidir, mexanika yoxdur, amma yazma və hüceyrələrin aşınmasının idarə olunması əməliyyatları ani etmir.
Şəbəkə. Bant genişliyi və gecikmədən, routerlərdən və marşrutdan asılıdır. Giga-bit olsa belə, uzaq serverə cavab mili- və ya daha çox millisaniyə olur, CPU kimi nanosaniyələr deyil.
Əməliyyat sisteminin overhead-ları (OS Overhead)
- İcazələrin yoxlanması. Prosesin faylı oxuma/yazma icazəsi varmı?
- Fayl məlumatlarının tapılması. Fayl sistemi fragmentləri yığır.
- Bufere və cache. OS performans üçün buffer və cache-ləri idarə edir.
- Context switch. Proses I/O üçün gözləyərkən CPU dəyişir — bu da vaxtdır.
Böyük uçurum: CPU sürəti vs I/O sürəti
- CPU əməliyyatı: 0.2 – 0.5 nanosaniyə
- RAM-dən oxuma: 10 – 100 nanosaniyə
- SSD-dən oxuma: 50 – 100 mikrosekund
- HDD-dən oxuma: 5 – 10 millisekund
- Şəbəkə sorğusu: 10 – 100 millisekund və daha çox
Uçurum böyükdür. Hər xarakter üçün "kuryer çağırırsınızsa" (I/O), nə qədər sürətli "yığan" (CPU) olursunuzsa olun, yazmaq yavaş olacaq. Məlumatları bloklarla — cümlələr və abzaslar şəklində götürmək daha effektivdir.
2. Fayl daxilində: əslində nə baş verir?
Faylla işləyərkən əmrlər zənciri belə görünür:
flowchart TD
A[Sizin C# kodunuz] --> B[.NET FileStream]
B --> C[OS Windows / Linux / Mac]
C --> D[Fayl sistemi: NTFS, ext4, APFS]
D --> E[Cihaz drayveri]
E --> F[Fiziki disk: HDD / SSD]
- Sizin kodunuz məsələn, File.ReadAllText(path) çağırır.
- .NET daxildə FileStream, buffer-lar və sistem çağırışlarından istifadə edir.
- OS cache və növbələri idarə edir.
- Fayl sistemi faylın bloklarını tapır.
- Drayver cihazla əlaqə yaradır.
- Yaddaş cihazı fiziki əməliyyatı yerinə yetirir.
Hər təbəqə overhead əlavə edir. Ən çox tıxac fiziki cihazda olur.
3. Nümunə: praktikada yavaş kod
Antipattern: faylı bir baytla oxumaq ReadByte() vasitəsilə.
// ❌ Faylı bayt-bayt oxumaq səmərəsizdir
using FileStream fs = new FileStream("bigfile.txt", FileMode.Open);
int currentByte;
while ((currentByte = fs.ReadByte()) != -1)
{
// Bir baytla nəsə edirik
}
Niyə pisdir? Hər çağırış ReadByte() axına ayrıca müraciətdir. Böyük fayllarda belə çağırışların sayı milyonlarla ola bilər və sistem faydalı iş yerinə təşkilati xərclərə vaxt sərf edir.
Doğru — bloklarla oxumaq:
// ✅ Faylı böyük bloklarla effektiv oxuma
byte[] buffer = new byte[4096]; // 4 KB — standart buffer ölçüsü
int bytesRead;
using FileStream fs = new FileStream("bigfile.txt", FileMode.Open);
while ((bytesRead = fs.Read(buffer, 0, buffer.Length)) > 0)
{
// Alınan blok məlumatı emal edirik
}
Böyük porsiyalarla oxumaq OS və diskin cache və növbələri daha effektiv istifadə etməsinə imkan verir — icra vaxtı qatlarla azalır.
4. Real tətbiqlərə təsiri
İstifadəçi interfeysi (UI). Blok edən I/O pəncərəni "dondurur". Əhəmiyyətlidir ki, əməliyyatları fon/asanxronluğa çıxarasınız və əsas thradı bloklamayasınız.
Veb-serverlər və DB. Serverlər davamlı oxu/yazı əməliyyatları aparır; yavaş disk və ya şəbəkə bütün xidməti yavaşıda bilər. Buffering, connection pool və asin xron I/O throughput üçün açardır.
Big Data. Qiqabayt/terabayt səviyyəsində hər bir səmərəsizlik böyüyür. Blok ölçüləri, ardıcıl oxu və stream əsaslı emal məsələnin həllində rol oynayır.
Oyunlar. Səviyyələrin/asset-lərin uzun yüklənməsi — bu I/O-dir. Asset-ləri düzgün paketləmək və böyük chunk-larla oxumaq yüklənməni qısaldır.
5. Başlayanların tipik səhvləri
Tez-tez rastlanan səhv — böyük faylları sətr-sətr və ya bayt-bayt ReadByte ilə oxumaq və ya çox kiçik buffer (məsələn, 256 bayt) istifadə etmək. Sistemdə çağırışların sayı artır, performans düşür.
Digər sərhəd: böyük faylı tam oxumağa çalışıb File.ReadAllBytes istifadə etmək və nəticədə OutOfMemoryException almaq. "Qızıl orta" seçmək daha yaxşıdır: məntiqli bloklar (dəfələrlə 4–8 KB və daha böyük, yükləmə profilinə görə) və stream əsaslı emal.
GO TO FULL VERSION