Daha fazla Flash ve RAM için kod nasıl sıkıştırılır? [kapalı]


14

Belirli bir ürünümüz üzerinde bir özellik geliştirmeye çalıştım. Aynı özelliği başka bir ürüne taşıma isteği vardı. Bu ürün, geleneksel olarak 64K Flash ve 2k RAM'e sahip bir M16C mikrodenetleyiciye dayanmaktadır.

Olgun bir üründür ve bu nedenle yalnızca 132 Bayt Flash ve 2 Bayt RAM kaldı.

İstenen özelliği taşımak için (özelliğin kendisi optimize edilmiştir), 1400 bayt Flash'a ve ~ 200 Bayt RAM'e ihtiyacım var.

Herkes bu bayt kod sıkıştırma ile almak için herhangi bir öneriniz var mı? Halihazırda var olan çalışma kodunu sıkıştırmaya çalıştığımda hangi belirli şeyleri arıyorum?

Herhangi bir fikir gerçekten takdir edilecektir.

Teşekkürler.


1
Önerileri için herkese teşekkürler. Sizi ilerlememle ilgili olarak güncel tutacağım ve işe yarayan ve işe yaramayan adımları listeleyeceğim.
IntelliChick

Tamam işte çalıştı denedim şeyler: Derleyici sürümleri yukarı taşındı. Optimizasyon beni büyük ölçüde 2K Flash verdi. Belirli bir ürün için yedekli ve kullanılmayan işlevselliği (ortak kod tabanı nedeniyle devralınan) kontrol etmek için liste dosyaları üzerinden gitti ve biraz daha Flash kazandı.
IntelliChick

RAM için aşağıdakileri yaptım: En çok RAM kullanan işlevleri / modülleri kontrol etmek için harita dosyası üzerinden gittim. Eski koddan gerçekten ağır bir işlev (her biri sabit miktarda ayrılmış belleğe sahip 12 kanal) buldum, ortak kanallar arasında bilgi paylaşarak ne elde etmeye çalıştığını anladım ve RAM kullanımını optimize ettim. Bu bana ihtiyacım olan ~ 200 bayt verdi.
IntelliChick

Eğer ascii dosyalarınız varsa 8 ila 7 bit sıkıştırmayı kullanabilirsiniz. % 12.5 tasarruf sağlar. Bir zip dosyası kullanmak, onu bırakmaktan daha fazla kod almak ve açmak için daha fazla kod alır.
Sparky256

Yanıtlar:


18

Birkaç seçeneğiniz var: birincisi, yedek kod aramak ve çoğaltmadan kurtulmak için tek bir çağrıya taşımaktır; ikincisi işlevselliği kaldırmaktır.

.Map dosyanıza iyi bakın ve kurtulabileceğiniz veya yeniden yazabileceğiniz işlevler olup olmadığını görün. Ayrıca kullanılan kütüphane çağrılarının gerçekten gerekli olduğundan emin olun.

Bölme ve çarpma gibi bazı şeyler çok fazla kod getirebilir, ancak vardiya kullanmak ve sabitleri daha iyi kullanmak kodu daha küçük hale getirebilir. Ayrıca string sabitleri ve printfs gibi şeylere bir göz atın . Örneğin, her biri printfromunuzu yiyecektir, ancak bu dizeyi tekrar tekrar tekrarlamak yerine birkaç paylaşılan biçim dizesine sahip olabilirsiniz.

Bellek için, globallerden kurtulup kurulamayacağınıza ve bunun yerine bir işlevi otomatik olarak kullanıp kullanamayacağınıza bakın. Ayrıca, ana fonksiyonda, tıpkı küreseller gibi hafızayı yediği için, mümkün olduğunca çok değişkenten kaçının.


1
Öneriler için teşekkürler, dize sabitleri hariç kesinlikle çoğunu deneyebilirim. Bu, tamamen kullanıcı arayüzü olmayan, gömülü bir cihazdır ve kod içinde printf () çağrısı yapılmaz. İhtiyacım olan 1400 Bayt Flash / 200 bayt RAM'imi almak için bu önerilerin bana gelmesi umuduyla.
IntelliChick

