Neler yeni

Foruma hoş geldin, Ziyaretçi

Forum içeriğine ve tüm hizmetlerimize erişim sağlamak için foruma kayıt olmalı ya da giriş yapmalısınız. Foruma üye olmak tamamen ücretsizdir.

Türkiye'nin İlk ve tek FiveM forum adresi

Forum adresimize hoş geldin FiveMTürk olarak amacımız siz değerli kullanıcılarımıza en aktif fikir ve paylaşım platformu sunmak bir yana en güvenilir şekilde alışveriş yapabileceğiniz bir platform sunmaktır.
DF DF
DF DF
DF DF

Rehber ephesus'un optimizasyon defteri, part 1 - giriş

mehmetefeerkan

Üye
FT Kullanıcı
Katılım
3 yıl 3 ay 3 gün
Mesajlar
197
Discord
marlboroghini
Tam olarak bir rehber niteliği taşımadığını düşünmekle beraber, üzerinde çalıştığım farklı altyapılara sahip farklı paketlerde gerçekleştirdiğim optimizasyon çalışmalarının ardından topluluğa da yardımcı olmak bakımından çıkardığım dersleri burada paylaşmak istedim.


Eğer rehberim size uzun geldiyse sadece renkli yazıları okuyarak ta kısa sürede bilgi sahibi olabilirsiniz.

Basit Döngüler, ve Lua/JS/C# ile FiveM/GTAV Entegrasyonu, Native Performansları

Basidinden başlayalım:
Kod:
while true do
    --döngü içi
end

Bu ^ meretten tiksinenler var. Şimdi evet, döngülerden özellikle Lua gibi scripting dillerinde kaçınmak lazım. Ancak, önemli olan bu döngülerin nerede çağırıldığı değil, bu çağırılan döngülerde neyin çağırıldığıdır.
Şu noktada, binlerce liralara paket satan developerların bile bilmediği bir noktaya değinmek icab ediyor.

FiveM'de biz geliştiricilerin kullanımına sunulan Lua, JS, ve C# yazım stilleri sadece birer Wrapper'dan ibarettir, yani, FiveM'in çekirdeğindeki C++ koduyla, farklı dillerde iletişime geçmemizi sağlar.

Bunu, artifacts\citizen\scripting\lua\natives_server.lua dosyasında az çok anlıyoruz ve görüyoruz.
69704

Resimde gördüğünüz (return içerenler hariç, onları şimdi açıklayacağım) satırlar, docs.fivem.net/natives adresinden tanıdık geliyordur muhtemelen. Dediğim gibi: Örneğin, Lua dilinde bize "global" fonksiyon olarak sunulan GetPlayerWeaponDefenseModifier fonksiyonu esasında FiveM Çekirdeğindeki 0xf1543241 adresindeki C++ fonksiyonunu çağırıyor ve yanıtı bize iletiyor.
(FiveM çekirdeğinden bilmemne adresinden çağırılan fonksiyon da esasında GTAV'in kendi çekirdeğindeki başka bir adresi çağırmaktadır.)

Peki, neden lafı asla görmediğimiz ve umursamadığımız fonksiyon adreslerine ve citizen klasörlerinden falan filan getirdim? Olay şu:
  • GetDistanceBetweenCoords, DrawMarker, GetEntityCoords, PlayerPedId, IsControlJustReleased|Pressed gibi çokça (ve %90 döngülerde) kullandığımız bu fonksiyonlar bu Wrapper'larda adresleri çağırıyor ve bu fonksiyonlar esasında C++ gibi bir dilde bile yavaş çalışan fonksiyonlar.
  • GetDistanceBetweenCoords fonksiyonunda iki vec3 koordinat arasındaki sinüs tabanlı farkı,
  • DrawMarker fonksiyonunda komple bir grafik kütuphanesini ve çizim Util'lerini,
  • GetEntitiyCoords'da oyun motorunun renderladığı onbinlerce entity'lerden filtrelenmiş birilerinin relatif koordinatlarını,
  • IsControlJustReleased'de iki renderlanan kare arasındaki klavye girdi kaydını (Kare tabanlı Giriş-Çıkış kaydı oyun motorları için çok ağır bir şeydir)
  • PlayerPedId'de de sunucuya milyonlarca "handle" önce gönderilmiş ve acknowledge edilmiş bir Id'yi istiyoruz.

    Döngülerde kullanılan, bu saydıklarımdan daha da ağır (pahalı) fonksiyonlar da var, bunlar sadece bir kaçı.

    Kendiniz de gidip esx_ambulancejob gibi marker çizen temel bir scriptinizden bu fonksiyonların durduğu döngüleri silmeyi deneyebilirsiniz; eğer halihazırda scriptin optimizasyonu yoksa, scriptin resmon değeri yarıdan fazla düşecek, muhtemelen 0'ı bulacaktır.

    (esx_ambulancejob'unuz tek scriptse ve/veya gerçekten 0.01'den fazla resmon yiyorsa kendinizi gözden geçirmenizi tavsiye ederim bu arada...)

    Kısacası: Native Çağırmak PAHALI ve performans canavarı bir şey. Mümkün olduğunca (genelde olmuyor :) ) uzak durulması gerekmektedir.
