Çok fazla RAM kullanıyorum. Bu nasıl ölçülebilir?


19

Projemde ne kadar RAM kullandığımı bilmek istiyorum, anlayabildiğim kadarıyla, gerçekten bunu yapmanın bir yolu yok (kendim hesaplamaktan başka). RAM'i bitirdiğimi belirlediğim oldukça büyük bir projede sahneye çıktım.

Bir bölüm ekleyebilir ve daha sonra tüm cehennem benim kod başka bir yerde görünür bir sebep olmadan gevşek çünkü bunu belirledim . Başka #ifndefbir şey çıkarsam, tekrar çalışır. Yeni kodda programlı olarak yanlış bir şey yoktur.

Bir süre kullanılabilir RAM'in sonuna geldiğimden şüphelendim. (Mümkün olsa da) çok fazla yığın kullandığımı sanmıyorum, aslında ne kadar RAM kullandığımı belirlemenin en iyi yolu nedir?

Devam edip çözmeye çalıştığımda, numaralara ve yapılara ulaştığımda sorun yaşıyorum; ne kadar belleğe mal oluyorlar?

ilk düzenleme: AYRICA, başlangıçtan beri eskizimi çok düzenledim, bunlar başlangıçta aldığım gerçek sonuçlar değil, ama şimdi aldığım şeyler.

  text     data     bss     dec     hex filename
 17554      844     449   18847    499f HA15_20140317w.cpp.elf
 16316      694     409   17419    440b HA15_20140317w.cpp.elf
 17346      790     426   18562    4882 HA15_20140317w.cpp.elf

İlk satır (metin 17554 ile) çalışmıyor, fazla düzenleme yapıldıktan sonra ikinci satır (metin 16316 ile) olması gerektiği gibi çalışıyor.

düzenleme: üçüncü satır çalışma, seri okuma, benim yeni fonksiyonları, vb her şeye sahiptir. Aslında bazı küresel değişkenler ve yinelenen kod kaldırıldı. Bundan bahsediyorum çünkü (şüphelenildiği gibi) bu, sae başına bu kodla ilgili değil, RAM kullanımı ile ilgili olmalı. Bu da beni asıl soruya geri döndürüyor, "en iyi nasıl ölçülür" hala bazı cevapları kontrol ediyorum, teşekkürler.

Yukarıdaki bilgileri gerçekten nasıl yorumlayabilirim?

Şimdiye kadar benim anlayışım:

`TEXT` is program instruction memory
`DATA` is variables (unitialised?) in program memory
`BSS`  is variables occupying RAM

BSS 1024 bayttan önemli ölçüde daha az olduğundan, neden ikinci çalışıyor, ancak birincisi çalışmıyor? Eğer DATA+BSSikisi de 1024'ten fazla yer kaplarsa.

yeniden düzenleme: Ben kodu dahil etmek için soruyu düzenledi, ama şimdi gerçekten sorun ile ilgisi yoktu çünkü belki kaldırdım (belki kötü kodlama uygulamaları, değişken bildirimleri ve benzeri dışında). Kodu gerçekten görmek istiyorsanız , düzenlemelere tekrar bakarak inceleyebilirsiniz . Elimdeki soruya geri dönmek istedim.


