너무 많은 RAM을 사용하고 있습니다. 이것을 어떻게 측정 할 수 있습니까?


19

내가 알 수있는 한, 프로젝트에서 사용중인 RAM의 양을 알고 싶습니다. 실제로 직접 계산하고 계산하는 것 외에는 실제로 해결할 수있는 방법이 없습니다. 나는 RAM이 부족하다고 판단한 다소 큰 프로젝트의 무대에 도달했습니다.

섹션을 추가 할 수 있고 명백한 이유없이 코드의 다른 곳에서 느슨해지기 때문에 이것을 결정했습니다 . #ifndef다른 것이 있으면 다시 작동합니다. 새 코드에는 프로그래밍 방식으로 문제가 없습니다.

사용 가능한 RAM이 끝날 것으로 의심되었습니다. 너무 많은 스택을 사용하고 있다고 생각하지 않습니다 (가능하더라도) 실제로 사용중인 RAM의 양을 결정하는 가장 좋은 방법은 무엇입니까?

통과하고 그것을 해결하려고 노력하면서 열거 형과 구조체에 도달 할 때 문제가 있습니다. 메모리 비용은 얼마입니까?

첫 번째 편집 : 또한 시작한 이후 스케치를 너무 많이 편집했습니다. 이것은 처음에 얻은 실제 결과는 아니지만 지금 얻는 결과입니다.

  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

첫 번째 줄 (텍스트 17554 포함)이 작동하지 않았으며, 많은 편집 후 두 번째 줄 (텍스트 16316 포함)이 정상적으로 작동합니다.

편집 : 세 번째 줄에는 모든 작업, 직렬 판독, 새로운 기능 등이 있습니다. 본질적으로 일부 전역 변수와 중복 코드를 제거했습니다. 이 코드는 sae 당이 코드가 아니기 때문에 RAM 사용과 관련이 있기 때문에 이것을 언급합니다. 원래 질문으로 돌아가서 "최선의 측정 방법"으로 돌아가서 여전히 몇 가지 답변을 확인하고 있습니다. 감사합니다.

위의 정보를 실제로 어떻게 해석합니까?

지금까지 내 이해는 :

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

BSS가 1024 바이트보다 적기 때문에 두 번째는 왜 작동하지만 첫 번째는 그렇지 않습니까? 그렇다면 DATA+BSS둘 다 1024 이상을 차지합니다.

다시 편집 : 코드를 포함하도록 질문을 편집했지만 문제와 실제로 관련이 없기 때문에 코드를 제거했습니다 (잘못된 코딩 방법, 변수 선언 등). 당신은 당신이 경우 편집을 통해 다시보고 코드를 검토 할 수 있습니다 정말 그것을보고 싶어요. 나는 RAM 문제를 측정하는 방법에 대한 더 기반을 둔 당면한 질문으로 돌아가고 싶었습니다.


