Cpp Sınıfı İçinde Global ve Yerel Değişken Kullanımı: Performans ve Hafıza Yönetimi

2.953 Kişi Okudu

Cpp Sınıfı İçinde Global ve Yerel Değişken Kullanımı: Performans ve Hafıza Yönetimi

Yazılım geliştirme sürecinde, performans ve hafıza yönetimi hayati öneme sahiptir. Bu yazımızda, C++ programlama dilinde sınıf içinde global ve yerel değişken kullanımının performans ve hafıza yönetimi üzerindeki etkilerini detaylı bir şekilde ele alacağız. Ayrıca, bu konseptleri anlamak için basit ancak etkili örnek kodlar ve bu kodların assembly diline çevrilmiş hallerini inceleyeceğiz.

C++ Örnek Kod

#include <stdint.h>

struct LargeStruct {
    uint64_t num1 = 0;
    uint64_t num2 = 0;
    uint64_t num3 = 0;    
    
    // Büyük bir veri bloğu
    uint8_t data[10000] = {0}; 
};

class MyClass {
public:
    void updateGlobalInstance();
    void updateLocalInstance();
    // Sınıf seviyesinde global bir örnek
    LargeStruct globalInstance = {0}; 
};

void MyClass::updateGlobalInstance() {
    // Global örneği güncelle
    globalInstance.num1 = 0; 
}

void MyClass::updateLocalInstance() {
   // Fonksiyon seviyesinde yerel bir örnek
    LargeStruct localInstance = {0}; 
    localInstance.num1 = 0;
}

Assembly Dilindeki İki Fonksiyonun Karşılaştırılması ve Performans Etkileri

MyClass::updateGlobalInstance() fonksiyonunun assembly dili çıktısı:

MyClass::updateGlobalInstance():
        push    rbp
        mov     rbp, rsp
        mov     QWORD PTR [rbp-8], rdi
        mov     rax, QWORD PTR [rbp-8]
        mov     QWORD PTR [rax], 0
        nop
        pop     rbp
        ret

MyClass::updateLocalInstance() fonksiyonunun assembly dili çıktısı:

MyClass::updateLocalInstance():
        push    rbp
        mov     rbp, rsp
        sub     rsp, 10112
        mov     QWORD PTR [rbp-10104], rdi
        lea     rax, [rbp-10096]
        mov     edx, 10088
        mov     esi, 0
        mov     rdi, rax
        call    memset
        mov     QWORD PTR [rbp-10096], 0
        nop
        leave
        ret

İlk fonksiyon (MyClass::updateGlobalInstance()) ve ikinci fonksiyon (MyClass::updateLocalInstance()) arasındaki en önemli fark, ikinci fonksiyonun daha fazla yerel depolama alanı kullanması ve bu nedenle daha fazla hafıza işlemi gerçekleştirmesi ve daha büyük bir stack frame oluşturmasıdır. Bu farklar performansa etki edebilir.

İkinci fonksiyonun assembly ifadeleri ve performans etkileri şu şekildedir:

  1. sub rsp, 10112: Bu komut, yerel depolama için 10112 bayt yer ayırır. Bu, büyük bir stack frame oluşturur ve bu nedenle daha fazla hafıza kullanır. Ayrıca, bu işlem işletim sistemi ve donanım tarafından gerçekleştirildiğinden bazı performans maliyeti vardır.
  2. mov QWORD PTR [rbp-10104], rdi: Bu komut, girdi parametresini yerel depolama alanına kopyalar. Bu, hafıza erişimi gerektirir ve hafıza trafiği oluşturabilir.
  3. mov rax, QWORD PTR [rbp-10096] ve mov QWORD PTR [rbp-10096], 0: Bu iki komut, yerel depolama alanında değer okuma ve yazma işlemleri gerçekleştirir. Hafıza erişimi gerektirir ve performans üzerinde etkisi olabilir.
  4. call memset: Bu komut, yerel depolama alanını temizlemek için memset işlevini çağırır. Bu işlem, büyük bir bellek bölgesini temizlemek için döngüsüz bir işlem yapar, bu nedenle performans açısından maliyetli olabilir.

Bu farklar, ikinci fonksiyonun daha fazla hafıza ve işlemci kaynağı kullanabileceği anlamına gelir. Büyük bir stack frame oluşturmak ve büyük bellek bölgelerini temizlemek, performansı etkileyebilir ve programın daha yavaş çalışmasına neden olabilir. Özellikle bu işlemler sık sık çağrılıyorsa veya büyük veri yapıları ile çalışılıyorsa, bu performans maliyeti daha da belirgin olabilir.

İşte bu tür durumlarda kodun optimize edilmesi ve hafıza yönetiminin dikkatli bir şekilde ele alınması önemlidir. Çünkü gereksiz büyük yerel depolama alanları ve gereksiz bellek temizleme işlemleri, programın performansını olumsuz etkileyebilir.

Hafıza Kullanım Akışı Diyagramı

Aşağıya tanımlamış olduğunuz verilerin dağılımı ve kullanımı ile ilgili bilgilendirici görseller ekliyorum. 

updateGlobalInstance Hafıza Kullanım Akış Diagramı
updateGlobalInstance Hafıza Kullanım Akış Diagramı
updateLocalInstance Hafıza Kullanım Akış Diagramı
updateLocalInstance Hafıza Kullanım Akış Diagramı
Hafıza İçerisinde Verilerin Dağılımı
Hafıza İçerisinde Verilerin Dağılımı

Performans ve Hafıza Yönetimi

Bu iki fonksiyon ve onların assembly dili çıktıları, global ve yerel değişken kullanımının hafıza üzerinde nasıl farklılık gösterdiğini açıkça ortaya koyar. updateGlobalInstance fonksiyonu, sınıfın global durumunu güncellerken, updateLocalInstance fonksiyonu, her çağrıldığında büyük bir yerel yapı oluşturur ve bu, hafıza üzerinde önemli bir yük oluşturabilir.

  • Global Değişken Kullanımı: updateGlobalInstance fonksiyonu, sınıfın zaten ayrılmış olan global bir örneğini günceller. Bu işlem, ek hafıza ayırma gerektirmez ve performans üzerinde minimal etkiye sahiptir. Ancak, global durumu dikkatli bir şekilde yönetmek önemlidir.
  • Yerel Değişken Kullanımı: updateLocalInstance fonksiyonu, her çağrıldığında yığın üzerinde yeni bir LargeStruct örneği oluşturur. Büyük yapılar için bu, önemli bir hafıza ve zaman maliyeti oluşturabilir. Fonksiyon sona erdiğinde, yerel değişkenler otomatik olarak temizlenir, bu da kodun temiz ve yan etkisiz olmasını sağlar.

Sonuç

C++’da global ve yerel değişken kullanımının, hafıza yönetimi ve performans üzerinde önemli etkileri vardır. Yazılım geliştiriciler, bu etkileri dikkate alarak, uygulamalarının gereksinimlerine en uygun çözümü seçmelidir. Aynı zamanda fonksiyonlarda çok büyük yapı ve sınıfları bünyesine parametre olarak almamalıdır.

Aşağıda Stack ve Heap hafıza alanlarını anlamak için faydalı bir video bulabilirsiniz.

Hafıza Yapılarını Anlamak İçin Bir Eğitim İçeriği

Yayınlayan

Ahmet Yasin CİVAN

Mekatronik Mühendisi, Gömülü Yazılım Geliştiricisi.