Ekleyeceğimi düşündüm, son birkaç hafta içinde çeşitli kod bölümleri ekledim, sonra işe başlayana kadar optomize ettim, ama şimdi sadece yarım doz bayt vars ekledim ve işim bitti ... :(
Madivad

StringProgramlarınızda yazı kullanıyor musunuz ? Bunun, yığını bellekte kalmayacağınız noktaya ayırabilen, sık sık dinamik bellek ayırma ve ayırma işlemleri gerçekleştirdiği bilinmektedir.
jfpoilpret

@jfpoilpret StringGenel gider nedeniyle s'den uzak duruyorum . Ben farklı recompiles için içerik uzunluğunu değiştirmek, çünkü tamamen değil BİR bayt dizisi var, dedi, ben hemen hemen her zaman şu anda sabit bir boyut (tüm benim karakter dizileri tanımlamak, char dizileri ile mutlu çalışma değilim.
Madivad

Kodunuzu buraya göndermek (veya çok büyükse macunu) bellekle karşılaştığınız sorunları öğrenebilir.
jfpoilpret

@jfpoilpret Gerçekten kodu gönderemiyorum, çok büyük ve ne yazık ki çok şişirilmiş, 16 dosyaya yayıldı. Gerekli olanın çok ötesinde büyümeme izin verdiğim bir projeydi (bir araya getirilen birkaç proje). Şimdi, sorunu çözmeye yardımcı olacağından eminim, onu ayırmaya başlıyorum. Bazı kısımları olmasına rağmen, insanların bakması (ya da bana rehberlik etmesi) gerekiyor, ancak bunları daha sonra göndereceğim.
Madivad

Yanıtlar:


15

AVRGCC: Yığın Kullanımını İzleme işlevlerini kullanabilirsiniz

İşlevin yığın kullanımını kontrol etmesi amaçlanmıştır, ancak rapor ettiği şey hiç kullanılmamış gerçek RAM'dir (yürütme sırasında). Bunu, RAM'i bilinen bir değerle (0xC5) "boyayarak" (doldurarak) ve ardından kaç baytın hala aynı başlangıç ​​değerine sahip olduğunu sayarak RAM alanını kontrol ederek yapar.
Rapor, kullanılmayan RAM'i gösterecektir (minimum boş RAM) ve bu nedenle kullanılan maksimum RAM'i (Toplam RAM bildirilen RAM) hesaplayabilirsiniz.

İki işlev vardır:

  • StackPaint başlatma sırasında otomatik olarak yürütülür ve RAM'i 0xC5 değeriyle "boyar" (gerekirse değiştirilebilir).

  • Kullanılmayan RAM'i saymak için herhangi bir noktada StackCount çağrılabilir.

İşte bir kullanım örneği. Çok fazla bir şey yapmaz, ancak işlevlerin nasıl kullanılacağını göstermeyi amaçlamaktadır.

// -----------------------------------------------------------------------------
extern uint8_t _end;
extern uint8_t __stack;

void StackPaint(void) __attribute__ ((naked)) __attribute__ ((section (".init1")));

void StackPaint(void)
{
#if 0
    uint8_t *p = &_end;

    while(p <= &__stack)
    {
        *p = 0xc5;
        p++;
    }
#else
    __asm volatile ("    ldi r30,lo8(_end)\n"
                    "    ldi r31,hi8(_end)\n"
                    "    ldi r24,lo8(0xc5)\n" /* STACK_CANARY = 0xc5 */
                    "    ldi r25,hi8(__stack)\n"
                    "    rjmp .cmp\n"
                    ".loop:\n"
                    "    st Z+,r24\n"
                    ".cmp:\n"
                    "    cpi r30,lo8(__stack)\n"
                    "    cpc r31,r25\n"
                    "    brlo .loop\n"
                    "    breq .loop"::);
#endif
} 


uint16_t StackCount(void)
{
    const uint8_t *p = &_end;
    uint16_t       c = 0;

    while(*p == 0xc5 && p <= &__stack)
    {
        p++;
        c++;
    }

    return c;
} 

// -----------------------------------------------------------------------------

void setup() {

Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:
Serial.println(StackCount(), DEC);  // calls StackCount() to report the unused RAM
delay(1000);
}

ilginç bir kod parçası, teşekkürler. Bunu kullandım ve 600+ bayt olduğunu gösteriyor, ancak daha derinlerde gömdüğümde azaldığını, ancak silmediğini gösteriyor. MAYBE benim sorunum başka yerlerde.
Mart'ta Madivad

@Madivad Bu 600+ baytın, StackCount'u çağırdığınızda o ana kadar olan minimum kullanılabilir RAM miktarını temsil ettiğini unutmayın. Eğer çağrıyı ne kadar derin yerleştirdiğinizi gerçekten fark etmez, eğer kod ve iç içe çağrıların çoğunluğu StackCount çağrılmadan önce yapılmışsa sonuç doğru olacaktır. Örneğin, kartınızı bir süre çalışır durumda bırakabilirsiniz (yeterli kod kapsamı elde etmek için veya ideal olarak tanımladığınız yanlış davranışı elde edene kadar) ve rapor edilen RAM'i almak için bir düğmeye basabilirsiniz. Yeterince varsa, sorunun nedeni bu değildir.
Ocak 14:14

1
Teşekkürler @alexan_e, Ekranımda bunu bildiren bir alan oluşturdum, bu yüzden önümüzdeki birkaç gün içinde ilerledikçe, özellikle başarısız olduğunda bu sayıyı ilgi ile izleyeceğim! Tekrar teşekkürler
Madivad


bunun için teşekkürler, farkındayım, bundan bahsedildi. Bildiğim kadarıyla onu kullanmıyorum (bunu kullanan bir kütüphane olabileceğini biliyorum, henüz tam olarak kontrol etmedim).
Mart'ta Madivad

10

Çalışma zamanında bellek kullanımı ile ilgili ana sorunlar şunlardır:

  • dinamik ayırmalar için yığınta kullanılabilir bellek yok ( mallocveya new)
  • bir işlevi çağırırken yığınta yer kalmadı

Her ikisi de aslında AVR SRAM (Arduino'da 2K) ile aynıdır ( program yürütülürken hiçbir zaman boyut değişmeyen statik verilere ek olarak ).

Genellikle, dinamik bellek ayırma, MCU'larda nadiren kullanılır, yalnızca birkaç kütüphane genellikle onu kullanır (bunlardan biri, Stringkullanmadığınızdan bahsettiğiniz sınıftır ve bu iyi bir noktadır).

Yığın ve yığın aşağıdaki resimde görülebilir ( Adafruit'un izniyle ): resim açıklamasını buraya girin

Bu nedenle, en çok beklenen sorun yığın taşmasıdır (yani, yığın yığına doğru büyüdüğünde ve üzerine taştığında ve daha sonra yığın SRAM'ın statik veri bölgesindeki tüm taşmalarda kullanılmadıysa). şunlardan biri için yüksek bir risk altındasınız:

  • veri bozulması (örn. yığın yığın veya statik veriyi yeniler), bu da size anlaşılmaz bir davranış sağlar
  • yığın bozulması (yani yığın veya statik veriler yığın içeriğinin üzerine yazar), genellikle çökmeye neden olur

Yığının üst kısmı ile yığının üst kısmı arasında kalan bellek miktarını bilmek için (aslında, aşağıda gösterildiği gibi aynı görüntüde hem yığını hem de yığını temsil edersek, buna alt diyebiliriz), aşağıdaki işlevi kullanabilirsiniz:

int freeRam () {
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}

Yukarıdaki kodda __brkval, yığının üstüne işaret eder, ancak 0yığının kullanılmadığı zamandır, bu durumda yığının altını işaretleyen ilk değişkeni &__heap_starthangi noktalara işaret ettiğimizi __heap_start; &vtabii ki yığının en üstüne işaret eder (bu, yığına itilen son değişkentir), dolayısıyla yukarıdaki formül, yığının (veya kullanırsanız yığının) büyümesi için kullanılabilir bellek miktarını döndürür.

Bu boyutun, bu boyutun önemli ölçüde azaldığını denemek ve bulmak için kodunuzun çeşitli konumlarında kullanabilirsiniz.

Tabii ki, bu işlevin negatif bir sayı döndürdüğünü görürseniz, çok geç oldu: zaten yığını taştınız!


1
Moderatörler için: bu yazıyı topluluk wiki'sine koyduğum için özür dilerim, yazının ortasında yazarken yanlış bir şeyler yapmış olmalıydım. Lütfen buraya koy, çünkü bu eylem kasıtsızdı. Teşekkürler.
jfpoilpret

Bu cevap için teşekkürler, kelimenin tam anlamıyla sadece bir parça kodunu bir saat önce buldum ( playground.arduino.cc/Code/AvailableMemory#.UycOrueSxfg'nin dibinde ). Ekranımda hata ayıklamak için oldukça geniş bir alanım olduğu için henüz eklemedim (ama yapacağım). Sanırım dinamik olarak bir şeyler atama konusunda kafam karıştı. Mı mallocve newtek yolu bunu yapabilirim? Eğer öyleyse, dinamik bir şeyim yok. Ayrıca, UNO'nun 2K SRAM olduğunu öğrendim. 1K olduğunu düşündüm. Bunları göz önünde bulundurarak, RAM bitmiyor! Başka bir yere bakmam gerek.
Mart'ta Madivad

Ayrıca var calloc. Ama bilmeden dinamik ayırma kullanan 3. taraf kütüphanelerini kullanıyor olabilirsiniz (bundan emin olmak için tüm bağımlılıklarınızın kaynak kodunu kontrol etmeniz gerekir)
jfpoilpret

2
İlginç. Tek "sorun", boş RAM'i çağrıldığı noktada rapor etmesidir, bu nedenle sağ tarafa yerleştirilmedikçe bir yığın aşıldığını fark etmeyebilirsiniz. Sağladığım işlevin o alanda bir avantajı var gibi görünüyor, çünkü min serbest RAM'i o noktaya kadar rapor ediyor, bir RAM adresi kullanıldıktan sonra, artık ücretsiz olarak rapor edilmiyor (aşağı tarafta bazı işgal edilmiş RAM olabilir "boya" değeriyle eşleşen ve boş olarak bildirilen bayt sayısı). Bunun dışında, kullanıcının istediği şeye bağlı olarak belki bir yol diğerinden daha uygun.
alexan_e

İyi bir nokta! Cevabınızdaki bu özel noktayı fark etmedim (ve benim için aslında bir hata gibi görünüyordu), şimdi serbest bölgeyi önceden "boyamak" noktasını görüyorum. Belki bu cevabı cevabınızda daha açık hale getirebilirsiniz?
jfpoilpret

7

Oluşturulan .elf dosyasını geçici dizininizde nasıl bulacağınızı anladığınızda, bir SRAM kullanımını dökmek için aşağıdaki komutu yürütebilirsiniz; burada project.elfoluşturulan .elfdosyayla değiştirilir . Bu çıktının avantajı, SRAM'inizin nasıl kullanıldığını inceleme yeteneğidir . Tüm değişkenlerin küresel olması gerekiyor mu, gerçekten gerekli mi?

avr-objdump -S -j .bss project.elf

project.elf:     file format elf32-avr


Disassembly of section .bss:

00800060 <__bss_start>:
        ...

00800070 <measurementReady>:
        ...

00800071 <cycles>:
        ...

00800073 <measurement>:
  800073:       00 00 00 00                                         ....

00800077 <measurementStart>:
  800077:       00 00 00 00                                         ....

0080007b <timerOverflows>:
  80007b:       00 00 00 00

Aşağıdaki yorumlarda belirtildiği gibi, Ignacio Vazquez-Abrams'ın yığın veya dinamik bellek kullanımını göstermediğine dikkat edin.

Ayrıca bir avr-objdump -S -j .data project.elfkontrol edilebilir, ancak benim programları hiçbiri bununla bir şey çıktı, bu yüzden yararlı olup olmadığını emin olamaz. Bu liste gerekiyordu '(sıfır olmayan) veri başlatıldı.'


Veya sadece kullanabilirsiniz avr-size. Ancak bu size dinamik ayırmalar veya yığın kullanımı göstermez.
Ignacio Vazquez-Abrams

@ IgnacioVazquez-Abrams dinamikleri hakkında, benim çözümüm için de aynı. Cevabımı düzenledi.
Mart'ta jippie

Tamam, bu şimdiye kadarki en ilginç cevap. Denedim avr-objdumpve avr-sizekısa bir süre sonra yazımı düzenleyeceğim. Bunun için teşekkürler.
Mart'ta Madivad

3

Bir süre kullanılabilir RAM'in sonuna geldiğimden şüphelendim. (Mümkün olsa da) çok fazla yığın kullandığımı sanmıyorum, aslında ne kadar RAM kullandığımı belirlemenin en iyi yolu nedir?

Manuel tahminin bir kombinasyonunu kullanmak ve sizeofoperatörü kullanmak en iyisidir . Tüm beyanlarınız statikse, bu size doğru bir resim vermelidir.

Dinamik ayırmalar kullanıyorsanız, belleği ayırmaya başladığınızda bir sorunla karşılaşabilirsiniz. Bunun nedeni, yığın üzerindeki bellek parçalanmasıdır.

Devam edip çözmeye çalıştığımda, numaralara ve yapılara ulaştığımda sorun yaşıyorum; ne kadar belleğe mal oluyorlar?

Bir enum bir int. Yani, bir enumbildiride 10 öğeden oluşan bir kümeniz varsa, bu olurdu 10*sizeof(int). Ayrıca, bir numaralandırma kullanan her değişken basitçe bir int.

Yapılar için, bunu sizeofbulmak en kolay yöntem olacaktır . Yapılar, üyelerinin toplamına eşit (minimum) bir alan kaplar. Derleyici yapı hizalaması yaparsa, daha fazla olabilir, ancak bu olası değildir avr-gcc.


Her şeyi olabildiğince statik olarak atarım. sizeofBu amaçla kullanmayı hiç düşünmemiştim . Şu anda, (küresel olarak) zaten hesaplanmış neredeyse 400 bayt var. Şimdi, enumları (manuel olarak) ve (birkaçına sahip olduğum yapıları - ve kullanacağım sizeof) hesaplayıp rapor edeceğim .
Mayıs 14'te Madivad

sizeofAvrdude IIRC tarafından basıldığından statik verilerinizin boyutunu gerçekten bilmeniz gerektiğinden emin değilim .
jfpoilpret

@jfpoilpret Sanırım versiyona bağlı. Tüm sürümler ve platformlar bunu sağlamaz. Mine (Linux, birden çok sürüm) Mac için bir bellek kullanımı göstermez.
asheeshr

Ayrıntılı çıktıları araştırdım, orada olması gerektiğini düşündüm, değil
Madivad

@AsheeshR Bunun farkında değildim, benimki Windows'da iyi çalışıyor.
jfpoilpret

1

Programınızın kullandığı flaş, SRAM ve EEPROM miktarının düzgün bir şekilde görüntülenmesini sağlayan Arduino Builder adlı bir program var .

Arduino Oluşturucu

Arduino oluşturucu CodeBlocks Arduino IDE çözümünün bir parçasını oluşturur . Bağımsız bir program olarak veya CodeBlocks Arduino IDE aracılığıyla kullanılabilir.

Ne yazık ki Arduino Builder biraz eski ama çoğu program ve Uno gibi çoğu Arduino için çalışmalı.

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.