Citizen Kütuphanesi, Thread'ler ve Garbage Collector

Diğer efsaneye gelelim:
Kod:
Citizen.CreateThread(function()
    --yeni thread
end)

Şimdi öncelikle sağlıklı yerlerde kullanıldığı ve saçma sapan döngüler içermediği sürece, CreateThread metodunun hiçbir zararı yoktur. Dahası, Lua gibi bir dille FiveM gibi bir platforma scripting yapabilmek için CreateThread kullanmak zorundayız, kaçarı yok.

Ancak, CreateThread'lerin içinde CreateThread çağırmamaya dikkat etmekte fayda var.
Çünkü Citizen kütuphanesi, bir resource'ta bulunan farklı thread'leri (Citizen.Wait(zaman) ikiden fazla defa çağırılmadıysa) senkron tutmak için büyük çaba sarfediyor.
Mutlaka başınıza gelmiştir: bazı sunucularda, ağ bağlantısının vs. çok iyi olmasına rağmen araç hasarının oyuncular arasında düzgün senkronize olmamasının sebeplerinden biri de CreateThread içinde CreateThread çağırılmış olmasıdır. (yine de, bildiğimiz NetworkFrame senkron sorunları (göz kayması) daha etkilidir bu konuda.)

CreateThread konusunda çok gözden kaçırılan ama çok kritik bir konuya daha değinmek istiyorum :
FiveM'in Lua motorunun çok başarılı bir çöp toplayıcısı mevcut.
Çoğu programlama dillerinde bulunan "garbage collector" yani çöp toplayıcı sistem, hafızada artık programın ihtiyaç duymadığı adresleri belirler ve bu adresleri boşaltır. Bu sayede, ne CPU boş adres bulmakla uğraşır, ne de RAM kullanımı kafayı yer.
Bu garbage collector zaten yazdığınız kodların çok büyük kısmında çalışıyor.
Hiç yanlışlıkla 10-20 saniyede gittikçe oyunu yavaşlattıktan sonra crash verdirten bir script yazdınız mı? Ben yazdım :D. Bu, bir "Memory Leak" yani RAM/Hafıza Sızıntısı anlamına gelir. Memory Leak, Garbage Collection'un programa yetişememesi ve dolayısıyla RAM'deki adreslerin boşaltılamaması durumudur.

Garbage Collector'ı daha da efektif kullanmak için, İşiniz bittikten sonra CreateThread'lerinizin dibinde return çekmeniz yeterlidir. Bu return, Citizen'in devamlı sürmesi gerektiğini düşündüğü iş parçacığı yani Thread ile işinizin bittiğini ifade eder. Garbage Collector belli bir süreden sonra bu return ile durdurulmuş thread'i, gömülü fonksiyonunu ve tanımlanmış içdeğişkenlerini hafızadan temizler.
Kısacası Citizen.CreateFunction'larla işiniz bittikten sonra (genelde döngüler için kullanıldığından bitmediği oluyor) return çekmek yararınıza olur. CreateThread içinde CreateThread koymak ta çoğu zaman iyi bir fikir olmuyor. Alternatif bir yaklaşım varsa o değerlendirilmelidir.