추가 할 것이라고 생각했는데 지난 몇 주 동안 코드의 다양한 새 섹션을 추가 한 다음 작동 할 때까지 선택했지만 이제는 절반의 doz 바이트 변수 만 추가했으며 완료했습니다 ... :(
Madivad

당신 String은 당신의 프로그램에서 타입 을 사용 합니까? 이것은 빈번한 동적 메모리 할당 및 릴리스를 수행하는 것으로 알려져 있으며, 이로 인해 힙이 남지 않은 지점으로 힙이 조각화 될 수 있습니다.
jfpoilpret

@jfpoilpret String오버 헤드로 인해 s 에서 멀리 떨어져 있습니다 . 나는 char 배열로 작업하는 것을 기쁘게 생각합니다. 거의 모든 char 배열을 고정 된 크기로 정의합니다 (현재는 다른 재 컴파일을 위해 컨텐츠 길이를 변경하기 때문에 순전히 ONE 바이트 배열을 갖습니다)
Madivad

여기에 코드를 게시하면 (혹은 너무 큰 경우 pastebin에) 메모리에 어떤 문제가 있는지 알아볼 수 있습니다.
jfpoilpret

@jfpoilpret 실제로 코드를 게시 할 수는 없습니다. 불행히도 엄청나게 부풀어 오른 16 파일 이상. 그것은 내가 요구했던 것 이상으로 잘 자랄 수 있었던 프로젝트였습니다 (여러 프로젝트가 합쳐졌습니다). 이제 문제를 해결하는 데 도움이 될 것입니다. 그것의 일부가 있지만 사람들이 보거나 나를 안내해야하지만 나중에 게시 할 것입니다.
Madivad

답변:


15

제공된 기능을 사용할 수 있습니다. AVRGCC : 스택 사용량 모니터링

이 함수는 스택 사용량을 확인하기위한 것이지만보고 된 것은 실제로 실행 된 적이없는 실제 RAM입니다 (실행 중). 알려진 값 (0xC5)으로 RAM을 "페인팅"(채우기) 한 다음 여전히 동일한 초기 값을 갖는 바이트 수를 세는 RAM 영역을 확인하여이를 수행합니다.
이 보고서에는 사용되지 않은 RAM (최소 여유 RAM)이 표시되므로 사용 된 최대 RAM (총 RAM-보고 된 RAM)을 계산할 수 있습니다.

두 가지 기능이 있습니다.

  • StackPaint 는 초기화 중에 자동으로 실행되고 0xC5 값으로 RAM을 "페인트"합니다 (필요한 경우 변경 가능).

  • 사용하지 않은 RAM을 계산하기 위해 언제라도 StackCount 를 호출 할 수 있습니다.

다음은 사용법의 예입니다. 많이하지는 않지만 기능을 사용하는 방법을 보여주기위한 것입니다.

// -----------------------------------------------------------------------------
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);
}

흥미로운 코드 조각입니다. 감사합니다. 나는 그것을 사용했고, 600 바이트 이상을 사용할 수 있다고 제안하지만, 더 깊은 서브 서브에 매립하면 감소하지만 닦지는 않습니다. 따라서 내 문제는 다른 곳일 수 있습니다.
Madivad

@Madivad이 600+ 바이트는 StackCount를 호출 할 때까지 사용 가능한 최소 RAM 크기를 나타냅니다. StackCount를 호출하기 전에 대부분의 코드 및 중첩 된 호출이 실행 된 경우 결과가 정확할 경우 호출을 얼마나 깊게 할 것인지는 실제로 차이가 없습니다. 예를 들어, 충분한 코드 적용 범위를 확보하거나 이상을 설명 할 때까지 이상적으로는 보드를 잠시 동안 작동 상태로 유지 한 다음 버튼을 눌러보고 된 RAM을 가져올 수 있습니다. 충분하면 문제의 원인이 아닙니다.
alexan_e

1
@alexan_e에게 감사드립니다. 저는 디스플레이에 지금 이것을보고하는 영역을 만들었습니다. 앞으로 며칠 동안 진행할수록 특히 실패 할 때이 숫자를 관심있게 볼 것입니다! 다시 한번 감사드립니다
Madivad


그 점에 대해 감사합니다. 내가 아는 한, 나는 그것을 사용하지 않고있다 (나는 그것을 사용하는 라이브러리가있을 수 있다는 것을 알고있다. 나는 아직 완전히 점검하지 않았다).
Madivad

10

런타임시 메모리 사용량과 관련하여 발생할 수있는 주요 문제는 다음과 같습니다.

  • 동적 할당을 위해 에 사용 가능한 메모리가 없음 ( malloc또는 new)
  • 함수를 호출 할 때 스택 에 공간이 남아 있지 않습니다

둘 다 실제로 AVR SRAM (Arduino의 2K)이 두 프로그램 모두에 사용되는 것과 동일 합니다 ( 프로그램 실행 중에 크기가 변하지 않는 정적 데이터 외에 ).

일반적으로 동적 메모리 할당은 MCU에서 거의 사용되지 않으며 일반적으로 소수의 라이브러리 만 사용합니다 (이 중 하나는 String사용하지 않는 클래스이므로 좋은 지적입니다).

스택과 힙은 아래 그림에서 볼 수 있습니다 ( Adafruit 제공 ). 여기에 이미지 설명을 입력하십시오

