Unity Eğitim
Dersler
Forum Sınav Merkezi Premium 💎
C# Dersleri

Unity NativeArray ile Performanslı Veri Yönetimi

Paylaşan: Unity Eğitim 27 October 2025 7 dakika okuma 119 görüntülenme

Giriş: Performansın Anahtarı NativeArray

Unity oyun geliştirme sürecinde performans, özellikle büyük veri setleri veya yoğun hesaplamalar içeren senaryolarda kritik bir rol oynar. Geleneksel C# koleksiyonları (List, Array) esneklik sunsa da, Unity'nin çoklu iş parçacığı (multi-threading) ve Burst Compiler gibi performans odaklı sistemleriyle tam uyumlu çalışmayabilir. İşte tam da bu noktada Unity NativeArray devreye girer. Bu makalede, NativeArray veri yapısının ne olduğunu, neden bu kadar önemli olduğunu ve projelerinizde nasıl etkin bir şekilde kullanabileceğinizi detaylıca inceleyeceğiz.

NativeArray Nedir ve Neden Kullanmalısınız?

NativeArray, Unity'nin C# Job System ve Burst Compiler ile birlikte çalışmak üzere tasarlanmış, yönetilmeyen (unmanaged) bellekte yer alan bir değer tipi (value type) koleksiyonudur. Geleneksel C# koleksiyonlarının aksine, NativeArray doğrudan işletim sisteminin belleğini kullanır ve çöp toplama (garbage collection) işlemine tabi değildir. Bu özellikler, özellikle performans kritik uygulamalarda büyük avantajlar sağlar.

Neden NativeArray Kullanmalısınız?

  • Yüksek Performans: Yönetilmeyen bellek kullanımı sayesinde çöp toplama yükünü ortadan kaldırır. Bu, özellikle kare başına sık sık veri oluşturup yok ettiğiniz durumlarda takılmaları (stuttering) önler.
  • Job System Entegrasyonu: NativeArray, Unity'nin Job System'i ile tam uyumludur. Bu, verileri farklı iş parçacıkları arasında güvenli ve verimli bir şekilde paylaşmanızı sağlar, böylece CPU'nuzun tüm çekirdeklerinden faydalanabilirsiniz.
  • Burst Compiler Dostu: Burst Compiler, NativeArray gibi veri yapılarını kullanarak C# kodunuzu son derece optimize edilmiş makine koduna dönüştürebilir. Bu kombinasyon, inanılmaz hız artışları sağlayabilir.
  • Önbellek Yerelliği (Cache Locality): Veriler bellekte ardışık (contiguous) olarak depolandığı için CPU önbelleği daha verimli kullanılır. Bu, verilere erişim hızını önemli ölçüde artırır.

Unity NativeArray Kullanım Temelleri

NativeArray kullanmak, standart C# dizilerine benzerdir ancak bellek yönetimi konusunda bazı farklılıklar vardır.

Oluşturma ve Bellek Ayırma (Allocation)

Bir NativeArray oluştururken, boyutunu ve bellek ayırıcı (Allocator) tipini belirtmeniz gerekir. Allocator, Unity'ye belleği ne kadar süreyle saklaması gerektiğini söyler.

using Unity.Collections; // NativeArray için gerekli namespace

public class NativeArrayOrnegi : MonoBehaviour
{
    private NativeArray myNativeArray;

    void Start()
    {
        // 10 elemanlı bir NativeArray oluştur
        // Allocator.TempJob: Birkaç kare boyunca kullanılacak veriler için ideal
        myNativeArray = new NativeArray(10, Allocator.TempJob);

        // Değer atama
        for (int i = 0; i < myNativeArray.Length; i++)
        {
            myNativeArray[i] = i * 2;
        }

        // Değerleri okuma
        Debug.Log("İlk eleman: " + myNativeArray[0]); // Output: 0
        Debug.Log("Beşinci eleman: " + myNativeArray[4]); // Output: 8
    }