1
@IntelliChick, hata ayıklamak veya bir çevre birimine göndermek üzere yazdırmak için katıştırılmış bir aygıtın içinde kaç kişinin printf () kullandığına şaşıracaksınız. Bundan daha iyi bildiğiniz gibi görünüyor, ama eğer biri sizden önce projeye kod yazmışsa, bunu kontrol etmek zarar vermez.
Kellenjb

5
Ve sadece önceki yorumumu genişletmek için, kaç kişinin hata ayıklama ifadeleri eklediğine şaşıracaksınız, ancak bunları asla kaldırmayacaksınız. # İfdef yapan insanlar bile bazen tembelleşir.
Kellenjb

1
Harika, teşekkürler! Bu kod temeli miras aldım, bu yüzden kesinlikle onlar için bir göz atın. İleride sizi bilgilendirmeye devam edeceğim ve gelecekte bunu yapması gerekebilecek diğer herkes için bir referans olarak ne yaparak kaç bayt bellek veya Flash kazandığımı takip etmeye çalışacağım.
IntelliChick

Bu konuda sadece bir soru - iç içe fonksiyon çağrısı atlamadan katmana ne olacak. Bu ne kadar ek yük getirir? Birden çok işlev çağrısına sahip olarak modülerliği korumak veya işlev çağrılarını azaltmak ve bazı baytları kaydetmek daha iyidir. Ve bu önemli mi?
IntelliChick

8

Belirli bir derleyicinin özellikle kötü olduğu şeyleri aramak için her zaman dosya (birleştirici) çıktısını listelemeye değer.

Örneğin, yerel değişkenlerin çok pahalı olduğunu görebilirsiniz ve uygulama riske değecek kadar basitse, birkaç döngü sayacını statik değişkenlere taşımak çok fazla kod tasarrufu sağlayabilir.

Veya dizi indeksleme çok pahalı olabilir ancak işaretçi işlemleri çok daha ucuz olabilir. Ya da tam tersi.

Ancak montaj diline bakmak ilk adımdır.


3
Derleyicinizin ne yaptığını bilmeniz çok önemlidir. Derleyicimde hangi bölünmenin olduğunu görmelisin. Bebekleri ağlatıyor (ben dahil).
Kortuk

8

Örneğin -OsGCC'de derleyici optimizasyonları hız ve kod boyutu arasında en iyi dengeyi sağlar. -O3Kod boyutunu artırabileceğinden kaçının .


3
Bunu yaparsanız, HER ŞEYİ tekrar test etmeniz gerekecektir! Optimizasyonlar, derleyicinin yaptığı yeni varsayımlar nedeniyle çalışma kodunun çalışmamasına neden olabilir.
Robert

@Robert, bu yalnızca tanımsız ifadeler kullanıyorsanız doğrudur: örneğin a = a ++, -O0 ve -O3 içinde farklı şekilde derlenir.
Thomas O

5
@ Thomas doğru değil. Saat döngülerini geciktirmek için bir for döngünüz varsa, birçok optimizer, içinde hiçbir şey yapmadığınızı fark eder ve kaldırır. Bu sadece 1 örnektir.
Kellenjb

1
@thomas O, Ayrıca, geçici işlev tanımları konusunda dikkatli olduğunuzdan emin olmanız gerekir. Optimize ediciler, C'yi iyi tanıdıklarını düşünen ancak atomik işlemlerin karmaşıklıklarını anlamayanları patlatacaklar.
Kortuk

1
Tüm iyi puan. Uçucu fonksiyonlar / değişkenler, tanım gereği, optimize edilmemelidir. Bu konuda (arama zamanı ve satır içi) dahil olmak üzere optimizasyon yapan herhangi bir optimizer bozulur.
Thomas O

8

RAM için, tüm değişkenlerinizin aralığını kontrol edin - karakterleri kullanabileceğiniz ints kullanıyor musunuz? Tamponlar olması gerekenden daha mı büyük?