따라서 가장 예상되는 문제는 스택 오버플로 (스택이 힙쪽으로 커져서 오버플로가 발생한 경우)-그리고 힙이 전혀 사용되지 않은 경우 SRAM의 정적 데이터 영역에서 오버플로가 발생한 경우입니다. 다음 중 하나의 위험이 높습니다.

  • 데이터 손상 (즉, 스택이 힙 또는 정적 데이터를 기록함)으로 이해할 수없는 동작
  • 스택 손상 (즉, 힙 또는 정적 데이터가 스택 내용을 덮어 씁니다)으로 인해 일반적으로 충돌이 발생합니다

힙의 상단과 스택의 상단 사이에 남은 메모리의 양을 알기 위해 (실제로, 아래에 묘사 된 것과 동일한 이미지에서 힙과 스택을 모두 나타내는 경우 하단을 호출 할 수 있습니다) 다음 기능을 사용할 수 있습니다 :

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

위의 코드에서, __brkval힙의 정상 점은 있지만 0힙이 사용되지 않은 경우,이 경우에 우리가 사용 &__heap_start되는 점을 __heap_start제 1 가변, 즉 마크 힙의 저부; &v물론 스택의 맨 위 (이것은 스택에서 마지막으로 푸시 된 변수 임)이므로 위의 수식은 스택 (또는 사용하는 경우 힙)이 커질 수있는 메모리 양을 반환합니다.

코드의 다양한 위치에서이 기능을 사용하여이 크기가 크게 줄어드는 위치를 찾아 볼 수 있습니다.

물론,이 함수가 음수를 반환하는 것을 본다면 너무 늦습니다 : 이미 스택을 오버플로했습니다!


1
중재자에게 :이 글을 커뮤니티 위키에 올리게되어 유감입니다. 글 중간에 타이핑하는 동안 뭔가 잘못되었을 것입니다. 의도하지 않은 조치이므로 여기에 다시 넣으십시오. 감사.
jfpoilpret

이 답변 덕분에 말 그대로 단지 한 시간 전에 ( 놀이터 의 바닥에서 . 디스플레이에 디버깅 할 영역이 상당히 많기 때문에 아직 포함 시키지는 않았습니다. 나는 물건을 동적으로 할당하는 것에 대해 혼란스러워했다고 생각합니다. 가 malloc하고 new있는 유일한 방법은 내가 그렇게 할 수 있습니까? 그렇다면 나는 역동적 인 것이 없습니다. 또한 UNO에 2K의 SRAM이 있다는 것을 알게되었습니다. 나는 그것이 1K라고 생각했다. 이것들을 고려할 때, 나는 RAM이 부족하지 않습니다! 다른 곳을 봐야합니다.
Madivad

또한 있습니다 calloc. 그러나 당신은 당신이 모르고 동적 할당을 사용하여 제 3 자 libs와 사용 될 수있다 (당신은 확실히 그것에 대해 할 모든 종속의 소스 코드를 확인해야 할 것)
jfpoilpret

2
흥미 롭군 유일한 "문제점"은 호출 된 시점에서 사용 가능한 RAM을보고하므로 올바른 부분에 배치하지 않으면 스택 오버런이 발생하지 않을 수 있습니다. 내가 제공 한 기능은 그 시점까지 최소 여유 RAM을보고하기 때문에 해당 영역에서 이점이있는 것으로 보입니다 .RAM 주소가 사용되면 RAM이 더 이상 사용되지 않는 것으로보고되지 않습니다 (아래쪽에 일부 점유 된 RAM이있을 수 있음) "페인트"값과 일치하고 사용 가능한 것으로보고 된 바이트). 그 외에도 사용자가 원하는 것에 따라 한 가지 방법이 다른 방법보다 더 적합 할 수 있습니다.
alexan_e

좋은 지적! 나는 당신의 대답 에서이 특정 지점을 알지 못했습니다 (그리고 실제로 버그처럼 보였습니다). 이제 자유 영역을 "페인팅"하는 지점이 선행됩니다. 아마도 당신은 당신의 대답 에서이 점을 더 분명하게 만들 수 있습니까?
jfpoilpret

7

임시 디렉토리에서 생성 된 .elf 파일을 찾는 방법을 알아낼 때 아래 명령을 실행하여 SRAM 사용을 덤프 할 수 있습니다. 여기서 project.elf생성 된 .elf파일 로 대체 됩니다. 이 출력의 장점은 SRAM 사용 방법 을 검사 할 수 있다는 것입니다 . 모든 변수가 전역 변수 여야합니까? 실제로 모두 필요합니까?

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