    void OnDestroy()
    {
        // NativeArray'i manuel olarak serbest bırakmak çok önemlidir!
        if (myNativeArray.IsCreated)
        {
            myNativeArray.Dispose();
        }
    }
}

Allocator Tipleri

Allocator, NativeArray'in bellek kullanım ömrünü belirler:

  • Allocator.Temp: Sadece geçerli kare veya çok kısa süreli işlemler için kullanılır. Aynı karede serbest bırakılması beklenir. En hızlı bellek ayırma tipidir.
  • Allocator.TempJob: Birkaç kare veya bir iş (Job) süresince kullanılacak veriler içindir. Genellikle bir iş tamamlandığında serbest bırakılır.
  • Allocator.Persistent: Uygulama ömrü boyunca veya uzun süreli kullanımlar için tasarlanmıştır. Manuel olarak Dispose() çağrılana kadar bellek serbest bırakılmaz. En yavaş bellek ayırma tipidir ancak en uzun ömürlüdür.

Doğru Allocator seçimi, bellek sızıntılarını önlemek ve performansı optimize etmek için kritik öneme sahiptir.

Pratik İpuçları

1. Job System ile Entegrasyon

NativeArray'in en büyük avantajlarından biri, Job System ile kusursuz entegrasyonudur. Verilerinizi NativeArray'de tutarak, bu verileri iş parçacıkları arasında güvenli ve hızlı bir şekilde aktarabilirsiniz.

using Unity.Collections;
using Unity.Jobs;
using UnityEngine;

public struct MyJob : IJob
{
    public NativeArray InputArray;
    public NativeArray OutputArray;

    public void Execute()
    {
        for (int i = 0; i < InputArray.Length; i++)
        {
            OutputArray[i] = InputArray[i] * 2;
        }
    }
}

public class JobSystemOrnegi : MonoBehaviour
{
    void Start()
    {
        NativeArray inputArray = new NativeArray(10, Allocator.TempJob);
        NativeArray outputArray = new NativeArray(10, Allocator.TempJob);

        for (int i = 0; i < inputArray.Length; i++)
        {
            inputArray[i] = i + 1;
        }

        MyJob job = new MyJob
        {
            InputArray = inputArray,
            OutputArray = outputArray
        };

        JobHandle handle = job.Schedule();
        handle.Complete(); // İşin bitmesini bekle

        for (int i = 0; i < outputArray.Length; i++)
        {
            Debug.Log("Output[" + i + "]: " + outputArray[i]);
        }

        inputArray.Dispose();
        outputArray.Dispose();
    }
}

2. Doğru Allocator Seçimi ve Dispose Yönetimi

Bellek sızıntılarını önlemek için her zaman NativeArray'i işiniz bittiğinde Dispose() etmeyi unutmayın. OnDestroy() veya uygun bir yaşam döngüsü metodunda bu işlemi yapmalısınız. Yanlış Allocator seçimi, gereksiz bellek kullanımına veya performans sorunlarına yol açabilir. Kısa süreli işlemler için Temp veya TempJob kullanın, uzun ömürlü veriler için Persistent'ı tercih edin.

3. NativeArray'i Standart Koleksiyonlara Dönüştürme

Bazen NativeArray'deki verileri standart C# koleksiyonlarına aktarmanız gerekebilir. Bunu ToArray() veya CopyTo() metodlarını kullanarak yapabilirsiniz.

NativeArray nativeFloats = new NativeArray(new float[] { 1.1f, 2.2f, 3.3f }, Allocator.Temp);
float[] standardArray = nativeFloats.ToArray(); // Yeni bir dizi oluşturur
List standardList = new List(nativeFloats); // Yeni bir liste oluşturur

nativeFloats.Dispose();