Kod sıkma çok uygulama ve kodlama tarzına bağlıdır. Kalan miktarlarınız, kodun biraz sıkılmış olmasına rağmen zaten gittiğini gösteriyor, bu da kalan çok az şey olduğu anlamına gelebilir.

Ayrıca genel işlevselliğe de dikkat edin - gerçekten kullanılmayan ve karıştırılabilecek bir şey var mı?


8

Bu eski bir proje ancak derleyici o zamandan beri geliştirildiyse, daha yeni bir derleyici daha küçük kod üretebilir


Teşekkürler Mike! Bunu geçmişte denedim ve kazanılan alan zaten kullanılmış! :) IAR C derleyici 3.21d'den 3.40'a yükseldi.
IntelliChick

1
Bir sürüm daha yükselttim ve bu özelliğe uyması için biraz daha Flash almayı başardım. RAM ile gerçekten mücadele ediyorum, bu değişmedi. :(
IntelliChick

7

Alanı optimize etme seçenekleri için derleyici kılavuzunuzu kontrol etmeye değer.

Gcc için -ffunction-sectionsve -fdata-sectionsbirlikte --gc-sectionsbağlayıcı bayrak ölü kod sıyırma için iyidir.

İşte bazı mükemmel ipuçları (AVR'ye yönelik)


Bu gerçekten işe yarıyor mu? Dokümanlar "Bu seçenekleri belirttiğinizde, birleştirici ve bağlayıcı daha büyük nesne ve yürütülebilir dosyalar oluşturur ve ayrıca daha yavaş olur" der. Ayrı bölümlere sahip olmanın Flash ve RAM bölümleri olan bir mikro için anlamlı olduğunu anlıyorum - Dokümanlardaki bu ifade mikrodenetleyiciler için geçerli değil mi?
Kevin Vermeer

Deneyimlerim, AVR için iyi çalışıyor
Toby Jaffey

1
Kullandığım derleyicilerin çoğunda bu iyi çalışmıyor. Bu kayıt anahtar sözcüğünü kullanmak gibidir. Derleyiciye bir değişkenin bir sicile girdiğini söyleyebilirsiniz, ancak iyi bir optimize edici bunu bir insandan daha iyi yapacağını söyleyebilir (bazılarının yapabileceği gibi bunu pratikte yapmanın kabul edilemez olduğu düşünülür).
Kortuk

1
Konumları atamaya başladığınızda, derleyiciyi belirli konumlara bir şeyler yerleştirmeye zorlarsınız, gelişmiş önyükleme yükleyicisi kodu için çok önemlidir, ancak optimizasyonda uğraşmak çok zordur, bunun için karar verirken optimizasyonun bir adımını atıyorsunuz yapabilirdi. Bazı derleyicilerde, kodun ne için kullanıldığına dair bölümler olacak şekilde tasarlanırlar, bu derleyiciye kullanımınızı anlaması için daha fazla bilgi bildirilmesi durumudur, bu yardımcı olacaktır. Derleyici bunu önermiyorsa, yapma.
Kortuk

6

Ayrılan yığın alanı ve yığın alanı miktarını inceleyebilirsiniz. Bunlardan biri veya her ikisi birden fazla tahsis edilirse önemli miktarda RAM geri alabilirsiniz.

Benim tahminim hiçbir dinamik bellek ayırma yoktur ile RAM 2k içine uyan başlatmak için bir proje (amaçlı olup malloc, callocvs.). Bu durumda, orijinal yazarın yığın için ayrılmış bazı RAM bıraktığı varsayılarak yığından tamamen kurtulabilirsiniz.

Bulması çok zor olan hatalara neden olabileceğinden, yığın boyutunu azaltmaya çok dikkat etmelisiniz. Tüm yığın alanını bilinen bir değere (bu değerler zaten yaygın olarak ortaya çıktığı için 0x00 veya 0xff dışında bir şey) başlatarak başlayabilir ve daha sonra ne kadar yığın alanının kullanılmadığını görmek için sistemi bir süre çalıştırın.


Bunlar çok iyi seçimler. Gömülü bir sistemde asla malloc kullanmamalısınız.
Kortuk

1
@Kortuk Bu, gömülü tanımınıza ve gerçekleştirilen göreve bağlıdır
Toby Jaffey

1
@joby, Evet, anlıyorum. 0 yeniden başlatılan ve linux gibi bir işletim sisteminin bulunmadığı bir sistemde Malloc çok çok kötü olabilir.
Kortuk

Dinamik bellek tahsisi yoktur, malloc'un, calloc'un kullanıldığı yer yoktur. Ben de yığın ayırmayı kontrol ettim ve zaten 0 olarak ayarlanmış, bu yüzden yığın ayırma yok. Şu anda ayrılmış Yığın boyutu 254 Bayt ve kesme yığın boyutu 128 bayttır.
IntelliChick

5

Kodunuz kayan noktalı matematik kullanıyor mu? Algoritmalarınızı yalnızca tamsayı matematiği kullanarak yeniden uygulayabilir ve C kayan nokta kitaplığını kullanmanın genel giderlerini ortadan kaldırabilirsiniz. Örneğin, sinüs, log, exp gibi bazı uygulamalarda, tamsayı polinom yaklaşımları ile yer değiştirilebilir.

Kodunuz CRC hesaplamaları gibi herhangi bir algoritma için büyük arama tabloları kullanıyor mu? Arama tablolarını kullanmak yerine algoritmanın değerleri anında hesaplayan farklı bir sürümünü değiştirmeyi deneyebilirsiniz. Uyarı, daha küçük algoritmanın büyük olasılıkla daha yavaş olmasıdır, bu nedenle yeterli CPU döngüsüne sahip olduğunuzdan emin olun.

Kodunuzda, dize tabloları, HTML sayfaları veya piksel grafikleri (simgeler) gibi büyük miktarda sabit veri var mı? Yeterince büyükse (örneğin 10 kB), verileri küçültmek ve gerektiğinde anında açmak için çok basit bir sıkıştırma şeması uygulamaya değer olabilir.


Ne yazık ki 10K tutarında 2 küçük arama tablosu vardır. Ve kayan nokta matematiği de kullanılmıyor. :( Önerileriniz için teşekkürler, onlar iyi.
IntelliChick

2

Daha kompakt bir stile çok kod yazmak için yeniden düzenlemeyi deneyebilirsiniz. Kodun ne yaptığına çok bağlıdır. Anahtar, benzer şeyler bulmak ve bunları birbirleri açısından yeniden uygulamaktır. Bir uç nokta, Forth gibi daha yüksek bir dil kullanmaktır, bununla birlikte C veya montajcıdan daha yüksek bir kod yoğunluğu elde etmek daha kolay olabilir.

İşte M16C için Forth .


2

Derleyicinin optimizasyon düzeyini ayarlayın. Birçok IDE, derleme zamanı pahasına kod boyutu optimizasyonuna izin veren ayarlara sahiptir (hatta bazı durumlarda işlem süresi bile). Optimize edicilerini birkaç kez yeniden çalıştırarak, daha az yaygın olan optimize edilebilir desenleri ve geçici / hata ayıklama derlemesi için gerekli olmayabilecek diğer tüm hileleri arayarak kod sıkıştırma işlemini gerçekleştirebilirler. Genellikle, varsayılan olarak derleyiciler orta düzeyde bir optimizasyona ayarlanır. Bazı tamsayı tabanlı optimizasyon ölçeği bulabilmeniz için ayarlarda kazın.


1
Şu anda Boyut için Maksimum değerine optimize edildi. :) Öneri için teşekkürler. :)
IntelliChick

2

Zaten IAR gibi profesyonel düzeyde bir derleyici kullanıyorsanız, küçük düşük seviyeli kod ayarlarıyla ciddi tasarruflar elde etmek için mücadele edeceğinizi düşünüyorum - işlevselliği kaldırmak veya önemli işlemler yapmak için daha fazla aramaya ihtiyacınız olacak. parçaları daha verimli bir şekilde yeniden yazar. Orijinal sürümü yazandan daha akıllı bir kodlayıcı olmanız gerekir ... RAM'e gelince, şu anda nasıl kullanıldığına çok sert bir şekilde bakmanız ve aynı RAM'in kullanımının üst üste binme kapsamı olup olmadığını görmeniz gerekir. farklı zamanlarda farklı şeyler (sendikalar bunun için kullanışlıdır). IAR'ın ARM / AVR'deki varsayılan yığın ve yığın boyutları aşırı cömert olma eğilimindeydim, bu yüzden ilk bakacaklar bunlar olurdu.


Teşekkürler Mike. Kod zaten birçok yerde sendikalar kullanıyor, ancak bunun hala yardımcı olabileceği başka yerlere bir göz atacağım. Ayrıca, seçilen yığın boyutuna bir göz atacağım ve bunun hiç optimize edilip edilemeyeceğini göreceğim.
IntelliChick

Yığın boyutu boyutunun uygun olduğunu nasıl bilebilirim?
IntelliChick

2

Başka bir şey kontrol etmek - bazı mimarileri bazı derleyiciler RAM sabitleri kopya - için tipik (örn AVR) örn IAR en AVR derleyici bir _ _flash qualifer gerektirir flaş sabitleri erişim / yavaş zor olduğu durumlar için değil RAM bir sabit kopya)


