Categories
Rehber Yazılım

Docker ile Sanal Ortam Oluşturun: Docker Nedir?

Docker, basit bir şekilde süper hızlı “sanal ortam” oluşturma teknolojisidir. Bu basitliğin ardında büyük olaylar bizi beklediği için sade bir giriş yapalım istedim. Hemen başlayalım.

dockeryazılım programının logosu

Sanallaştırma

Bir bilgisayar/sunucu içerisinde bulunan işletim sisteminin üzerinde sanal makine başlatılır.

Başlatılacak olan her sanal makine ana bilgisayarımızın fiziksel özelliklerinin bir kısmı ile beslenmeye başlar.

Her sanal makine içerisinde gerekli olan işletim sistemi bulunur. Bu sebeple makine dosyaları oldukça çok fazla yük içerir.

Makine sayısı arttıkça, fiziksel makinenin özellikleri hızla sanal makineler tarafından tüketilir.

Oluşturulan makinelerin fiziksel bilgisayardan alınacak kaynakların esnek yapıda seçilmemesi proje gereksinimlerinin değişiminde büyük sıkıntılar ortaya çıkarır.

Bu sebeplerle sanal makineler çeviklik açısından başa bela bir hale gelirler.

Konteynerleştirme

Bu terim yazılım dünyası için aslında yeni değildir. Docker sayesinde bu teknoloji son kullanıcının kullanımına çok kolay bir şekilde sunulmuştur.

Bu teknolojide amaç fiziksel makinemizin çekirdeğini direkt kullanmak.

Yeni işletim sistemlerinin tüm gereksinimleri yerine amaçlanan sistem belirlenir ve halihazırda asıl çekirdek üzerine bu sistem kurulumu gerçekleştirilir.

Bu sayede işlem ve yer maliyeti düşük, kolay taşınabilir makineler oluşturulur.

Docker

Docker‘ın sitesinden sisteminize uygun kurulumu ile ilgili detaylı bilgiyi alabilirsiniz.

Docker’ın sanallaştırmanın yanı sıra neler sunduğunu işleyişlerinden yola çıkarak anlatmaya çalıştık ancak yapabileceklerimizin çok daha fazlası bizleri bekliyor.

Şimdi Docker’ın “Image” kısmını ele alalım.

Konteynerlerin içerisinde çalışacak düşük kapasiteli yüksek erişebilirlik sağlayan sistem görüntüleri. Bu görüntüler nginx den mysql e her türlü paketi bulundurabilir.

Bu paketleri kendi işleriniz için özel olarak oluşturabilir, şirket içi ya da topluluk için özel geliştirmeler yapabilirsiniz.

Bu sayede oluşturduğunuz görüntüyü insanlar projelerinde kullanabilir.

Düşük dosya boyutları sayesinde dakikalar içerisinde uğraştırıcı bir çok ortamın kurulumu kolayca sağlanır.

Sistemimizde olan görüntüleri aşağıdaki şekilde görebiliriz. Şuan sistemimizde herhangi bir görüntü yok.

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE

Deneme amaçlı bir mysql görüntüsü çalıştıralım. Docker görüntünün olmadığını gördüğü zaman önce görüntüyü alacak, kurulumunu yapıp hizmetimize sunacaktır.

