Yazılım mühendisliği dünyasında bazen en sinsi performans darboğazları, sistem tamamen çalışır durumda göründüğü ve hiçbir hata fırlatmadığı için gözden kaçar. Yerel geliştirme ortamınızda sekiz çekirdekli güçlü bir iş istasyonunda kusursuz bir şekilde saniyeler içinde tamamlanan büyük veri işleme döngüleri, üretim ortamındaki kısıtlı bir konteynere taşındığında gizemli bir şekilde yavaşlayabilir. Program çökméz, kayıtlar eksiksiz yazılır ve işlem nihayetinde biter; ancak harcanan süre iki katına, hatta üç katına çıkabilir. Bu durum, modern bulut native mimarilerde sıklıkla göz ardı edilen ve uygulama çalışma zamanının (runtime) temel donanım metriklerini nasıl algıladığıyla doğrudan ilgilidir.
Bozkurt Yazılım olarak, otomasyon araçları ve yüksek performanslı arka plan servisleri geliştirirken karşılaştığımız en kritik eşiklerden biri, kaynakların doğru yönetilmesidir. Özellikle tek çekirdekli veya sıkı bir şekilde sınırlandırılmış CPU kotalarına sahip konteynerlerde koşan paralel döngüler, yazılımcıların en büyük tuzaklarından biridir. Paralellik ile eşzamanlılık (concurrency) kavramlarının birbirine karıştırılması, CPU bağımlı (CPU-bound) ve G/Ç bağımlı (I/O-bound) iş yüklerinin yanlış yönetilmesi, sistemlerin potansiyelinin çok altında çalışmasına yol açar. Bu kapsamlı rehberde, tek çekirdekli konteynerlerin yarattığı gizli serileşme tuzaklarını, çalışma zamanı davranışlarını, alt seviye çekirdek dinamiklerini ve bu sorunları kökten çözmek için uygulayabileceğiniz modern mimari stratejileri ele alacağız.
Çalışma Zamanı ve Donanım Uyumsuzluğu: İşlemci Sayısı Nasıl Yanıltır?
Modern programlama dilleri ve çalışma zamanları (Runtime), performanslarını optimize etmek için genellikle donanımın çekirdek sayısını baz alır. Örneğin .NET, Go veya Java gibi ekosistemlerde, paralellik sunan yapılar arka planda işletim sisteminin sunduğu işlemci sayısını sorgular. Geleneksel sunucularda veya geliştirici makinelerinde bu sorgu, doğrudan fiziksel veya mantıksal işlemci sayısını döner. Ancak konteyner teknolojileri söz konusu olduğunda, bu mekanizma tamamen farklı bir boyuta evrilir.
Linux tabanlı cgroups (control groups) mekanizması, bir konteynere CPU tahsis ederken fiziksel donanımı doğrudan paylaşmak yerine belirli bir yüzde veya çekirdek sınırı uygular. Örneğin Docker üzerinde çalıştırılan bir konteynere –cpus=’1.0′ parametresi verildiğinde, işletim sistemi çekirdeği o konteyner içindeki işlemlere tek bir mantıksal işlemci tahsis eder. Çalışma zamanı başlatıldığında Environment.ProcessorCount veya eşdeğer API’ler bu sınırı okur ve maksimum paralellik derecesini buna göre ayarlar. Sonuç olarak, geliştiricinin kodunda yer alan Parallel.ForEachAsync veya benzeri yapılar, tek bir çekirdek sınırına takılarak pratik olarak tekli (serial) bir döngüye dönüşür.
Bu durumun en tehlikeli yanı, uygulamanın mantıksal olarak hata vermemesidir. Yüzlerce harici API çağrısı yapan veya veritabanı kaydı oluşturan bir batch işlemi, paralel kodlandığı için geliştirici tarafından aynı anda birden fazla işi yapıyormuş gibi hayal edilir. Oysa çalışma zamanı, tek çekirdek kısıtı nedeniyle bu işlemleri sıraya sokar ve ardışık olarak yürütür. Geliştirici, üretim ortamındaki yavaşlamayı ilk başta normal bir ağ gecikmesi veya harici servis yavaşlığı olarak yorumlar, ancak kök neden tamamen konteynerin CPU sınırlandırma mekanizmasıdır.