Teşekkürler Mike. Evet, bunu zaten kontrol etmiştim - M16C IAR C derleyicisi için 'Yazılabilir sabitler' seçeneği deniyordu. Sabitleri ROM'dan RAM'e kopyalar. Projem için bu seçenek işaretli değil. Ama gerçekten geçerli bir çek! Teşekkürler.
IntelliChick

1

İşlemcinizin bir parametre / yerel yığın için donanım desteği yoksa, ancak derleyici yine de bir çalışma zamanı parametre yığını uygulamaya çalışır ve kodunuzun yeniden girilmesi gerekmiyorsa, kodu kaydedebilirsiniz statik değişkenleri statik olarak ayırarak boşluk bırakın. Bazı durumlarda, bunun manuel olarak yapılması gerekir; diğer durumlarda derleyici yönergeleri bunu yapabilir. Verimli manuel ayırma, değişkenlerin rutinler arasında paylaşılmasını gerektirecektir. Hiçbir rutinin başka bir rutinin "kapsamda" olduğunu düşündüğü bir değişkeni kullanmadığından emin olmak için bu tür bir paylaşım dikkatli bir şekilde yapılmalıdır, ancak bazı durumlarda kod boyutu yararları önemli olabilir.

Bazı işlemciler, bazı parametre geçiş stillerini diğerlerinden daha verimli hale getirebilecek çağrı kurallarına sahiptir. Örneğin, PIC18 kontrolörlerinde, bir rutin tek baytlık bir parametre alırsa, bir kayıt defterine geçirilebilir; bundan daha fazla zaman alırsa, tüm parametreler RAM'de geçirilmelidir. Bir rutin iki adet bir bayt parametre alacaksa, bir global değişken içinde birini "geçmek" ve sonra diğerini parametre olarak geçirmek en etkili olabilir. Yaygın olarak kullanılan rutinlerde tasarruflar artabilir. Genel olarak iletilen parametre tek bitli bir işaretse veya genellikle 0 veya 255 değerine sahip olacaksa özellikle önemli olabilirler (RAM'e 0 veya 255 depolamak için özel talimatlar vardır).