Unable to find image 'mysql:latest' locally
latest: Pulling from library/mysql
a076a628af6f: Pull complete                                                                                                                                                 f6c208f3f991: Pull complete                                                                                                                                                 88a9455a9165: Pull complete                                                                                                                                                 406c9b8427c6: Pull complete                                                                                                                                                 7c88599c0b25: Pull complete                                                                                                                                                 25b5c6debdaf: Pull complete                                                                                                                                                 43a5816f1617: Pull complete                                                                                                                                                 69dd1fbf9190: Pull complete                                                                                                                                                 5346a60dcee8: Pull complete                                                                                                                                                 ef28da371fc9: Pull complete                                                                                                                                                 fd04d935b852: Pull complete                                                                                                                                                 050c49742ea2: Pull complete                                                                                                                                                 Digest: sha256:--
Status: Downloaded newer image for mysql:latest
[Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.22-1debian10 started.
[Note] [Entrypoint]: Switching to dedicated user 'mysql'
[Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.22-1debian10 started.
[ERROR] [Entrypoint]: Database is uninitialized and password option is not specified
        You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD

Aynı komutu tekrar çalıştıralım.

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
mysql               latest              d4c3cafb11d5        25 hours ago        545MB

Artık görüntü yerel makinemizde hazır. İstediğimiz zaman bu görüntü ile yeni bir konteyner oluşturup birden fazla mysql sunucusu elde edebiliriz.

Peki Docker bu görüntüyü nereden aldı? Tabiki de kendi “image” havuzu Docker Hub’dan (docker farklı kaynaklardan da görüntü alabilir).

Bu sistem içerisinden işinize yarayan herhangi bir görüntüyü yerelinize almak için aşağıdaki komutu kullanabilirsiniz.

docker pull blabla

Etiketler bir çok depo(repository) alanında olduğu gibi Docker görüntülerinde de versiyonlama için kullanılır. Yapısı bu şekildedir;

<görüntü-adı>:<etiket>

Yukarıdaki mysql örneğimizi ele aldığımızda depodan çekilecek olan görüntü mysql görüntünün versiyon adı ise latest dır. Eğer mysql in farklı bir sürümünü yüklemek istiyorsak aşağıdaki gibi bir komut kullanmalıyız.

docker pull mysql:5.7.32

Docker Hub içerisinde herhangi bir görüntü deposuna girdiğimizde “Description”, “Reviews” ve “Tags” şeklinde 3 farklı tab görürüz.

Tags” tab i içerisine girdiğimizde ilgili deponun oluşturulmuş tüm versiyonlarını görebilir nasıl erişebileceğimize dair bilgileri edinebiliriz.

Docker çalışma sistemi ve belkemiği konteyner sistemidir.

Konteynerler çok hızlı bir şekilde ayağa kaldırılıp kullanıma hazır hale getirilir.

Bir uygulama için birden fazla uygulama/veri tabanı konteynerleri oluşturulup aynı anda çalışması ve yükü eşit bir şekilde paylaşacak şekilde yapılandırılmaları mümkündür.

Hatta bu sistemleri ek uğraş gerektirmeden otomatik olarak yapan sistemlerde geliştirilmiştir.

Bir görüntü seçilip docker run komutu kullanıldığında otomatik olarak konteyner oluşturulmaya başlanır. Yine mysql örneği ile gösterelim;

$ docker run --name mysqltest -e MYSQL_ROOT_PASSWORD=docker-test -d mysql:latest

Şimdi biraz inceleyelim ilk yaptığımız run komutundan farklı parametreler var.

— name mysqltest  konteyner adımız

-e MYSQL_ROOT_PASSWORD konteyner a gömmek istediğimiz ortam değişkeni

-d bağımsız(detached) modda konteyner başlar, komut girildikten sonra komut istemine geri döneriz, docker arkada oluşturma işlemine başlar, bizi engellemez.

mysql:latest kurmasını istediğimiz görüntünün adı

Docker konteynerlerini aşağıdaki komut ile listeyebiliriz;

$ docker container ls
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                 NAMES
2fa80a5b07c6        mysql:latest        "docker-entrypoint.s…"   26 minutes ago      Up 26 minutes       3306/tcp, 33060/tcp   mysqltest

Oluşturduğumuz konteyner ın özelliklerini ve durumunu görüntüleyebiliyoruz. Konteyner ı durdurmak için aşağıdaki komut kullanılabilir.

$ docker container stop 2fa80a5b07c6

stop ibaresinden sonraki alan konternerimizin id si. İstersek ismi ile de aynı işlemi gerçekleştirebiliriz.

Şimdi ise konteyner silmek için aşağıdaki komutu kullanalım

$ docker container rm mysqltest

Bu şekilde Docker da konteyner oluşturma durdurma ve silme işlemlerinin nasıl yapıldığını incelemiş olduk.

Bu dosyalar var olan görüntüleri özelleştirerek projemize özel yeni görüntüler elde etmemizi sağlıyor. İçerisine gireceğimiz komutlar ile Docker’ın konteyner oluşturma aşamasında ne tür adımlar izleyeceğini belirliyoruz. Docker vereceğimiz tüm işleri bizim yerimize istediğimiz kadar konteyner içerisinde yapıyor. Şimdi bir tane oluşturalım;

FROM node:14.15.4                                                                                                                                                           RUN mkdir /nodetest                                                                                                                                                         WORKDIR /nodetest                                                                                                                                                           ADD . /nodetest                                                                                                                                                             CMD node index.js

FROM oluşturulacak konteynerin kullanacağı görünütüyü belirtir.

RUN konteynerin çalıştıracağı komut burada bir klasör oluşturduk.

WORKDIR konteynerin komutlarımızı nerede çalıştıracağını belirttik.

ADD konteynerin içerisine aktarılacak fiziksel makinemizdeki dizini gösterdik Dockerfile ın bulunduğu dizindeki herşey konteyner içerisinde bulunan nodetest klasörüne kopyalanacak.

CMD konteynerin işlemlerden sonra çalıştırmasını istediğimiz ilk ve asıl işlem.

Dockerfile ımızın yanına bir index.js açıyor ve içerisini aşağıdaki şekilde dolduruyoruz;

const https = require('https')                                                                                                                                              let req = https.request({ hostname: 'webhook.site', path: '/1d7a8108-ecfe-413d-a210-156a0fe3ad96', port: 443, method: 'GET' }, () => {});
req.end();

Webhook.site üzerinden bir webhook oluşturuyoruz ve istek atıyoruz.

Şimdi oluşturduğumuz Dockerfile üzerinden bir görüntü oluşturmak için aşağıdaki komutu kullanalım:

$ docker build ./
Sending build context to Docker daemon  3.072kB
Step 1/5 : FROM node:14.15.4
 ---> 995ff80c793e
Step 2/5 : RUN mkdir /nodetest
 ---> Running in 37739a546f10
Removing intermediate container 37739a546f10
 ---> f5f56db2746d
Step 3/5 : WORKDIR /nodetest
 ---> Running in ae357849a077
Removing intermediate container ae357849a077
 ---> ae2f76d3ad04
Step 4/5 : ADD . /nodetest
 ---> 58b31c30cbcf
Step 5/5 : CMD node index.js
 ---> Running in 5bd9c578f13c
Removing intermediate container 5bd9c578f13c
 ---> e540f9d9eea3
Successfully built e540f9d9eea3

Bulunduğumuz dizini göstererek “e540f9d9eea3” idsine sahip bir görüntü oluşturduk hemen bunu inceleyelim:

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
<none>              <none>              e540f9d9eea3        24 seconds ago      942MB
node                14.15.4             995ff80c793e        27 hours ago        942MB
mysql               latest              d4c3cafb11d5        28 hours ago        545MB

İlk sırada bizim oluşturduğumuz görüntü geldi. Artık konteynerimizi başlatabiliriz.

$ docker run e540f9d9eea3

Bu komut ile konteyner başladığı anda Webhook.site adresinde istek hemen karşımıza geliyor.

Node konternerimiz ayağa kalktı tüm ortam yüklendi, index.js dosyamız alındı ve sonrasında talimatlarımıza göre çalıştırıldı.

Image for post

Image for post

İşlem başarıyla tamamlandı. Artık bu görüntü ile istediğiniz her şeyi yapabilirsiniz.

Yazımızda “Docker Nedir?”, “Hangi Teknolojiyi Kullanır?”, “Konteynerleştime(Containerization) Nedir?”, “Docker Image Nedir Nasıl Çalışır?”, “Docker Konyetner Nasıl Çalışır?”, “Dockerfile Nedir ve Nasıl Çalışır?” ve “Docker Image Nasıl Oluşturulur?” gibi pek çok soruya değinmeye ve cevap vermeye çalıştık.

 

Sonuna kadar okuduğunuz için teşekkür ederim. Umarım sizler için faydalı bir yazı olmuştur ve aklınızda soru işaretlerini bir nebze olsun azaltmıştır.

Categories
Rehber Web Geliştirme

Tensorflow NLP: Doğal Dil İşleme ve Kelime Tahmini

Yapay zeka sistemlerinin iniş ve çıkış dönemleri olduğunu artık hepimiz biliyoruz. Teknoloji geliştikçe yapay zeka alanına yapılan yatırım farklı evrelere dönüşerek bir anda karşımıza çıkıyor.

Artık hayatımızın her alanında irili ufaklı yapay zeka sistemlerinin çıkmasını bu evrelere borçluyuz. 

Sosyal mecralarda teknoloji ile ilgilenen insanları takip ediyorsanız akışınızda kesinlikle yapay zeka ile ilgili içerikler çıkmıştır.

Genellikle basit yüz tanıma, maske kontrolü, cisim tanıma ve yaş tahmini gibi işlerin yoğun olduğunu görebiliriz. Bu işleri yapmanın artık zor bir yolu -tabi isterseniz- yok.

Artık her yapay zeka aracı, bahsi geçen tüm işleri yapabilecek modüller içeriyor.

Bizim bu yazımızda kullanacağımız kütüphane Tensorflow. Bu aşamada amaç, sizlere kurulum ya da başlangıç öğretmek değil, bir dil işleyebilen ağ oluşturmak olduğunu belirtelim.

Tensorflow ile Doğal Dil İşleme

“Tensorflow Nedir?”, “Neural Network Nasıl Çalışır?” gibi sorularınız için farklı kaynaklar bulmanız gerekecektir. Buradaki amacımız net olarak “Tensorflow ile doğal dil işleme” dir. 

Kodlarımızı Google Colab üzerinden yazacağımız için kendi makinenizi yormaya ya da kurulumları yapmaya ihtiyacınız yok.

Colab üzerinden yeni bir Notebook oluşturalım. Açılan sayfada menüden “Çalışma zamanı” sekmesine gelip “Çalışma zamanı türünü değiştir”’e tıklayalım.

çalışma zamanı seçeneğinden çalışma zamanı türünü değiştir butonunu işaret eden bir ekran görüntüsütürünü

Açılan popup içerisinde “Donanım hızlandırıcı” olarak “GPU” seçeneğini seçelim. Bu işimizi hızlandıracaktır.not defteri ayarlarından bir düzeltme göstermek için alınan bir ekran görüntüsü

Genel bilgileri vermeyeceğimizi söylemiştik ancak yazacağımız her satır kodun ve kullanacağımız kütüphanelerdeki alt modüllerin neden import edildiğini, ne için kullanılacağını açıklayacağım.

import tensorflow as tf

Öncelikle “Tensorflow” import ediyoruz. Versiyon öğrenmek için aşağıdaki komutu kullanabiliriz. 

print(tf.__version__)

Çıktımız bu şekilde görünecektir.

tensorflow kod görüntüsü

Bizim yapacağımız proje, vereceğimiz metin ya da kelime üzerinden sonraki kelimeninin ne olacağını tahmin eden bir proje olacak. Gerekli kütüphanelerimizi eklemeye başlayalım.


import tensorflow as tf

from tensorflow.keras.preprocessing.text import Tokenizer

from tensorflow.keras.preprocessing.sequence import pad_sequences

from tensorflow.keras.models import Sequential

import numpy as np

İlk satırda Tensorflow ‘u tf adıyla ekliyoruz.

İkinci satırda vereceğimiz verileri kelime olarak parçalara yani tokenlara ayırarak indexlenmiş halde vermesi için “Tokenizer” modülünü içeri alıyoruz.

Üçüncü satırda eklediğimiz “pad_sequences” modülü sinir ağımıza verileri sabit matrix uzunlukları ile verebilmemizi sağlayacak.

Dördüncü satırda bulunan “Sequential” modülü sinir ağımızın oluşturulacağı modül yani modelimiz bu modül tarafından üretilecek.

Son satırda ise bolca kullandığımız numpy kütüphanesini ekliyoruz. Çeşitli array manipülasyonları için kullanacağız. 

Kendimize bir metin bulacağız. İster şiir ister bir imdb yorumu isterseniz de kaggle.com’dan bir veri seti kullanabilirsiniz. Ben imdb üzerinden birkaç yorumu ekleyerek ilerleyeceğim. Bunu bir text değişkenine atıyorum, tüm karakterleri küçültüp satır satır kesiyoruz.  

text = """

----

"""

lines = text.lower().split("\n")

Şimdi tokenlarımızı oluşturma zamanı içeri aldığımız “Tokenizer” modülünden bir tane türetip “fit_on_texts” fonksiyonuna “lines” arrayimizi gönderiyoruz. Bu sayede tokenlarımızı oluşturuyoruz. Oluşan tokenların sayısını bir değişkene atıyoruz modelimizi oluştururken işimize yarayacak. “input_sequences” objemiz birazdan manipülasyona uğratacağımız, modelimize girdi olacak tokenlar içerecek.

tokenizer = Tokenizer()

tokenizer.fit_on_texts(lines)

total_words = len(tokenizer.word_index) + 1

input_sequences = []

Şimdi “input_sequences” ın içerisini dolduracağız. Buradaki amaç her bir kelimeden sonra gelecek olan kelime için yeni bir varyasyon, yani bir veri oluşturmak. Kod harici örnek vermek gerekirse “güç: 1, tuşuna: 2, entegre: 3, şekilde: 4” şeklinde oluşan bir token indexin ana verimize eklenmesi bu şekilde olacaktır.

[1 2]            // güç tuşuna

[1 2 3]         // güç tuşuna entegre

[1 2 3 4]      // güç tuşuna entegre şekilde

for line in sliced_lines:

    token_list = tokenizer.texts_to_sequences([line])[0]

    for i in range(1, len(token_list)):

        tokens = token_list[:i + 1]

        input_sequences.append(tokens)

Hemen projemizdeki çıktısına da bir bakalım.tensorflow kod görüntüsü

Şimdi “input_sequences” da bulunan en uzun satırın değerini alıp “maxlen” içerisinde atıyoruz. “pad_sequences” için padding değişkeni oluşturduk ve “pre” ataması yaptık. Yani “maxlen” den küçük olan her satırın ön kısmına bir tampon oluşturulacak.

Eğer sonuna isteseydik “padding” değerini “post” olarak atayacaktık. Dördüncü satırda verimizdeki her satırın son kısmını kesiyor ve “xs” değişkenine atıyoruz.

Son kısımları ise “labels” değişkenine. Bu sayede “xs” de predict edilecek bir kelimenin sonucu labels a düşmüş olacak.

Beşinci satırda ise “labels” değişkeni içerisinde bulunan verileri modele output olarak verebilmek amacı ile “tf.keras.utils.to_categorical” fonksiyonunu kullanıyoruz.

“num_classes” parametresi ile verimizde kaç farklı kelime olabileceği ile ilgili bilgiyi veriyoruz.

maxlen = max([len(x) for x in input_sequences])

padding = "pre"

input_sequences = np.array(pad_sequences(input_sequences, padding=padding, maxlen=maxlen))

xs, labels = input_sequences[:,:-1], input_sequences[:,-1]

ys = tf.keras.utils.to_categorical(labels, num_classes=total_words)

Bu durumda “preprocessing” aşamamız tamamlanmış oluyor şimdi sırada modelimizi oluşturmak ve eğitmek var.

Modelimizi oluşturuyor ve üç adet katman ekliyoruz. İlk katmanımız “Embedding”. Bu katman kelimelerimize bir vektörleme sağlıyor.

Kelimelerimize uzayda birer konum atıyoruz. Bu katman yerine GlobalMaxPooling1D katmanı kullanmamız da mümkün.

İçerisindeki parametrelere baktığımızda ilk parametre “dimension” yani kelimelerimizin bağlantıları ikinci parametremiz “total_words” yani toplamda uzaya yerleştirilecek kelime miktarı ve son olarak “input_length”, adından da anlaşılacağı üzere katmana girdi yapabilecek verinin uzunluğu.

Bidirectional katmanımız kısaca LSTM in verileri çift yönlü olarak hem normal hem tersten okuyarak tahmini kolaylaştırmasıdır. LSTM ler dil işlemede doğan sorunları ele alarak RNN ler için özellikle geliştirilmiş fonksiyonlardır. Detaylı bilgiye bu yazımızda yer veremeyeceğiz.

Son olarak çıkış katmanımız bulunuyor. Aktivasyonların detaylarına burada değinmeyeceğim. Modelimizi çalıştırıyoruz. “compile” içerisinde bulunan “loss” hariç diğer parametreler oluşturduğunuz yapıya göre ayarlanabilir durumdalar.

“loss” ise bu durumda bize bizim görmek isteyeceğimiz “categorical_crossentropy” fonksiyonu. “metrics” ise “fit” fonksiyonu sırasında modelin eğitim sürecinde görmek ve incelemek istediğiniz parametreler.

model = Sequential([

    tf.keras.layers.Embedding(total_words, 100, input_length=maxlen - 1),

    tf.keras.layers.Bidirectional(tf.keras.layers.LSTM(100)),

    tf.keras.layers.Dense(total_words, activation="softmax")

])

model.compile(loss="categorical_crossentropy", optimizer=tf.keras.optimizers.Adam(learning_rate=0.06), metrics=["accuracy"])

history = model.fit(xs, ys, epochs=30, verbose=1)

Verilerimiz ve modelimiz hazır. Şimdi sırada tahmin var.  Bir değişken oluşturuyor ve metnin bir kısmını giriyoruz.

Preprocessing aşamasında yaptığımız gibi tokenizer ile sequences larını oluşturuyoruz ve padding yapıyoruz.

Modelimize predict ettiriyoruz ve çıktıdan gelecek olan değeri tokenizer ile oluşturduğumuz index havuzunda eşleştiriyoruz ve sonraki kelimeyi buluyoruz.

seed = "every child who I have seen watch"

token_list = tokenizer.texts_to_sequences([seed])[0]

token_list = pad_sequences([token_list], maxlen=maxlen-1, padding=padding)

predicted = np.argmax(model.predict(token_list), axis=-1)

output_word = ""

for word, index in tokenizer.word_index.items():

    if index == predicted:

        output_word = word

        break

print(output_word)

Yukarıda yapılan geliştirmeyi bir loop a alabilir output_word ü seed e ekleyerek sürekli bir sonraki kelimeyi tahmin edebilir hale getirebilirsiniz.

Tensorflow doğal dil işleme ve kelime tahmini yazımızın sonuna geldik. İlgili kodların bulunduğu Notebook u aşağıda paylaşacağım.

Bu yazının üzerine devam edip geliştirmeler yapmak istiyorsanız bir sonraki aşama olarak Tensorflow un sitesinde bulunan şiir yazma tutorial ı ile devam edebilir.

Yeni kelimelerin oluşturulmasının nasıl yapıldığını farklı ve detaylı bir şekilde inceleyebilirsiniz. 

https://colab.research.google.com/drive/1fuIn7U4yS77xwg4GggkHv1FrrCrkvWKf

https://www.tensorflow.org/tutorials/text/text_generation

Categories
Oyun Geliştirme Rehber

Unity Kod Optimizasyonu ile RAM’e Hükmedin

Selamlar, bu yazımızda Unity kod optimizasyonu hakkında detaylı bilgi vereceğiz. Çeşitli durumlar için geçerli pek çok optimizasyon çeşidi bulunuyor.

Object Pooling

Nesne Birleştirme ve Nesne Oluşturma olarakta geçiyor. Burada amaç en başta, kullanılacak nesneleri oluşturmak ve yok etmeden tekrar kullanmak. Çoğu zaman yapılan en büyük yanlışlardan biri bu oluyor. Prefab kullanılacağı zaman ‘Instantiate’ metodu ile bir kopyası oluşturuluyor. Objenin işi bittikten sonra ise tamamen yok ediliyor. Bu işlemler tek seferlik yapıldığı zaman pek sorun gibi durmasada tekrar edildiğinde çok fazla bellek kullanımına yol açıyor. 

Bu aşamada ‘Object Pooling’ devreye giriyor. Amacını yukarıda özetlemiştik. Şimdi ise biraz detaylandıralım ve örneklemelere geçelim.

Eskiden bir ‘Shooter’ oyunu yaptıysanız silahların mermilerini her seferinde yeniden klonlar ve işleri bittiğinde ise onları yok ederdiniz. Object Pooling ile kullanılacak mermileri sahne başlangıcında oluşturuyoruz ve aktifliklerini kapatıyoruz. Artık bir mermi havuzumuz var tıpkı şarjör gibi. Silah her ateşlendiğinde mermi klonlamak yerine havuzda bulunan bir mermi çekilir, pozisyonu değiştirilir ve aktif hale getirilir. İşi bittiğinde ise pasif hale getirilir. Bu sayede objeler sadece başlangıçta oluşturulur ve asla yok edilmez.

Örnek göstermeye başlayalım. Öncelikle objelerin zaten sahnede olduğu durumu canlandırıyoruz. Kullanılacak objelerimizin hepsi başta sahnemizde yer alıyor.

unity 3d agent spawn point

Bu nesnelerimizin sadece aktiflikleri kapalı.

unity 3d agent inspector

Tüm objeleri yine manuel olarak scriptimizde bulunan listeye atayalım.

spawn script component unity kod optimizasyonu

Spawn işlemi için bir coroutine oluşturuyor ve başlatıyoruz.

unity kod optimizasyonu coroutine kod blogu

Objemizi LinQ ile aktiflik durumuna göre çekiyoruz (Performans bakımından LinQ kullanmanız önerilmez). Null kontrol yapıyor, aktifliğini açıyoruz ve sonrasında kullanımdan kalan velocitysini sıfırlıyoruz.

unity kod optimizasyonu enumerator kod blogu

Object Pooling genel olarak bu şekilde kullanılıyor. Objelerin tekrar kullanılmak üzere  sahnede saklanması sizlere çok fazla performans artışı sağlayacaktır.

Update vs Coroutine

Unity’de sürekli ya da belirli bir süre sonra yapılacak işlemler için Coroutineleri kullanabiliyoruz. Bazı durumlarda ise Update içerisine yazdığımız kodları Coroutine içerisine yazmak performans bakımından çok daha avantajlı bir hale gelebiliyor. Arada çok üst düzey bir fark var mı derseniz tepkimelere ve bellek kullanımlara bakıldığında üst düzey bir ayrım göremezsiniz. Ancak çoğu durumda Update kullanmanın gereksizliğini ortaya koyacak derecede esneklik sağlayan Coroutinelerin özellikleri güzel bir geliştirme avantajı da sunuyor. Gelin beraber bu ufak farklara bakalım.

Object Pooling de zaten bir Coroutine başlatmıştık. StartCoroutine fonksiyonu ile bir routine başlatabiliyoruz. Coroutinelerde iş akışını durdurmak istiyorsak aşağıdaki gibi bir kullanım yapabiliyoruz.

unity 3d wait kod bloğu

Bu kod satırı sayesinde, 5 saniyelik bir bekleme sonrasında kodlarımız çalışmaya devam ediyor.

İstersek başladığı karenin bitmesini beklemesini de söyleyebiliriz.

unity 3d wait for end of frame

While ile kullanarak Update gibi bir çağrı elde edebiliriz. 

unity 3d while wait for end of frame

İşte bu durumlarda Update yerine kullanmak avantajlı duruma getiriyor bizleri. Update içerisinde çeşitli conditionlar yazmak ya da farklı farklı fonksiyonları kullanılmasalar çağırarak her seferinde ek yüklerin ortaya çıkmasına neden oluyoruz. Birden fazla Coroutine oluşturarak her akışın kendine ait bekleme noktalarına sahip olmasını sağlayabiliriz. 

Her akışı farklı düzende kontrol edebiliriz. Tek bir akış oluşturup tekrar limitlerini belirleyebiliriz. Bunların çoğunu Update fonksiyonunda yapamıyoruz çünkü akışı rahat kontrol edemiyor, koşul mekanizmaları oluşturuyoruz. Mekanizmalar oluşturmak için çeşitli koşullar geliştirmemiz gerekiyor ve de kodların çalışmasını engellemek için bir yerden kesmek. Bu durumda ise Update çığrından çıkabiliyor.

String.Empty.

String oluştururken herhangi bir değer girilmediğinde “” tırnak kullanarak boş olarak tanımlarız. Ancak bu atamayı yaptığımızda string bellekte yerini alır ve nesne artık oluşturulmuştur. 

String.Empty kullanıldığında ise nesne empty alana referans verilir. Yani bellekte zaten olan bir alanı referans olarak gösterir. Bu sayede ekstra yer kaplamamış olur. 

String.Emptynin kullanımı String.IsNullOrEmpty fonksiyonunu da beraberinde getiriyor. Null veya uzunluğu 0 olan objeleri rahatlıklı kontrol etmek amacıyla kullanılan bir fonksiyondur. Her iki durumu da destekler.

String.Empty ile ilgili detaylı bilgiye .Net dokümanlarından erişebilirsiniz.

StringBuilder

String birleştirme işlemleri Unity ya da herhangi bir C# projenizde fazla yer alıyor, 2 – 3 den fazla string i birleştirmeye çalışıyorsanız, bu işlemleri artık ‘+’ operatörü ile yapmak yerine StringBuilder’ı kullanmaya başlayabilirsiniz. Performans bakımından gayet güzel bir etkiye sahip.

.Net dokümanına buraya tıklayarak erişebilirsiniz.

Caching Components

GetComponent fonksiyonu ile aldığımız Component’lar bir değişkene atarak tekrar kullanılabilir. GetComponent objeye erişmek için çok fazla güç harcıyor. Objenin bir referansını oluşturup sonrası için de kullanmak çok daha mantıklı.

unity kod optimizasyonu rigid body component

Finding Objects

Obje aramalarında en çok yapılan kullanılanlar GameObject.Find ve GameObject.FindWithTag fonksiyonları. Bu fonksiyonların kullanımı çok maliyetlidir ancak Find fonksiyonunun maliyeti tüm objeler için yaptığı aramadan dolayı daha fazladır. Tage göre arama çok daha az maliyetlidir. 

unity 3d finding objects

Bir obje aramak yerine direkt slota assign etmek yukarıda bulunan iki yöntemden çok daha kullanışlıdır.

unity kod optimizasyonu finding objects

Camera.Main

Bu fonksiyon yüksek maliyetli, aktif kamerayı getiren bir Unity API çağrısıdır. Finding Objects kısmında belirttiğimiz fonksiyonlardan pek bir farkı bulunmamaktadır. Hepsi bellekte bulunan tüm objeleri gezer. Bunları önlemek amacıyla bu fonksiyonların döndürdüklerini cachelemek gerekiyor ve bir daha kullanmamak gerekiyor.

Local Declare Garbage

Çalıştırılacak fonksiyonlar içerisinde kullanılan değişkenlerin tanımlamaları fonksiyon içerisinde yapılıyorsa değişken her seferinde belleğe yazılır eski adresine tanımlanmaz. Bu sebeple büyük bir şişme gerçekleşir. Performansı etkiler.

unity 3d update get component

Bu durumda her zaman farklı bir array oluşur. Bunun yerine aşağıdaki şekilde bir kullanım yapmamız çok daha iyi olacaktır.

unity 3d start get component

Unity’de hangi platform için çalışırsanız çalışın, Unity kod optimizasyonu uygulamanız sizlerin yararına olacaktır. Daha birçok optimizasyon çeşidi ve durumu bulunmaktadır. Bunları projelerinizde ihtiyaç doğrultusunda kullanabilir ve öğrenebilirsiniz.

Unity kod optimizasyonu yapmak için yeterli tecrübeniz ve bilgi birikiminiz yoksa, FastnTech olarak size yardımcı olabiliriz.