Zamana Bağlı Kodu Geliştirme İpuçları
Hızlı kod yazmak için uygulamanızın tüm yönlerini ve sistemle nasıl etkileşime geçtiğini anlamak gerekir. Bu makalede, kodunuzun zaman açısından kritik bölümlerinin tatmin edici bir şekilde çalıştığından emin olmanıza yardımcı olmak için daha belirgin kodlama tekniklerinden bazılarına alternatifler önermektedir.
Özetlemek gerekirse, zaman açısından kritik kodu geliştirmek için şunları kullanmanız gerekir:
Programınızın hangi bölümlerinin hızlı olması gerektiğini bilin.
Kodunuzun boyutunu ve hızını bilin.
Yeni özelliklerin maliyetini bilin.
İşi gerçekleştirmek için gereken minimum çalışmayı bilin.
Kodunuzun performansı hakkında bilgi toplamak için performans izleyicisini (perfmon.exe) kullanabilirsiniz.
Bu Makaledeki Bölümler
Önbellek Eksikleri ve Sayfa Hataları
Hem iç ve dış önbellekte hem de sayfa hatalarında (program yönergeleri ve verileri için ikincil depolamaya gitme) isabetsiz önbellek isabetleri, programın performansını yavaşlatır.
CPU önbelleği isabeti programınıza 10-20 saat döngüsüne mal olabilir. Dış önbellek isabeti 20-40 saat döngüsüne mal olabilir. Sayfa hatası bir milyon saat döngüsüne mal olabilir (500 milyon yönergeleri/saniyeyi işleyen bir işlemci ve sayfa hatası için 2 milisaniyelik bir süre varsayılır). Bu nedenle, kaçırılan önbellek isabetlerinin ve sayfa hatalarının sayısını azaltacak kod yazmak program yürütme açısından en iyisidir.
Programların yavaş olmasının bir nedeni, daha fazla sayfa hatası almaları veya önbelleği gerekenden daha sık kaçırmalarıdır. Bu sorundan kaçınmak için, iyi bir başvuru konumuna sahip veri yapılarını kullanmak önemlidir; bu da ilgili şeyleri bir arada tutmak anlamına gelir. Bazen başvurunun yetersiz yerelliği nedeniyle harika görünen bir veri yapısı korkunç, bazen de tersi doğru olur. Burada iki örnek verilmiştir:
Dinamik olarak ayrılan bağlı listeler program performansını düşürebilir. Bir öğeyi aradığınızda veya bir listeden sonuna geçiş yaptığınızda, atlanan her bağlantı önbelleği kaçırabilir veya sayfa hatasına neden olabilir. Basit dizileri temel alan bir liste uygulaması, daha iyi önbelleğe alma ve daha az sayfa hatası nedeniyle daha hızlı olabilir. Dizinin büyümesinin daha zor olacağı gerçeğine izin verseniz bile, yine de daha hızlı olabilir.
Dinamik olarak ayrılmış bağlı listeleri kullanan karma tablolar performansı düşürebilir. Uzantıya göre, içeriklerini depolamak için dinamik olarak ayrılmış bağlantılı listeler kullanan karma tablolar çok daha kötü performans gösterebilir. Aslında, son analizde, bir dizide basit bir doğrusal arama daha hızlı olabilir (koşullara bağlı olarak). Dizi tabanlı karma tablosunun ("kapalı karmalama" olarak adlandırılır) kullanılması, genellikle üstün performansa sahip, genellikle göz ardı edilen bir uygulamadır.
Sıralama ve Arama
Sıralama, birçok tipik işlemle karşılaştırıldığında doğal olarak zaman alır. Gereksiz yavaşlamalardan kaçınmanın en iyi yolu, kritik zamanlarda sıralama yapmaktan kaçınmaktır. Yapabileceklerin:
Sıralamayı performans açısından kritik olmayan bir zamana kadar ertele.
Verileri daha önceki, performans açısından kritik olmayan bir zamanda sıralayın.
Verilerin yalnızca gerçekten sıralanması gereken bölümünü sıralayın.
Bazen listeyi sıralı düzende oluşturabilirsiniz. Dikkatli olun, çünkü verileri sıralı düzende eklemeniz gerekirse, başvuru yerelliği düşük olan ve önbellek eksikliklerine ve sayfa hatalarına yol açan daha karmaşık bir veri yapısına ihtiyaç duyabilirsiniz. Her durumda çalışan bir yaklaşım yoktur. Çeşitli yaklaşımlar deneyin ve farklılıkları ölçün.
Sıralamaya yönelik bazı genel ipuçları şunlardır:
Hataları en aza indirmek için stok sıralamasını kullanın.
Sıralamanın karmaşıklığını azaltmak için önceden yapabileceğiniz tüm çalışmalar faydalı olabilir. Verilerinizin üzerinden bir kerelik geçiş yapmak karşılaştırmaları basitleştirir ve sıralamayı O(n günlük n) ile O(n) arasında azaltırsa, neredeyse kesinlikle öne çıkacaksınız.
Sıralama algoritması başvurusunun yerelliğini ve üzerinde çalışmasını beklediğiniz verileri düşünün.
Aramalar için sıralamaya göre daha az alternatif vardır. Arama zaman açısından kritikse, ikili arama veya karma tablo araması neredeyse her zaman en iyisidir, ancak sıralamada olduğu gibi yerelliği göz önünde bulundurmanız gerekir. Küçük bir dizideki doğrusal arama, sayfa hatalarına veya önbellek kayıplarına neden olan birçok işaretçi içeren bir veri yapısında ikili aramadan daha hızlı olabilir.
MFC ve Sınıf Kitaplıkları
Microsoft Foundation Sınıfları (MFC), kod yazmayı büyük ölçüde basitleştirebilir. Zaman açısından kritik kod yazarken, bazı sınıflardaki ek yükün farkında olmanız gerekir. Zaman açısından kritik kodunuzun performans gereksinimlerinizi karşılayıp karşılamadığını görmek için kullandığı MFC kodunu inceleyin. Aşağıdaki liste, bilmeniz gereken MFC sınıflarını ve işlevlerini tanımlar:
CString
MFC, dinamik olarak bellek ayırmak içinCString
C çalışma zamanı kitaplığını çağırır. Genel olarak konuşursak,CString
diğer dinamik olarak ayrılan dizeler kadar verimlidir. Dinamik olarak ayrılan herhangi bir dizede olduğu gibi, dinamik ayırma ve serbest bırakma ek yüküne sahiptir. Genellikle, yığındaki basitchar
bir dizi aynı amaca hizmet edebilir ve daha hızlıdır. Sabit dize depolamak için bir kullanmayınCString
. Bunun yerineconst char *
kullanın. BirCString
nesneyle gerçekleştirdiğiniz tüm işlemlerin bazı ek yükleri vardır. Çalışma zamanı kitaplık dizesi işlevlerini kullanmak daha hızlı olabilir.CArray
ACArray
, normal bir dizinin gerekmemesi esnekliği sağlar, ancak programınızın buna ihtiyacı olmayabilir. Dizinin belirli sınırlarını biliyorsanız, bunun yerine genel bir sabit dizi kullanabilirsiniz. kullanıyorsanızCArray
, boyutunu belirlemek ve yeniden ayırma gerektiğinde büyüyeceğiniz öğe sayısını belirtmek için kullanınCArray::SetSize
. Aksi takdirde, öğe eklemek dizinizin sık sık yeniden ayrılmasına ve kopyalanmasına neden olabilir; bu verimsizdir ve belleği parçalayabilir. Ayrıca, bir diziye öğe eklerseniz,CArray
sonraki öğeleri bellekte taşır ve diziyi büyütmesi gerekebilir. Bu eylemler önbellek hatalarına ve sayfa hatalarına neden olabilir. MFC'nin kullandığı koda bakarsanız performansı artırmak için senaryonuza daha özel bir şey yazabileceğinizi görebilirsiniz. Örneğin bir şablon olduğundanCArray
, belirli türler için özelleştirmeler sağlayabilirsinizCArray
.CList
CList
ikiye kat bağlantılı bir liste olduğundan, öğe ekleme işlemi listede baş, kuyruk ve bilinen bir konumda (POSITION
) hızlı olur. Bir öğeyi değere veya dizine göre aramak için sıralı arama gerekir, ancak liste uzunsa yavaş olabilir. Kodunuz ikiye kat bağlantılı bir liste gerektirmiyorsa, kullanarakCList
yeniden düşünmek isteyebilirsiniz. Tek bir bağlantılı liste kullanmak, tüm işlemler için başka bir işaretçiyi güncelleştirme yükünü ve bu işaretçinin belleğini kaydeder. Ek bellek büyük değildir, ancak önbellek eksikleri veya sayfa hataları için başka bir fırsattır.IsKindOf
Bu işlev birçok çağrı oluşturabilir ve farklı veri alanlarındaki belleğe erişebilir ve başvurunun yanlış yerelliğine yol açabilir. Hata ayıklama derlemesi için yararlıdır (örneğin, bir ASSERT çağrısında), ancak bunu bir yayın derlemesinde kullanmaktan kaçınmaya çalışın.PreTranslateMessage
Belirli bir pencere ağacının farklı klavye hızlandırıcılarına ihtiyacı olduğunda veya ileti pompasına ileti işleme eklemeniz gerektiğinde kullanınPreTranslateMessage
.PreTranslateMessage
MFC dağıtım iletilerini değiştirir. öğesini geçersiz kılarsanızPreTranslateMessage
, bunu yalnızca gereken düzeyde yapın. Örneğin, yalnızca belirli bir görünümün alt öğelerine giden iletilerle ilgileniyorsanız geçersiz kılmanızCMainFrame::PreTranslateMessage
gerekmez. Bunun yerine görünüm sınıfı için geçersiz kılınPreTranslateMessage
.Herhangi bir pencereye gönderilen iletileri işlemek için komutunu kullanarak
PreTranslateMessage
normal dağıtım yolunu atlatmayın. Bu amaçla pencere yordamlarını ve MFC ileti eşlemelerini kullanın.OnIdle
Boşta kalan olaylar, veWM_KEYUP
olayları gibiWM_KEYDOWN
beklemediğiniz zamanlarda gerçekleşebilir. Zamanlayıcılar kodunuzu tetiklemenin daha verimli bir yolu olabilir. Yanlış iletiler oluşturarak veya her zaman bir geçersiz kılmadanOnIdle
döndürerekTRUE
, iş parçacığınızın uyku moduna hiçbir zaman izin vermeyecek şekilde tekrar tekrar çağrılmaya zorlamayınOnIdle
. Yine bir zamanlayıcı veya ayrı bir iş parçacığı daha uygun olabilir.
Paylaşılan kitaplıklar
Kodun yeniden kullanılması tercih edilir. Ancak, başka birinin kodunu kullanacaksanız, performansın sizin için kritik olduğu durumlarda tam olarak ne yaptığını bildiğinizden emin olmanız gerekir. Bunu anlamanın en iyi yolu, kaynak kodu adımlayarak veya PView veya Performans İzleyicisi gibi araçlarla ölçüm yapmaktır.
Yığın
İsteğe bağlı olarak birden çok yığın kullanın. ile HeapCreate
oluşturulan ek yığınlar, HeapAlloc
ilgili ayırma kümesini yönetmenize ve sonra atmanıza olanak sağlar. Çok fazla bellek işlemeyin. Birden çok yığın kullanıyorsanız başlangıçta işlenen bellek miktarına özellikle dikkat edin.
Birden çok yığın yerine, yardımcı işlevleri kullanarak kodunuz ile varsayılan yığın arasında arabirim oluşturabilirsiniz. Yardımcı işlevler, uygulamanızın performansını geliştirebilecek özel ayırma stratejilerini kolaylaştırır. Örneğin, sık sık küçük ayırmalar gerçekleştiriyorsanız, bu ayırmaları varsayılan yığının bir bölümüne yerelleştirmek isteyebilirsiniz. Büyük bir bellek bloğu ayırabilir ve ardından bu bloktan suballocate yapmak için bir yardımcı işlevi kullanabilirsiniz. Ardından, ayırma varsayılan yığından çıktığı için kullanılmayan belleğe sahip birden çok yığına sahip olmazsınız.
Ancak bazı durumlarda varsayılan yığını kullanmak başvurunun yerelliğini azaltabilir. Nesneleri yığından yığına taşımanın etkilerini ölçmek için İşlem Görüntüleyicisi, Spy++ veya Performans İzleyicisi kullanın.
Yığınlardaki her ayırmayı hesaba katabileceğinden yığınlarınızı ölçün. Denetim noktası ve yığın dökümü için C çalışma zamanı hata ayıklama yığın yordamlarını kullanın. Çıktıyı Microsoft Excel gibi bir elektronik tablo programına okuyabilir ve sonuçları görüntülemek için özet tabloları kullanabilirsiniz. Ayırmaların toplam sayısını, boyutunu ve dağılımını not edin. Bu sonuçları çalışma kümelerinin boyutuyla karşılaştırın. Ayrıca ilgili boyuttaki nesnelerin kümelenmesine de bakın.
Bellek kullanımını izlemek için performans sayaçlarını da kullanabilirsiniz.
İş Parçacıkları
Arka plan görevleri için olayların etkin boşta işlenmesi, iş parçacıklarını kullanmaktan daha hızlı olabilir. Tek iş parçacıklı bir programda başvurunun yerelliğini anlamak daha kolaydır.
İş parçacığını yalnızca engellediğiniz bir işletim sistemi bildirimi arka plan çalışmasının kökündeyse kullanmak iyi bir kuraldır. Bir olayda ana iş parçacığını engellemek pratik olmadığından, iş parçacıkları böyle bir durumda en iyi çözüm olur.
İş parçacıkları iletişim sorunları da sunar. İş parçacıklarınız arasındaki iletişim bağlantısını bir ileti listesiyle veya paylaşılan belleği ayırarak ve kullanarak yönetmeniz gerekir. İletişim bağlantısını yönetmek için genellikle yarış koşullarını ve kilitlenme sorunlarını önlemek için eşitleme gerekir. Bu karmaşıklık hatalara ve performans sorunlarına kolayca dönüşebilir.
Daha fazla bilgi için bkz . Boşta Döngü İşleme ve Çoklu İş Parçacığı Kullanımı.
Küçük Çalışma Kümesi
Daha küçük çalışma kümeleri daha iyi başvuru konumu, daha az sayfa hatası ve daha fazla önbellek isabeti anlamına gelir. İşlem çalışma kümesi, işletim sisteminin başvurunun yerelliğini ölçmek için doğrudan sağladığı en yakın ölçümdür.
Çalışma kümesinin üst ve alt sınırlarını ayarlamak için kullanın
SetProcessWorkingSetSize
.Çalışma kümesinin üst ve alt sınırlarını almak için kullanın
GetProcessWorkingSetSize
.Çalışma kümesinin boyutunu görüntülemek için Spy++ tuşlarını kullanın.