ARM'de, sıklıkla birlikte kullanılan global değişkenlerin bir yapıya yerleştirilmesi, kod boyutunu önemli ölçüde azaltabilir ve performansı artırabilir. A, B, C, D ve E ayrı global değişkenler ise, hepsini kullanan kodların her birinin adresini bir kayıt defterine yüklemesi gerekir; yeterli kayıt yoksa, bu adresleri birden çok kez yeniden yüklemek gerekebilir. Buna karşılık, aynı küresel yapı MyStuff'ın bir parçasıysa, MyStuff.A, MyStuff.B, vb. Kullanan kod MyStuff'ın adresini bir kez yükleyebilir. Büyük zafer.


1

1.Kodunuz çok sayıda yapıya dayanıyorsa, yapı üyelerinin en fazla belleği kaplayanlardan sipariş edildiğinden emin olun.

Örn: "uint16_t uint8_t uint32_t" yerine "uint32_t uint16_t uint8_t"

Bu, minimum yapı dolgusu sağlayacaktır.

2. Varsa değişkenler için sabit kullanın. Bu, bu değişkenlerin ROM'da olmasını ve RAM tüketmemesini sağlayacaktır.


1

Bazı müşterilerin kodlarını sıkıştırırken başarıyla kullandığım birkaç (belki de bariz) hile:

  1. Bit alanlarına veya bit maskelerine kompakt bayraklar. Genellikle booleans tamsayı olarak depolandığından hafızayı boşa harcadığından bu yararlı olabilir. Bu hem RAM hem de ROM'u kaydeder ve genellikle derleyici tarafından yapılmaz.

  2. Kodda yedeklilik olup olmadığına bakın ve yinelenen ifadeleri yürütmek için döngüler veya işlevler kullanın.

  3. Ayrıca birçok ROM değiştirerek bazı ROM kaydettik if(x==enum_entry) <assignment> numaralandırma girişleri dizi dizini olarak kullanılabilir dikkat ederek sabitleri ifade dizinli bir dizi ile