'Çalışma!' dedin mi çalışmıyor kardeşim!
Tabii, yukarıda Native referansı kullanılarak scripting yapılması planlanmış bir platformun kullanıcılarına "mümkün oldukça Native kullanmayın" diyen ben, şimdi de size "kullanabileceğiniz yerde kullanın" diyeceğim.
Neden mi?
Zehir gibi FPS öldüren bir döngü hayal edelim. İçinde tüm pahalı Native'lerimiz olsun.
Eğer bu döngüyü 'true' dışında başka bir koşula daha bağlarsak, bu döngünün içindeki kodlar ikinci koşulumuz true olana kadar katiyen ateşlenmiyor.
Dolayısıyla, çok inoptimize yazdığınız ama bir o kadar da sunucunuza koymak istediğiniz, yarısı GitHub'dan aşırma soygun scriptinizi, bir kenara atmanıza gerek yok. (şaka yapıyorum, alındıysan okumaya devam bile etme)

Velev ki herhangi bir marker çizmeksizin, ana desk'in önünde yazılacak bir komutla soygun başlatacak bir şekilde merkez bankası soygunu scripti yazıyorsunuz.
Aşağıdaki örnekteki şekilde ilerlerseniz soygunu başlatmayan/soygunla alakası olmayan oyuncular (müdahaleye gelen polisler dahil!) yoğun soygun dinamiklerinden minimum miktarda etkilenecektir.
Bu örnekte koskoca merkez bankasını tek bir kişinin soyduğunu, bir silaha dahi ihtiyaç duymadığını falan varsayıyorum, ama siz yine de ana fikri anlasanız yeter.

Kod:
RegisterCommand("bankayisoy", function()
    local playerPedId_ = PlayerPedId()
    if (GetDistanceBetweenCoords(deskKoordinatlar, GetEntityCoords(playerPedId_), true) < Config.maxDistFromDesk) then
        if playerData.job.name ~= "police" then
            initHeist(playerPedId_)
        end
    end
end)


function initHeist(plpeid)
    heistinprogress = true
    TriggerEvent("polisBildirim:soygun:merkezb", plpeid)
    --animasyonlar |  gerekirse döngü çağırılır while heistinprogress ile sadece soygun durumunda aktif tutulur.
    --silah dinamikleri | çoğu silah modificator fonksiyonu bir defa çağırılsa yetiyor.
    --markerlar | animasyonlarla aynı mantıkta renderlanabilir
    --tooltipler | NUI çağrıları vs.
    --zaman counter'ı | polislerin geliş süresi için bir geri sayım counter'ı olabilir; bir createthread'de sayar ve return ile biter (garbage collector :) )
    TriggerServerEvent("bankasoygunu:renderPropsAndNPCs") | spawnlanacak proplar ve diğer 3.şahıs tarafından görünür entitiy'lerin herkeste yüklenmesi gerekir.
end