Eşzamanlılık (Concurrency) ile Paralellik (Parallelism) Arasındaki İnce Çizgi
Yazılım geliştiricilerin sıkça düştüğü temel kavramsal hata, eşzamanlılık ile paralelliği aynı şey sanmaktır. Paralellik, birden fazla görevin gerçekten aynı anda, fiziksel olarak farklı işlemci çekirdeklerinde çalıştırılmasıdır. Eşzamanlılık ise birden fazla görevin zaman dilimlerine bölünerek ilerletilmesidir; ortada tek bir çekirdek olsa bile, görevler arasında hızlı geçişler yapılarak aynı anda yürüyormuş izlenimi verilir. Tek çekirdekli bir konteynerde paralellik imkansızdır, ancak doğru tasarlandığında yüksek düzeyde eşzamanlılık elde etmek mümkündür.
Ağ istekleri, dosya okuma/yazma işlemleri veya üçüncü taraf API entegrasyonları gibi G/Ç bağımlı (I/O-bound) iş yüklerinde CPU’nun görevi oldukça azdır. İşlem, çoğunlukla karşı tarafın yanıt vermesini beklemekle (asenkron bekleme süresiyle) geçer. Bu bekleme süreleri esnasında CPU boştadır ve başka bir görevi işlemek için tamamen uygundur. Eğer kodunuz, işlemci çekirdek sayısına körü körüne bağlanır ve tek çekirdekli bir konteynerde MaxDegreeOfParallelism değerini 1 olarak kabul ederse, harici servisler yanıt verene kadar CPU tamamen boş bekler ve diğer kayıtlar işleme alınmaz.
Bozkurt Yazılım olarak geliştirdiğimiz sosyal medya otomasyon araçlarında ve entegrasyon servislerinde, ağ tabanlı işlemlerin yoğunluğu nedeniyle bu ayrıma özellikle dikkat ediyoruz. I/O ağırlıklı operasyonlarda çekirdek sınırını bir darboğaz olmaktan çıkarmak, uygulamanın verimliliğini doğrudan katlar. Kodunuzu tasarlarken, işlemcinin matematiksel hesaplama gücüne mi yoksa dış kaynakların yanıt süresine mi bağımlı olduğunuzu net bir şekilde ayırt etmeniz gerekir. CPU sınırları, I/O operasyonlarını kısıtlamak için asla birincil kriter olmamalıdır.
Küçük Kanunu (Little’s Law) ve Uçuşan İsteklerin Matematiksel Hesabı
Sistem mimarisinde kapasite planlaması yaparken John Little tarafından ortaya konulan ve kuyruk teorisinin temellerinden biri olan Küçük Kanunu (Little’s Law), konteyner içi performans optimizasyonlarında hayat kurtarıcı bir rol oynar. Bu kanun, sistemdeki ortalama öğe sayısının (items in flight / uçuşan işlemler), ortalama varış hızı ile bir öğenin sistemde geçirdiği ortalama sürenin çarpımına eşit olduğunu söyler. Formülsel olarak L = λ * W şeklinde ifade edilir.
Tek çekirdekli bir konteynerde varsayılan ayarlarla koşan bir döngü, aynı anda yalnızca tek bir öğenin uçuşmasına izin verir. Örneğin, her biri üç zenginleştirme (enrichment) API çağrısı ve bir veritabanı kayıt işleminden oluşan 200 kayıttan oluşan bir veri kümesini ele alalım. Her bir kayıt için toplam işlem süresi (ağ bekleme süreleri dahil) 400 milisaniye sürüyorsa, saniyede işlenen kayıt sayısı mantıksal olarak çok düşük seviyelerde kalacaktır. Tek bir çekirdek kısıtı, aynı anda yalnızca tek bir kaydın bu döngüden geçmesine izin verdiği için, Little kanununun sunduğu potansiyel bant genişliği tamamen çöker.
Uçuşan işlem sayısını (concurrency limit) bilinçli olarak artırmak, sistemin aynı anda birden fazla ağ isteğini beklemesini sağlar. Birinci kayıt harici servisten yanıt beklerken, ikinci ve üçüncü kayıtlar da eşzamanlı olarak kendi isteklerini gönderebilir. Böylece tek çekirdekli bir işlemci bile, I/O bekleme sürelerini akıllıca overlap (örtüştürme) yaparak çok daha yüksek bir veri işleme verimliliğine ulaşabilir. Tabii ki bu artışın denetlenmesi ve dış servislerin kapasitesine göre sınırlandırılması hayati önem taşır.
Çoklu Bağımlılık Senaryoları ve Paylaşımlı Sınırlayıcıların Tehlikeleri
Paralel döngülerde performansı artırmak için eşzamanlılık limitini yükseltmek tek başına yeterli değildir; hatta yanlış uygulandığında felakete yol açabilir. Gerçek dünya uygulamalarında bir iş akışı genellikle birden fazla dış servise bağımlıdır. Örneğin, birinci servis (zenginleştirme API’si) saniyede yüzlerce isteği rahatlıkla kaldırabilirken, arka plandaki eski bir veritabanı veya ana sistem (system of record) aynı anda yalnızca az sayıda isteği işleyebilir.
Genellikle geliştiriciler bu sorunu çözmek için tüm dış çağrıları kapsayan tek bir SemaphoreSlim veya genel bir havuz kullanırlar. Tüm çağrıları tek bir ana sınırlayıcıya bağlamak, sistemi koruyor gibi görünse de ciddi bir dağılım dengesizliği yaratır. Yavaşlayan bir bağımlılık servisi, paylaşılan tüm izinleri (permits) uzun süre meşgul edebilir ve kapasitesi tam olan diğer hızlı servislerin de bloke olmasına yol açabilir. Toplamda otuz izin bulunması, hassas bir sistemin aşırı yüklenmeyeceğini garanti etmez.
Dış servislerin kapasiteleri birbirinden farklıdır ve her bağımlılığın kendi performans karakteristikleri vardır. Bir analitik servisi ile kritik ödeme veya kayıt servisi aynı eşzamanlılık sınırını paylaşamaz. Bu nedenle, genel bir havuz yerine her bağımlı servise özel bütçeler ve ayrı denetim mekanizmaları atanmalıdır. Aksi takdirde, sistemin en zayıf halkası tüm iş akışını kilitler ve tek çekirdekli konteynerin zaten kısıtlı olan kapasitesi tamamen boşa harcanır.

