Birçok durumda, bazı görevleri gerçekleştirmenin en uygun yolu, görevin gerçekleştirildiği bağlama bağlı olabilir. Eğer bir rutin montaj dilinde yazılmışsa, talimatlar dizisinin içeriğe göre değiştirilmesi genellikle mümkün olmayacaktır. Basit bir örnek olarak, aşağıdaki basit yöntemi göz önünde bulundurun:
inline void set_port_high(void)
{
(*((volatile unsigned char*)0x40001204) = 0xFF);
}
Yukarıda verilen 32 bit ARM kodu için bir derleyici, büyük olasılıkla bunu aşağıdaki gibi işleyecektir:
ldr r0,=0x40001204
mov r1,#0
strb r1,[r0]
[a fourth word somewhere holding the constant 0x40001204]
ya da belki
ldr r0,=0x40001000 ; Some assemblers like to round pointer loads to multiples of 4096
mov r1,#0
strb r1,[r0+0x204]
[a fourth word somewhere holding the constant 0x40001000]
Bu, elle birleştirilmiş kodda biraz optimize edilebilir:
ldr r0,=0x400011FF
strb r0,[r0+5]
[a third word somewhere holding the constant 0x400011FF]
veya
mvn r0,#0xC0 ; Load with 0x3FFFFFFF
add r0,r0,#0x1200 ; Add 0x1200, yielding 0x400011FF
strb r0,[r0+5]
Elle birleştirilen her iki yaklaşım da 16 yerine 12 bayt kod alanı gerektirir; ikincisi, bir "yükü" bir ARM7-TDMI üzerinde iki döngüyü daha hızlı yürüten bir "ekleme" ile değiştirir. Kod, r0'ın bilmediği / umursamadığı bir bağlamda yürütülecek olsaydı, montaj dili sürümleri derlenmiş sürümden biraz daha iyi olurdu. Öte yandan, derleyicinin bazı kayıtların [örn. R5], istenen adresin 0x40001204 [örneğin 0x40001000] 'in 2047 bayt içinde bir değer tutacağını bildiğini ve ayrıca başka bir kaydın [örneğin r7] gideceğini bildiğini varsayalım düşük bitleri 0xFF olan bir değeri tutmak için. Bu durumda, bir derleyici kodun C sürümünü basitçe:
strb r7,[r5+0x204]
Elle optimize edilmiş montaj kodundan bile çok daha kısa ve hızlı. Ayrıca, set_port_high öğesinin bağlamda oluştuğunu varsayalım:
int temp = function1();
set_port_high();
function2(temp); // Assume temp is not used after this
Gömülü bir sistemi kodlarken hiç mantıklı değil. Eğer set_port_high
montaj kodunda yazılır, derleyici (dönüş değeri tutan r0 hareket etmesi gerekir function1
) başka bir yerde daha önce derleme kod yürütmesini ve (çünkü o zaman sonra r0 bu değer çekmeme function2
, r0 ilk parametreyi bekliyoruz) bu nedenle "optimize edilmiş" montaj kodu beş talimat gerektirir. Derleyici, adresi veya depolanacak değeri tutan herhangi bir kayıt bilmiyor olsa bile, dört komutlu sürümü (kullanılabilir kayıtların kullanımına uyarlanabilir - r0 ve r1 olması gerekmez) "optimize edilmiş" montajı yener -dil sürümü. Derleyici, daha önce açıklandığı gibi r5 ve r7'deki gerekli adres ve verilere sahip function1
olsaydı , bu kayıtları değiştirmez ve bu nedenleset_port_high
tek bir strb
talimatla -"elle optimize edilmiş" montaj kodundan daha küçük ve daha hızlı dört talimat .
Elle optimize edilmiş montaj kodunun, programlayıcının kesin program akışını bildiği durumlarda bir derleyiciden daha iyi performans gösterebileceğini, ancak derleyicilerin bağlamı bilinmeden önce bir kod parçasının yazıldığı veya bir kaynak kod parçasının olabileceği durumlarda parladığını unutmayın. birden çok bağlamdan çağrıldığında [ set_port_high
koddaki elli farklı yerde kullanılırsa, derleyici bağımsız olarak onu en iyi nasıl genişleteceklerine karar verebilir].
Genel olarak, montaj dilinin, her bir kod parçasına çok sınırlı sayıda bağlamdan yaklaşılabildiği durumlarda en yüksek performans iyileştirmelerini sağlamaya uygun olduğunu ve bir parça parçanın olduğu yerlerde performansa zarar vermeye eğilimli olduğunu öneririm. koda birçok farklı bağlamdan yaklaşılabilir. İlginçtir (ve uygun olarak) montajın performans için en faydalı olduğu durumlar genellikle kodun en basit ve okunması kolay olanlardır. Derleme dil kodunun aşırı karışıklığa dönüşeceği yerler genellikle derlemede yazmanın en küçük performans avantajı sunacağı yerlerdir.
[Küçük not: montaj kodunun hiper optimize edilmiş gooey karışıklığı vermek için kullanılabileceği bazı yerler vardır; örneğin, ARM için yaptığım bir kod parçası, RAM'den bir kelime almak ve değerin üst altı bitine dayanarak yaklaşık on iki rutinden birini yürütmek zorundaydı (aynı rutete eşlenen birçok değer). Ben böyle bir şeye bu kodu optimize düşünüyorum:
ldrh r0,[r1],#2! ; Fetch with post-increment
ldrb r1,[r8,r0 asr #10]
sub pc,r8,r1,asl #2
R8 kaydı her zaman ana dağıtım tablosunun adresini tutmuştur (kodun zamanının% 98'ini harcadığı döngü içinde, hiçbir şey bunu başka bir amaçla kullanmamıştır); 64 girişin tümü, ondan önceki 256 bayttaki adreslere atıfta bulunur. Birincil döngü çoğu durumda yaklaşık 60 döngü sert yürütme süresi sınırına sahip olduğundan, dokuz döngü getirme ve gönderme bu hedefe ulaşmak için çok etkili oldu. 256 32 bit adresli bir tablo kullanmak bir döngü daha hızlı olurdu, ancak 1KB çok değerli RAM [flash birden fazla bekleme durumu ekleyecekti] gobbled olurdu. 64 32-bit adresleri kullanmak, getirilen sözcükten bazı bitleri maskelemek için bir talimat eklemeyi gerektiriyordu ve hala kullandığım tablodan 192 bayt daha fazla gitmişti. 8-bit ofset tablosunu kullanarak çok kompakt ve hızlı kod verdiler, ama bir derleyicinin gelmesini beklediğim bir şey değil; Ben de bir derleyici tablo adresini tutmak için bir kayıt "tam zamanlı" adamak için beklemem.
Yukarıdaki kod, bağımsız bir sistem olarak çalışacak şekilde tasarlanmıştır; periyodik olarak C kodunu çağırabilir, ancak yalnızca belirli zamanlarda iletişim kurduğu donanımın her 16 ms'de bir kabaca bir milisaniye aralıklarla "boşta" duruma getirilebildiği zaman.