아래 주석에서 언급 한 Ignacio Vazquez-Abrams와 같이 스택 또는 동적 메모리 사용은 표시되지 않습니다.

또한를 avr-objdump -S -j .data project.elf확인할 수는 있지만 내 프로그램 중 아무것도 아무것도 출력하지 않으므로 유용한 지 여부를 알 수 없습니다. 이 목록에 가정 '0이 아닌 데이터를 초기화'.


또는 당신은 그냥 사용할 수 있습니다 avr-size. 그러나 동적 할당이나 스택 사용량은 표시되지 않습니다.
Ignacio Vazquez-Abrams

@ IgnacioVazquez-Abrams는 역학에 관한 것으로 내 솔루션과 동일합니다. 내 답변을 수정했습니다.
jippie

좋아, 이것은 지금까지 가장 흥미로운 답변입니다. 실험을 avr-objdump했으며 avr-size곧 위의 게시물을 편집하겠습니다. 고마워
Madivad

3

사용 가능한 RAM이 끝날 것으로 의심되었습니다. 너무 많은 스택을 사용하고 있다고 생각하지 않습니다 (가능하더라도) 실제로 사용중인 RAM의 양을 결정하는 가장 좋은 방법은 무엇입니까?

수동 추정과 sizeof연산자 를 사용하여 조합하는 것이 가장 좋습니다 . 모든 선언이 정적 인 경우 정확한 그림을 제공해야합니다.

동적 할당을 사용하는 경우 메모리 할당 해제를 시작하면 문제가 발생할 수 있습니다. 이는 힙의 메모리 조각화 때문입니다.

통과하고 그것을 해결하려고 노력하면서 열거 형과 구조체에 도달 할 때 문제가 있습니다. 메모리 비용은 얼마입니까?

열거 형은만큼 많은 공간을 차지합니다 int. 따라서 enum선언 에 10 개의 요소 세트가 있으면 그 값은입니다 10*sizeof(int). 또한 열거 형을 사용하는 모든 변수는 단순히입니다 int.

구조의 경우 가장 쉽게 sizeof찾을 수 있습니다. 구조는 멤버의 합과 같은 (최소) 공간을 차지합니다. 컴파일러가 구조 정렬을 수행하는 경우 더 많을 수 있지만의 경우에는 그렇지 않습니다 avr-gcc.


가능한 한 모든 것을 정적으로 할당합니다. 나는 sizeof이 목적 으로 사용하려고 생각하지 않았습니다 . 현재 거의 400 바이트가 이미 (전 세계적으로) 설명되어 있습니다. 이제 열거 형 (수동)과 구조체 (몇 가지를 가지고 있으며 사용할 것입니다 sizeof)를 계산하고 다시보고합니다.
Madivad

확실히 당신은 정말 필요하지 않음 sizeof이이 avrdude IIRC 인쇄됩니다로 정적 데이터의 크기를 알 수 있습니다.
jfpoilpret

@jfpoilpret 버전에 따라 다릅니다. 모든 버전과 플랫폼이이를 제공하지는 않습니다. 내 (Linux, 여러 버전)는 하나의 메모리 사용을 나타내지 않지만 Mac 버전은 메모리 사용을 보여줍니다.
asheeshr

장황한 결과물을 검색했는데, 거기에 있어야한다고 생각했습니다. 그렇지 않습니다.
Madivad

@AsheeshR 나는 그것을 알지 못했다. 내 Windows에서 잘 작동한다.
jfpoilpret

1

프로그램 에서 사용중인 플래시, SRAM 및 EEPROM의 양을 깔끔하게 시각화하는 Arduino Builder 라는 프로그램이 있습니다.

아두 이노 빌더

Arduino 빌더는 CodeBlocks Arduino IDE 솔루션의 일부를 구성합니다 . 독립형 프로그램 또는 CodeBlocks Arduino IDE를 통해 사용할 수 있습니다.

불행히도 Arduino Builder는 약간 오래 되었지만 Uno와 같은 대부분의 프로그램과 대부분의 Arduino에서 작동합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.