Bağımlılık Bazlı Bütçeleme Stratejisi: Bağımsız Sınırlayıcılar
Tek çekirdekli konteyner tuzaklarından kaçınmanın en zarif ve mühendislik odaklı yolu, her bir dış bağımlılık için ayrı eşzamanlılık bütçeleri (Per-Dependency Budgeting) tanımlamaktır. Uygulamanızın ana döngüsü yeterince geniş bir eşzamanlılık penceresine izin verirken, her bir harici servis çağrısı kendi özel SemaphoreSlim kapısından (gate) geçmelidir. Bu yaklaşım, servislerin birbirlerinin darboğazlarından izole edilmesini sağlar.
Örneğin, C# ekosisteminde tasarlayabileceğiniz özel bir bağımlılık bütçe yöneticisi sınıfı, zenginleştirme servisleri ile ana kayıt veritabanı erişimlerini birbirinden tamamen ayırabilir:
public sealed class DependencyBudgets(int enrichmentLimit, int recordsLimit)
{
private readonly SemaphoreSlim _enrichment = new(enrichmentLimit, enrichmentLimit);
private readonly SemaphoreSlim _records = new(recordsLimit, recordsLimit);
public Task<T> CallEnrichmentAsync<T>(Func<CancellationToken, Task<T>> call, CancellationToken ct)
=> GateAsync(_enrichment, call, ct);
public Task<T> CallRecordsAsync<T>(Func<CancellationToken, Task<T>> call, CancellationToken ct)
=> GateAsync(_records, call, ct);
private static async Task<T> GateAsync<T>(SemaphoreSlim gate, Func<CancellationToken, Task<T>> call, CancellationToken ct)
{
await gate.WaitAsync(ct);
try { return await call(ct); }
finally { gate.Release(); }
}
}
Bu mimari desen, dış servislerin anlık yavaşlamalarında bile ana döngünün çökmesini veya tüm sistemin kilitlenmesini engeller. Zenginleştirme servisi yavaşlasa bile, kayıt servisi kendi bütçesi dahilinde güvenli bir şekilde çalışmaya devam edebilir. Konteyner kaynakları ne kadar kısıtlı olursa olsun, I/O tabanlı işlemler bu sayede en yüksek verimle ve birbirini ezmeden koşturulabilir.
Eşzamanlılık ve Hız Sınırı (Rate Limiting) Arasındaki Farklar
Yazılım dünyasında en sık karıştırılan iki kavram, eşzamanlılık sınırı (concurrency limit) ile hız sınırıdır (rate limiting). Eşzamanlılık sınırı, belirli bir anda sistemde aynı anda kaç tane aktif işlemin uçuştuğunu (in-flight) belirler. Hız sınırı ise belirli bir zaman diliminde (örneğin saniyede veya dakikada) toplam kaç isteğin yapılabileceğini kontrol eder. Yapay zeka (LLM) API’leri, bulut servisleri ve ödeme ağ geçitleri genellikle hem eşzamanlılık hem de dakika başına istek (RPM) kotaları uygular.
Tek çekirdekli konteynerlerde paralel döngüler çalıştırılırken sadece bir SemaphoreSlim kullanarak eşzamanlılığı sınırlamak, harici API sağlayıcılarının per-minute kotalarını aşmanızı engelleyemez. Servis sağlayıcınız saniyede en fazla 10 isteğe izin veriyorsa, eşzamanlılık sınırını 50 yapmak kısa süre içinde rate limit hatalarıyla (HTTP 429 Too Many Requests) karşılaşmanıza neden olur. Bu nedenle, Little kanunu ile hesaplanan eşzamanlılık ihtiyacı ile servis sağlayıcının zaman bazlı kotaları birlikte değerlendirilmelidir.
Etkili bir dijital iş akışı ve otomasyon altyapısı kurarken, hem eşzamanlılık denetleyicilerini hem de token-bucket veya leaky-bucket algoritmasına dayanan rate limiter mekanizmalarını entegre etmek şarttır. Bozkurt Yazılım olarak geliştirdiğimiz harici entegrasyon modüllerinde, ağ trafiğini hem eşzamanlılık havuzlarıyla hem de hassas hız sınırlarıyla denetleyerek kesintisiz ve hatasız bir veri akışı sağlıyoruz. Bu çift katmanlı yaklaşım, konteyner kaynakları ne kadar kısıtlı olursa olsun dış servislerin kurallarına tam uyum sağlanmasını garanti eder.
Konteyner Optimizasyonunda Karşılaştırma Matrisi: Yaklaşımlar ve Sonuçları
Farklı eşzamanlılık ve konteyner kaynak yönetim stratejilerinin performans üzerindeki etkilerini net bir şekilde görmek için aşağıdaki karar matrisi incelenebilir. Bu matris, tek çekirdekli ve çok çekirdekli ortamlarda hangi yaklaşımın ne tür sonuçlar doğurduğunu özetlemektedir.
| Strateji Adı | CPU Sınırı Durumu | I/O Verimliliği | Dış Servis Güvenliği | Riskler ve Dezavantajlar |
|---|---|---|---|---|
| Varsayılan Paralel Döngü (.NET/Go) | Tek Çekirdek (1 CPU) | Çok Düşük (Seristleşme) | Yüksek (Tekli akış) | Gizli performans kaybı, uzun kuyruk süreleri |
| Manuel MaxDegreeOfParallelism Artırımı | Tek Çekirdek (1 CPU) | Orta | Riskli (Aşırı yüklenme) | Dış servislerin rate-limit hataları vermesi |
| Tek Paylaşımlı SemaphoreSlim | Herhangi bir sınır | İyi | Orta | Yavaş bir servisin tüm havuzu kilitlemesi |
| Bağımlılık Bazlı Bütçeleme (Per-Dependency) | Kısıtlı veya Sınırsız | Mükemmel | Çok Yüksek | İlk yapılandırma ve test maliyeti |
Bu karşılaştırma tablosu, yazılım mimarlarının konteyner ortamlarına kod deploy ederken sadece kodun mantığına değil, altyapının sunduğu donanım sınırlarına da odaklanması gerektiğini açıkça ortaya koymaktadır. Doğru mimari desen seçilmediğinde, en modern diller ve en yeni kütüphaneler bile kısıtlı konteynerlerde verimsiz çalışmaktan kurtulamaz.

