1. Giriş
Nə üçün buffer lazımdır və sürətin müqayisəsi
Artıq başa düşdük ki, bufferizasiya — məlumatları böyük “toplular”a yığmağa imkan verən strategiyadır, beləliklə diskə yüz minlərlə dəfə deyil, daha az dəfə müraciət edilir və bir dəfəyə çoxlu məlumat ötürülür. Bu xüsusilə böyük həcmli məlumatlarda performansda ciddi artım verir.
I/O performansı haqda nə vaxt düşünmək lazımdır
- Əgər böyük fayllarla işləyirsinizsə (gigabaytlar, terabaytlar — məsələn, şirkətinizin bütün iş illərinin log kolleksiyası).
- Əgər minimal cavab vaxtı tələb olunur (məsələn, real-time log işləmə).
- Əgər fayla müraciətlərin sayı çoxdursa (məsələn, kütləvi şəkil adlarının dəyişdirilməsi/kopyalanması).
- Müsahibədə göstərmək üçün ki, optimizasiya yanaşmalarını başa düşürsünüz (və sürəti ölçməyi sevirsiniz, sadəcə “işləsin” deyə yox).
.NET-də defolt üzrə əksər file stream-lər artıq öz bufferizasiyasına malikdir, amma bəzən incə sazlama və ya xüsusi strategiya tələb olunur.
Əsas “oyunçular” — hansı bufferlər olur
| Sinif | Defolt buffer | Ölçünü dəyişmək olar? | Tətbiq sahəsi |
|---|---|---|---|
|
Var (4096 bayt) | Bəli (constructor vasitəsilə) | Əsas file stream |
|
Bəli (4096 bayt) | Bəli (constructor vasitəsilə) | Stream üzərində “qabıq” |
|
Bəli (1024/1024 bayt) | Bəli (constructor) | Tekstlə işləmə |
- BufferedStream başqa stream-i “yığa” və performansı yaxşılaşdıra bilər (məsələn, əgər baz stream pis bufferizasiya olunubsa və ya daha böyük buffer lazımdırsa).
- Buffer ölçüsü — sürət və RAM istehlakı arasında kompromisdir.
2. Nümunələr:
Gəlin böyük faylı kopyalamaq üçün üç yanaşmanı müqayisə edək:
- Buffersiz — bir baytla (zərərli, amma nümayiş üçündür!)
- Defolt buffer — standart FileStream ilə CopyTo
- Əl ilə buffer idarəsi — buffer-i özümüz ötürürük, ölçünü optimizasiya edirik
Eksperimentlər üçün fayl kopyalama üzrə sadə utilit yaradaq və onu tətbiqimizə əlavə edək. Faylın adı olsun BigFile.bin.
class FileCopyBenchmarks
{
// Bir baytla kopyalama (anti-nümunə — belə etməyin!)
public static void CopyOneByte(string source, string dest)
{
using var input = new FileStream(source, FileMode.Open, FileAccess.Read);
using var output = new FileStream(dest, FileMode.Create, FileAccess.Write);
int b;
while ((b = input.ReadByte()) != -1)
{
output.WriteByte((byte)b);
}
}
// Standart FileStream buffer-i ilə kopyalama
public static void CopyWithDefaultBuffer(string source, string dest)
{
using var input = new FileStream(source, FileMode.Open, FileAccess.Read);
using var output = new FileStream(dest, FileMode.Create, FileAccess.Write);
input.CopyTo(output); // Daxili buffer-dən istifadə edir (adətən 81920 bayt)
}
// Əl ilə buffer nəzarəti ilə kopyalama
public static void CopyWithCustomBuffer(string source, string dest, int bufferSize = 1024 * 1024)
{
using var input = new FileStream(source, FileMode.Open, FileAccess.Read);
using var output = new FileStream(dest, FileMode.Create, FileAccess.Write);
byte[] buffer = new byte[bufferSize];
int bytesRead;
while ((bytesRead = input.Read(buffer, 0, buffer.Length)) > 0)
{
output.Write(buffer, 0, bytesRead);
}
}
}
Hansı funksiyanın daha sürətli olduğunu ölçək.
Performansı düzgün necə ölçmək
.NET-də vaxtı ölçmək üçün ən asan üsul Stopwatch-dan istifadə etməkdir:
static void Measure(Action action, string description)
{
var sw = Stopwatch.StartNew();
action();
sw.Stop();
Console.WriteLine($"{description}: {sw.ElapsedMilliseconds} ms");
}
İndi eyni faylı müxtəlif üsullarla kopyalamağa çalışaq:
string source = "BigFile.bin";
string dest1 = "copy1.bin";
string dest2 = "copy2.bin";
string dest3 = "copy3.bin";
// Əvvəlcədən BigFile.bin faylını yaradın (məsələn, 100-500 MB) və ya hər hansı böyük fayldan istifadə edin.
Measure(() => FileCopyBenchmarks.CopyOneByte(source, dest1), "CopyOneByte (bir-bir bayt)");
Measure(() => FileCopyBenchmarks.CopyWithDefaultBuffer(source, dest2), "CopyWithDefaultBuffer (standart)");
Measure(() => FileCopyBenchmarks.CopyWithCustomBuffer(source, dest3, 1024 * 1024), "CopyWithCustomBuffer (1 MB)");
Riskli məqamlar və tələlər
- Əgər çox dəfə ardıcıl işə salınarsa, OS cache diski “isti” edə bilər və sonrakı ölçümlər daha sürətli çıxar — real qiymətləndirmə üçün proqramı yenidən başlatmaq və cache-i təmizləmək yaxşıdır.
- Əgər fayl kiçikdirsə (10–20 KB), bufferizasiyanın faydası nəzərə çarpmaya bilər — fayl nə qədər böyükdürsə, fərq bir o qədər gözəçarpandır.
- Əgər buffer ölçüsünü çox böyük göstərsəniz (məsələn, 100 MB), RAM istehlakı kəskin artar və sistemə zərər verə bilər.
Nəticələrin vizuallaşdırılması: cədvəl
| Metod | 500 MB fayl üçün vaxt (ms) |
|---|---|
| Bir-bir bayt | 100 000+ |
| Standart FileStream / CopyTo | 1 000 — 5 000 |
| Əl ilə buffer 1 MB | 700 — 1 200 |
Rəqəmlər təxmini olsa da, trend belədir — buffer böyüdükcə diskə müraciət sayı azalır və sürət artır.
3. Əl ilə buffer idarəsinin anatomiyası
Niyə bəzən buffer ölçüsünü əl ilə sazlamaq istəyirsən? Sadə bir analogiya: yeni mənzilə köçürsən, əşyaları bir stəkan-la daşımaq olar, ya da böyük bir qutu götürmək olar. Amma qutunun da bir həddi var, onu qaldıra bilməzsən!
Əl ilə buffer ilə oxuma necə işləyir
// Əl ilə buffer ölçüsünü idarə etmə nümunəsi
int bufferSize = 1024 * 1024; // 1 MB
byte[] buffer = new byte[bufferSize];
int read;
while ((read = inputStream.Read(buffer, 0, buffer.Length)) > 0)
{
outputStream.Write(buffer, 0, read);
}
- Read metodu bütün buffer-i doldurmağa çalışır, amma fayl bitsə daha az qaytara bilər.
- Buffer ölçüsü çox vaxt 32 KB-dən 4–8 MB-ə qədər götürülür — daha böyük ölçü adətən əlavə artım vermir.
- Əgər belə əməliyyatlar və ya thread-lər çoxdursa, RAM-i unutmamaq lazımdır.
Buffer ölçüsü ilə eksperiment
Nümunədə buffer ölçüsünü dəyişdirin (32 KB, 128 KB, 1 MB, 4 MB) və performans pikinin harada olduğunu görün. Adətən “qızıl orta” təxminən 1 MB olur.
Əl ilə buffer nə zaman daha faydalıdır
- İstifadə olunan yaddaş həcmini nəzarət etmək lazım olduqda (məsələn, proqram zəif serverdə işə düşür).
- Əgər stream avtomatik bufferləşmir (NetworkStream, custom streamlər).
- Çoxlu paralel əməliyyatlarla işləyərkən — hər thread-ə optimal ölçüdə öz buffer ayırmaq olar.
- Çox böyük faylı maksimal sürətlə emal etmək istəyəndə (məsələn, böyük CSV faylının çevrilməsi).
4. Ən yaxşı təcrübələr və tipik səhvlər
Buffer-i tamamilə nəhəng etmək olar? “Ram boldu” deyib bunu etmək olar, amma niyə? Çox böyük buffer bəzən performansı da aşağı sala bilər: yaddaş boş-boşuna tutulur və sistem cache/ləngimə problemlərinə düşə bilər.
Əl ilə bufferizasiya hər şeyi sürətləndirmir. Kiçik fayllar və ya artıq optimizasiya olunmuş stream-lər üçün (məsələn, böyük daxili buffer-li FileStream) artım minimal olacaq, amma kod daha mürəkkəbləşəcək.
Tipik tələyə düşmə: stream-i bağlamağı unutmaq və ya exception-ı düzgün tutmamaq — fayl bloklanmış qalacaq. Fayllarla işləyərkən using və səhv emalı üçün try-catch istifadə edin.
GO TO FULL VERSION