(IDE'de değil burada elle yazdım kodu, sürç-ü syntax varsa affola :D)


Altyapı tabanlı ve toplu optimizasyon
altyapı altyapı ayrı ayrı kısımlarla çok daha büyük, detaylı ve sade bir part2 atmayı düşünüyorum bu rehber için. Şimdilik leak discordlarındaki #optimizasyon kanallarındaki "moruq optimizasoyn için mümqün olduunca az while kullan he" laflarına bel bağlayacağınıza burdan güzel güzel yararlanın. Ha bi de Lua değil JS Yazın ya. Daha hızlı. :3

ha, unutmadan, ukalalar için değil, bilmeyeni, öğrenmek isteyeni, ya da merak edeni için yazdım bu rehberi. "yaa git ondan bizde var abee" muhabbeti yapmayın. bu yazdıklarımı biliyorsanız tıpkı benim gibi işinize bakın. öğrenmek ve öğretmek isteyenlerin arasını toksiklemeyin.

Saygılar. ephesus#6324 |


 
Son düzenleme:
herşey çok detaylı ve güzel anlatılmış. Benimde bir kaç yeni öğrendiğim şeyler oldu, eline sağlık.
 
Tam olarak bir rehber niteliği taşımadığını düşünmekle beraber, üzerinde çalıştığım farklı altyapılara sahip farklı paketlerde gerçekleştirdiğim optimizasyon çalışmalarının ardından topluluğa da yardımcı olmak bakımından çıkardığım dersleri burada paylaşmak istedim.


Eğer rehberim size uzun geldiyse sadece renkli yazıları okuyarak ta kısa sürede bilgi sahibi olabilirsiniz.

Basit Döngüler, ve Lua/JS/C# ile FiveM/GTAV Entegrasyonu, Native Performansları

Basidinden başlayalım:
Kod:
while true do
    --döngü içi
end

Bu ^ meretten tiksinenler var. Şimdi evet, döngülerden özellikle Lua gibi scripting dillerinde kaçınmak lazım. Ancak, önemli olan bu döngülerin nerede çağırıldığı değil, bu çağırılan döngülerde neyin çağırıldığıdır.
Şu noktada, binlerce liralara paket satan developerların bile bilmediği bir noktaya değinmek icab ediyor.

FiveM'de biz geliştiricilerin kullanımına sunulan Lua, JS, ve C# yazım stilleri sadece birer Wrapper'dan ibarettir, yani, FiveM'in çekirdeğindeki C++ koduyla, farklı dillerde iletişime geçmemizi sağlar.

Bunu, artifacts\citizen\scripting\lua\natives_server.lua dosyasında az çok anlıyoruz ve görüyoruz.
Ekli dosyayı görüntüle 69704

Resimde gördüğünüz (return içerenler hariç, onları şimdi açıklayacağım) satırlar, docs.fivem.net/natives adresinden tanıdık geliyordur muhtemelen. Dediğim gibi: Örneğin, Lua dilinde bize "global" fonksiyon olarak sunulan GetPlayerWeaponDefenseModifier fonksiyonu esasında FiveM Çekirdeğindeki 0xf1543241 adresindeki C++ fonksiyonunu çağırıyor ve yanıtı bize iletiyor.
(FiveM çekirdeğinden bilmemne adresinden çağırılan fonksiyon da esasında GTAV'in kendi çekirdeğindeki başka bir adresi çağırmaktadır.)

Peki, neden lafı asla görmediğimiz ve umursamadığımız fonksiyon adreslerine ve citizen klasörlerinden falan filan getirdim? Olay şu:
  • GetDistanceBetweenCoords, DrawMarker, GetEntityCoords, PlayerPedId, IsControlJustReleased|Pressed gibi çokça (ve %90 döngülerde) kullandığımız bu fonksiyonlar bu Wrapper'larda adresleri çağırıyor ve bu fonksiyonlar esasında C++ gibi bir dilde bile yavaş çalışan fonksiyonlar.
  • GetDistanceBetweenCoords fonksiyonunda iki vec3 koordinat arasındaki sinüs tabanlı farkı,
  • DrawMarker fonksiyonunda komple bir grafik kütuphanesini ve çizim Util'lerini,
  • GetEntitiyCoords'da oyun motorunun renderladığı onbinlerce entity'lerden filtrelenmiş birilerinin relatif koordinatlarını,
  • IsControlJustReleased'de iki renderlanan kare arasındaki klavye girdi kaydını (Kare tabanlı Giriş-Çıkış kaydı oyun motorları için çok ağır bir şeydir)
  • PlayerPedId'de de sunucuya milyonlarca "handle" önce gönderilmiş ve acknowledge edilmiş bir Id'yi istiyoruz.

    Döngülerde kullanılan, bu saydıklarımdan daha da ağır (pahalı) fonksiyonlar da var, bunlar sadece bir kaçı.

    Kendiniz de gidip esx_ambulancejob gibi marker çizen temel bir scriptinizden bu fonksiyonların durduğu döngüleri silmeyi deneyebilirsiniz; eğer halihazırda scriptin optimizasyonu yoksa, scriptin resmon değeri yarıdan fazla düşecek, muhtemelen 0'ı bulacaktır.

    (esx_ambulancejob'unuz tek scriptse ve/veya gerçekten 0.01'den fazla resmon yiyorsa kendinizi gözden geçirmenizi tavsiye ederim bu arada...)

    Kısacası: Native Çağırmak PAHALI ve performans canavarı bir şey. Mümkün olduğunca (genelde olmuyor :) ) uzak durulması gerekmektedir.
Citizen Kütuphanesi, Thread'ler ve Garbage Collector

Diğer efsaneye gelelim:
Kod:
Citizen.CreateThread(function()
    --yeni thread
end)

Şimdi öncelikle sağlıklı yerlerde kullanıldığı ve saçma sapan döngüler içermediği sürece, CreateThread metodunun hiçbir zararı yoktur. Dahası, Lua gibi bir dille FiveM gibi bir platforma scripting yapabilmek için CreateThread kullanmak zorundayız, kaçarı yok.

Ancak, CreateThread'lerin içinde CreateThread çağırmamaya dikkat etmekte fayda var.
Çünkü Citizen kütuphanesi, bir resource'ta bulunan farklı thread'leri (Citizen.Wait(zaman) ikiden fazla defa çağırılmadıysa) senkron tutmak için büyük çaba sarfediyor.
Mutlaka başınıza gelmiştir: bazı sunucularda, ağ bağlantısının vs. çok iyi olmasına rağmen araç hasarının oyuncular arasında düzgün senkronize olmamasının sebeplerinden biri de CreateThread içinde CreateThread çağırılmış olmasıdır. (yine de, bildiğimiz NetworkFrame senkron sorunları (göz kayması) daha etkilidir bu konuda.)

CreateThread konusunda çok gözden kaçırılan ama çok kritik bir konuya daha değinmek istiyorum :
FiveM'in Lua motorunun çok başarılı bir çöp toplayıcısı mevcut.
Çoğu programlama dillerinde bulunan "garbage collector" yani çöp toplayıcı sistem, hafızada artık programın ihtiyaç duymadığı adresleri belirler ve bu adresleri boşaltır. Bu sayede, ne CPU boş adres bulmakla uğraşır, ne de RAM kullanımı kafayı yer.
Bu garbage collector zaten yazdığınız kodların çok büyük kısmında çalışıyor.
Hiç yanlışlıkla 10-20 saniyede gittikçe oyunu yavaşlattıktan sonra crash verdirten bir script yazdınız mı? Ben yazdım :D. Bu, bir "Memory Leak" yani RAM/Hafıza Sızıntısı anlamına gelir. Memory Leak, Garbage Collection'un programa yetişememesi ve dolayısıyla RAM'deki adreslerin boşaltılamaması durumudur.

Garbage Collector'ı daha da efektif kullanmak için, İşiniz bittikten sonra CreateThread'lerinizin dibinde return çekmeniz yeterlidir. Bu return, Citizen'in devamlı sürmesi gerektiğini düşündüğü iş parçacığı yani Thread ile işinizin bittiğini ifade eder. Garbage Collector belli bir süreden sonra bu return ile durdurulmuş thread'i, gömülü fonksiyonunu ve tanımlanmış içdeğişkenlerini hafızadan temizler.
Kısacası Citizen.CreateFunction'larla işiniz bittikten sonra (genelde döngüler için kullanıldığından bitmediği oluyor) return çekmek yararınıza olur. CreateThread içinde CreateThread koymak ta çoğu zaman iyi bir fikir olmuyor. Alternatif bir yaklaşım varsa o değerlendirilmelidir.



'Çalışma!' dedin mi çalışmıyor kardeşim!
Tabii, yukarıda Native referansı kullanılarak scripting yapılması planlanmış bir platformun kullanıcılarına "mümkün oldukça Native kullanmayın" diyen ben, şimdi de size "kullanabileceğiniz yerde kullanın" diyeceğim.
Neden mi?
Zehir gibi FPS öldüren bir döngü hayal edelim. İçinde tüm pahalı Native'lerimiz olsun.
Eğer bu döngüyü 'true' dışında başka bir koşula daha bağlarsak, bu döngünün içindeki kodlar ikinci koşulumuz true olana kadar katiyen ateşlenmiyor.
Dolayısıyla, çok inoptimize yazdığınız ama bir o kadar da sunucunuza koymak istediğiniz, yarısı GitHub'dan aşırma soygun scriptinizi, bir kenara atmanıza gerek yok. (şaka yapıyorum, alındıysan okumaya devam bile etme)

Velev ki herhangi bir marker çizmeksizin, ana desk'in önünde yazılacak bir komutla soygun başlatacak bir şekilde merkez bankası soygunu scripti yazıyorsunuz.
Aşağıdaki örnekteki şekilde ilerlerseniz soygunu başlatmayan/soygunla alakası olmayan oyuncular (müdahaleye gelen polisler dahil!) yoğun soygun dinamiklerinden minimum miktarda etkilenecektir.
Bu örnekte koskoca merkez bankasını tek bir kişinin soyduğunu, bir silaha dahi ihtiyaç duymadığını falan varsayıyorum, ama siz yine de ana fikri anlasanız yeter.

Kod:
RegisterCommand("bankayisoy", function()
    local playerPedId_ = PlayerPedId()
    if (GetDistanceBetweenCoords(deskKoordinatlar, GetEntityCoords(playerPedId_), true) < Config.maxDistFromDesk) then
        if playerData.job.name ~= "police" then
            initHeist(playerPedId_)
        end
    end
end)


function initHeist(plpeid)
    heistinprogress = true
    TriggerEvent("polisBildirim:soygun:merkezb", plpeid)
    --animasyonlar |  gerekirse döngü çağırılır while heistinprogress ile sadece soygun durumunda aktif tutulur.
    --silah dinamikleri | çoğu silah modificator fonksiyonu bir defa çağırılsa yetiyor.
    --markerlar | animasyonlarla aynı mantıkta renderlanabilir
    --tooltipler | NUI çağrıları vs.
    --zaman counter'ı | polislerin geliş süresi için bir geri sayım counter'ı olabilir; bir createthread'de sayar ve return ile biter (garbage collector :) )
    TriggerServerEvent("bankasoygunu:renderPropsAndNPCs") | spawnlanacak proplar ve diğer 3.şahıs tarafından görünür entitiy'lerin herkeste yüklenmesi gerekir.
end

(IDE'de değil burada elle yazdım kodu, sürç-ü syntax varsa affola :D)


Altyapı tabanlı ve toplu optimizasyon
altyapı altyapı ayrı ayrı kısımlarla çok daha büyük, detaylı ve sade bir part2 atmayı düşünüyorum bu rehber için. Şimdilik leak discordlarındaki #optimizasyon kanallarındaki "moruq optimizasoyn için mümqün olduunca az while kullan he" laflarına bel bağlayacağınıza burdan güzel güzel yararlanın. Ha bi de Lua değil JS Yazın ya. Daha hızlı. :3

ha, unutmadan, ukalalar için değil, bilmeyeni, öğrenmek isteyeni, ya da merak edeni için yazdım bu rehberi. "yaa git ondan bizde var abee" muhabbeti yapmayın. bu yazdıklarımı biliyorsanız tıpkı benim gibi işinize bakın. öğrenmek ve öğretmek isteyenlerin arasını toksiklemeyin.

Saygılar. ephesus#6324 |


+rep cennetten yazan biri için çok iyi olmuş umarım internetin kesilmez abi onun haricinde cidden kaliteli bir yazı olmuş.
 
Çok güzel olmuş bu işe yeni başlamak isteyip başlamadan önce herşeyini bilmem isteyen biri için, yani ben aradığım şey. Ama çom fazla teknik terim kullanmışsın sana burda hepsini açıkla demiyorum ama bu temel tekniği (en azından senin yazdıklarını anlıycak kadar) nerden öğrenebilirim daha doğrusu ne öğrenmem gerekir.
 

Forumdan daha fazla yararlanmak için giriş yapın yada üye olun!

Forumdan daha fazla yararlanmak için giriş yapın veya kayıt olun!

Kayıt ol

Forumda bir hesap oluşturmak tamamen ücretsizdir.

Şimdi kayıt ol
Giriş yap

Eğer bir hesabınız var ise lütfen giriş yapın

Giriş yap

Bu konuyu görüntüleyen kullanıcılar

Tema düzenleyici

Tema özelletirmeleri

Granit arka planlar

Lütfen Javascript'i etkinleştirin!Javascript'i etkinleştirin!