0

Yapabiliyorsanız, küçük işlevler yerine satır içi işlevler veya derleyici makroları kullanın. Geçiş argümanları ile boyut ve hız yükü vardır ve bu fonksiyonun satır içi hale getirilmesiyle giderilebilir.


1
İyi bir derleyici bunu sadece bir kez çağrılan cisimler için otomatik olarak yapmalıdır.
mikeselectricstuff

5
Inlining genellikle hız optimizasyonları için daha yararlı olduğunu ve genellikle artan boyut pahasına buldum.
Craig McQueen

satır içi gibi tipik önemsiz işlevler dışında, kod boyutunu genellikle artıracaktırint get_a(struct x) {return x.a;}
Dmitry Grigoryev

0

Yerel değişkenleri CPU kayıtlarınızla aynı boyutta olacak şekilde değiştirin.

CPU 32-bit ise, maksimum değer asla 255'in üzerine çıkmasa bile 32-bit değişkenler kullanın. 8-bit değişken kullandım, derleyici üst 24 biti maskelemek için kod ekleyecektir.

Bakacağım ilk yer for-loop değişkenleridir.

for( i = 0; i < 100; i++ )

Bu, 8 bit değişken için iyi bir yer gibi görünebilir, ancak 32 bit değişken daha az kod üretebilir.


Bu kodu kurtarabilir, ancak RAM yiyecektir.
mikeselectricstuff

Bu işlev çağrısı çağrı izlemenin en uzun dalındaysa RAM'i yiyecektir. Aksi takdirde, diğer bazı işlevlerin zaten ihtiyaç duyduğu yığın alanını yeniden kullanır.
Robert

2
Yerel bir değişken olması koşuluyla genellikle doğrudur. RAM'de kısa olursa, küresel değişkenlerin boyutu, özellikle diziler, tasarruf aramaya başlamak için iyi bir yerdir.
mikeselectricstuff

1
İlginç bir şekilde, imkansız değişkenlerin yerine imzalı değişkenler koymak mümkündür. Bir derleyici imzasız bir kısa devre 32 bit kayıt için en iyi duruma getirirse, değeri 65535 sıfıra sarmak için kod eklemeniz gerekir. Ancak, derleyici bir kayıt için imzalı bir kısa devre optimize ederse, böyle bir kod gerekmez. Bir kısa devre 32767'nin üzerine çıkarsa ne olacağının garantisi olmadığından, derleyicilerin bununla başa çıkmak için kod yayınlamaları gerekmez. Baktığım en az iki ARM derleyicisinde, imzalı kısa kod bu nedenle imzasız kısa koddan daha küçük olabilir.
supercat
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.