Doğru Yapılandırma ve Başlangıç Doğrulamaları İçin Adım Adım Rehber
Üretim ortamına taşınacak kritik bir uygulamanın tek çekirdekli konteyner tuzaklarına düşmemesi için dağıtım ve geliştirme süreçlerinde belirli adımların titizlikle uygulanması gerekir. Bu adımlar, uygulamanın donanım değişikliklerinden olumsuz etkilenmesini önler ve öngörülebilir bir performans profili sunar.
- Donanım Varsayımlarından Kaçının: Environment.ProcessorCount değerine körü körüne güvenerek dinamik paralellik katsayıları belirlemek yerine, kritik iş döngüleri için her zaman açık ve konfigüre edilebilir limitler tanımlayın.
- Bağımlılıkları Haritalandırın: Uygulamanızın iletişim kurduğu her bir dış servisin (API, veritabanı, kuyruk) eşzamanlılık kapasitesini servis sahipleriyle netleştirin veya yük testleri ile tespit edin.
- Bağımsız Bütçeler Oluşturun: Her servis için ayrı SemaphoreSlim örnekleri kodlayın ve bu sınırları uygulama konfigürasyon dosyalarından (environment variables) yönetilebilir hale getirin.
- Başlangıç Validasyonu (Startup Validation): Uygulama ayağa kalkarken tüm konfigüre edilmiş bütçe limitlerini ve hız sınırlarını kontrol eden doğrulama rutinleri çalıştırın.
- Kuyruk ve Geri Basınç (Backpressure) İzleme: Dış servisler yavaşladığında isteklerin nerede biriktiğini izleyin. Sabit bütçeler istemciyi korurken, upstream (yukarı akış) tarafta oluşan kuyrukları metrik panellerinde görünür kılın.
Bu adımların eksiksiz uygulanması, yazılım projelerinin bulut ortamlarında ölçeklenirken gizli darboğazlarla karşılaşmasını engeller ve sistem kararlılığını en üst düzeye çıkarır.
Hangi Projeler Risk Altında? Doğru Kararı Verirken Dikkat Edilmesi Gerekenler
Her yazılım projesi tek çekirdekli konteyner tuzaklarından aynı derecede etkilenmez. CPU yoğun (CPU-bound) çalışan, örneğin video işleme, kriptografik hesaplamalar veya büyük veri matris manipülasyonu yapan uygulamalar zaten çekirdek sayısına doğrudan bağımlıdır ve tek çekirdekli bir konteynerde doğal olarak yavaşlarlar. Bu tür projelerde çözüm, konteynerin CPU limitini artırmaktır.
Ancak web API’leri, mikroservisler, veri senkronizasyon araçları, toplu e-posta gönderim servisleri ve sosyal medya otomasyon yazılımları gibi I/O yoğun (I/O-bound) projeler büyük risk altındadır. Bu projeler genellikle az CPU tüketir ancak çok sayıda dış ağ çağrısı yapar. Eğer bu tarz bir servis, maliyet optimizasyonu amacıyla bulut sağlayıcılarında minimum CPU kotalarıyla (örneğin 0.5 veya 1 CPU) çalıştırılıyorsa, yazılımcının paralellik mekanizmalarını doğru tasarlamaması durumunda performans felaketi kaçınılmaz olur.
Bozkurt Yazılım olarak, müşterilerimize sunduğumuz dijital iş akışı ve otomasyon çözümlerinde bu tür donanım kısıtlarını baştan hesaba katıyoruz. Mikroservis mimarilerimizi tasarlarken, kaynak maliyetlerini düşürürken performansın törpülenmemesi için bağımlılık bazlı bütçeleme stratejilerini standart bir mühendislik pratiği olarak uyguluyoruz. Doğru seçimi yapmak, sistemin gerçek dünya koşullarında nasıl tepki vereceğini önceden öngörmekten geçer.
Sıkça Sorulan Sorular (SSS)
Tek çekirdekli konteynerlerde paralel döngüler neden tamamen seriye döner?
Modern çalışma zamanları (Runtime), paralellik katsayısını belirlerken sistemin işlemci sayısını (.NET’te Environment.ProcessorCount gibi) baz alır. Konteyner ortamında CPU sınırı 1 olarak ayarlandığında, çalışma zamanı maksimum paralellik derecesini 1 olarak kabul eder ve döngü mantıksal olarak arka planda sıraya sokularak tekli (serial) yürütülür.
I/O ağırlıklı işlemlerde neden CPU sınırları sorun yaratır?
I/O ağırlıklı işlemler (ağ istekleri, API çağrısı, veritabanı sorgusu) çoğunlukla bekleme sürelerinden ibarettir. Tek çekirdek kısıtı, aynı anda yalnızca tek bir işlemin uçuşmasına izin verdiği için, harici servisler yanıt verene kadar işlemci boşa bekler ve çoklu görev avantajı ortadan kalkar.
Bağımlılık bazlı eşzamanlılık bütçesi (Per-Dependency Budget) nasıl kurulur?
Her bir harici bağımlılık servisi için ayrı bir SemaphoreSlim nesnesi tanımlanarak kurulur. Uygulama ana döngüsü geniş bir pencereye izin verse bile, her harici çağrı kendi hedef servisinin bütçe kapısından (gate) geçerek çağrılır. Böylece bir servis yavaşlasa bile diğeri etkilenmez.
Küçük Kanunu (Little’s Law) konteyner optimizasyonunda nasıl kullanılır?
Uçuşan işlem sayısı (concurrency), hedef veri işleme hızı (throughput) ile bir işlemin ortalama tamamlama süresinin çarpımıyla hesaplanır. Bu hesaplama, konteyner içindeki I/O işlemlerinin ne kadar eşzamanlı yürütülmesi gerektiğine dair matematiksel bir üst sınır verir.
Docker CPU sınırlandırması Go, Java ve .NET dillerini nasıl etkiler?
Farklı çalışma zamanları cgroups entegrasyonlarını farklı şekillerde yönetir. Örneğin Go (Go 1.25 ve sonrası) ve modern JVM sürümleri konteyner CPU sınırlarını otomatik olarak algılar. Ancak bu durum, I/O döngülerinin otomatik olarak optimize edildiği anlamına gelmez; geliştiricinin eşzamanlılık limitlerini manuel olarak yönetmesi gerekir.
GOMAXPROCS ayarını unutup kaç gece uykusuz kaldık.
Node.js tarafında thread havuzu boyutunu container limitlerine göre manuel mi ayarlamak gerekiyor acaba?
Lokalde 8 çekirdekte uçan kod prod ortamında 0.5 vCPU ile boğulunca kafayı yemiştik, tam bu konuya parmak basmış.
Bizim projede Task.Run kullanırken async yapıyı bozmadan throttler ekleyerek bu context switch maliyetini bir nebze olsun düşürebildik ama hala ideal noktada değiliz.
Özellikle Java tabanlı mikroservislerde JVM’in container içindeki cgroup limitlerini doğrudan okuyamaması eski sürümlerde çok can yakıyordu. Sizin bu bahsettiğiniz çalışma zamanı uyumsuzluğu tam olarak cgroup v1 ve v2 geçişindeki sıkıntılara benziyor, güncel sürüm kullanmak bu durumu ne kadar çözer?
Context switch yükü bazen asıl işten daha çok zaman alıyor.
CPU throttling oranlarını cAdvisor üzerinden izlemeden deployment çıkmamak lazım, özellikle limitler sıkıysa.
I/O bound işlerde bile thread havuzunu şişirmek neden performansı düşürür tam olarak?
Geçen hafta benzer bir sorunla uğraştık, kubernetes pod limitlerini artırınca düzeldi sandık ama meğerse yanlış thread havuzu yapılandırmasından kaynaklanıyormuş, sorunu çözmemiz günlerimizi aldı.
Python multiprocessing modülü bu tip kısıtlı ortamlarda maalesef hemen havlu atıyor.
Docker limit verirken request ve limit değerlerini eşit tutmak şart mı?
Bizim ekipte herkes paralel kod yazınca her şey hızlanacak sanıyor, halbuki tek çekirdekte context switching yüzünden sistem tamamen kilitlenebiliyor.
Task pool boyutunu otomatik ölçekleyen kütüphaneler kullanmak tek çekirdekli podlarda hayat kurtarıyor.
Üretim ortamındaki kaynak kısıtlamalarını lokalde simüle etmek için cgroups kullanarak denemeler yapıyoruz ancak yine de bazı edge case’ler gözden kaçabiliyor, bu tarz durumlarda test aşamasında hangi araçları önerirsiniz?
Throttling grafikleri yükseldiğinde ilk bakılacak yer kesinlikle paralel döngüler.