Yaygın Hatalar ve Çözümleri

  • Dispose() Unutmak: En yaygın hatadır. Unutulan NativeArray'ler bellek sızıntılarına yol açar ve Unity editöründe veya oyununuzda çökmelere neden olabilir. Çözüm: Her zaman NativeArray oluşturduğunuzda, onu ne zaman Dispose() edeceğinizi planlayın. Genellikle OnDestroy() veya işin tamamlandığı yerde yapılır.
  • Dispose() Sonrası Erişmeye Çalışmak: Serbest bırakılmış bir NativeArray'e erişmeye çalışmak çalışma zamanı hatasına (runtime error) neden olur. Çözüm: Erişmeden önce myNativeArray.IsCreated özelliğini kontrol edin.
  • Yanlış Allocator Kullanımı: Örneğin, tüm oyun boyunca kullanılacak bir veri için Allocator.Temp kullanmak, sürekli bellek ayırma ve serbest bırakma döngülerine neden olarak performansı düşürür. Çözüm: Bellek ömrüne uygun Allocator tipini seçin.
  • İş Parçacığı Güvenliği İhlalleri: Bir NativeArray'e aynı anda birden fazla iş parçacığından (Job) yazmaya çalışmak, veri bozulmasına yol açabilir. Çözüm: IJobParallelFor kullanırken her iş parçacığının kendi benzersiz indeksindeki verilere eriştiğinden emin olun. Kesişen yazma işlemleri için NativeQueue veya NativeStream gibi özel veri yapıları kullanın veya [NativeDisableContainerSafetyRestriction] niteliğini dikkatli kullanın (yalnızca gerçekten güvenli olduğundan eminseniz).

Performans ve Optimizasyon Notları

Unity NativeArray kullanarak elde edeceğiniz performans artışları, verilerinizi nasıl kullandığınıza bağlıdır.

  • Önbellek Yerelliği: NativeArray'deki veriler bellekte ardışık olarak depolandığından, CPU bir elemana eriştiğinde, bitişiğindeki elemanları da önbelleğe yükler. Bu, sonraki erişimlerin daha hızlı olmasını sağlar. Bu nedenle, verilerinizi işleme sıranız önemlidir.
  • Burst Compiler: Burst Compiler, NativeArray üzerinde işlem yapan kodları otomatik olarak optimize eder. Kodunuzu Job System ile birlikte kullanarak Burst'ün tam potansiyelinden yararlanın.
  • Çöp Toplama Yok: Yönetilmeyen bellek sayesinde, Unity'nin çöp toplayıcısının (Garbage Collector) duraklamalarından etkilenmezsiniz. Bu, özellikle VR/AR veya yüksek kare hızlı oyunlarda akıcı bir deneyim için çok önemlidir.
  • Veri Hizalaması (Data Alignment): NativeArray, verilerin bellekte doğru hizalanmasını sağlar. Bu, modern CPU'ların verileri daha verimli okumasına yardımcı olur.

Sonuç

Unity NativeArray, Unity'de yüksek performanslı ve güvenli veri yönetimi için vazgeçilmez bir araçtır. C# Job System ve Burst Compiler ile entegrasyonu sayesinde, CPU'nuzun tüm gücünü kullanarak karmaşık hesaplamaları ve büyük veri setlerini etkili bir şekilde işleyebilirsiniz. Başlangıçta bellek yönetimi (Dispose()) konusunda biraz dikkat gerektirse de, sunduğu performans avantajları bu çabaya fazlasıyla değer. Projelerinizde performans darboğazlarıyla karşılaşıyorsanız, NativeArray'i kullanmayı mutlaka değerlendirmelisiniz!

🧠 Ders Sonu Değerlendirme Testi

Dersi tamamladıktan sonra bilgilerinizi test edin ve ekstra puanlar kazanın.

🔥 +50 XP Ödül
🔒

Sınava Katılmak İçin Giriş Yapın

Bu ders sonu testini çözebilmek, bilginizi test edip **+50 XP** kazanmak ve **Sınav Şampiyonu** rozetinin kilidini açmak için üye girişi yapmalısınız.

Yorumlar (0)

Yorum yazabilmek ve derslere katkıda bulunabilmek için giriş yapmalısınız.

İlk yorumu siz yapın!