programing

C에서 메모리 사용량을 줄이기 위한 모범 사례는 무엇입니까?

jooyons 2023. 6. 19. 21:26
반응형

C에서 메모리 사용량을 줄이기 위한 모범 사례는 무엇입니까?

"메모리 효율적인 C 프로그래밍"을 위한 모범 사례는 무엇입니까?주로 내장형/모바일 장치의 경우 메모리 사용량이 적을 때의 지침은 무엇입니까?

a) 코드 메모리 b) 데이터 메모리에 대한 별도의 지침이 있어야 한다고 생각합니다.

C에서는 훨씬 간단한 수준에서 다음을 고려합니다.

  • #pragma pack(1)을 사용하여 구조를 바이트 정렬합니다.
  • 구조물에 다양한 유형의 데이터가 포함될 수 있는 결합 사용
  • int 대신 비트 필드를 사용하여 플래그 및 작은 정수 저장
  • 문자열을 저장하고, 문자열 풀을 구현하고, 포인터를 사용하는 데 고정 길이 문자 배열을 사용하지 않도록 합니다.
  • 열거된 문자열 목록에 대한 참조(예: 글꼴 이름)를 저장할 경우 문자열이 아닌 목록에 인덱스를 저장합니다.
  • 동적 메모리 할당을 사용하는 경우 재할당을 방지하기 위해 필요한 요소의 수를 미리 계산합니다.

임베디드 시스템을 사용하는 데 유용한 몇 가지 제안이 있습니다.

  • 는 룩업테또기상데실다사제음선확다인니합언지는용되를 사용하여 선언되는지 합니다.const.한다면const를 사용하면 데이터를 읽기 전용(예: 플래시 또는 EEPROM) 메모리에 저장할 수 있습니다. 그렇지 않으면 시작 시 데이터를 RAM에 복사해야 하므로 플래시와 RAM 공간을 모두 차지합니다.지도 파일을 생성하도록 링커 옵션을 설정하고 이 파일을 검토하여 메모리 맵에서 데이터가 정확히 할당되는 위치를 확인합니다.

  • 사용 가능한 모든 메모리 영역을 사용하고 있는지 확인합니다.예를 들어 마이크로컨트롤러에는 종종 사용할 수 있는 온보드 메모리가 있습니다(외부 RAM보다 액세스 속도가 빠를 수도 있음).컴파일러 및 링커 옵션 설정을 사용하여 코드와 데이터가 할당되는 메모리 영역을 제어할 수 있어야 합니다.

  • 코드 크기를 줄이려면 컴파일러의 최적화 설정을 확인하십시오.대부분의 컴파일러에는 속도나 코드 크기에 최적화된 스위치가 있습니다.컴파일된 코드의 크기를 줄일 수 있는지 여부를 확인하기 위해 이러한 옵션을 사용하는 것이 좋습니다.그리고 가능한 한 중복 코드를 제거해야 합니다.

  • 시스템에 필요한 스택 메모리 양을 확인하고 그에 따라 링커 메모리 할당을 조정합니다( 질문에 대한 답변 참조).스택 사용량을 줄이려면 스택에 큰 데이터 구조를 배치하지 마십시오("큰" 값이 관련된 경우).

가능한 한 고정점/정수 연산을 사용해야 합니다.많은 개발자들이 간단한 스케일 정수 연산으로 충분할 때 부동소수점 연산(느린 성능과 대규모 라이브러리 및 메모리 사용량과 함께)을 사용합니다.

모두 좋은 추천입니다.여기 제가 유용하다고 생각한 몇 가지 디자인 접근법이 있습니다.

  • 바이트 코딩

특수 목적 바이트 코드 명령 집합에 대한 인터프리터를 작성하고 해당 명령 집합에 가능한 한 많은 프로그램을 작성합니다.특정 작업에 고성능이 필요한 경우 네이티브 코드로 만들고 인터프리터에서 호출합니다.

  • 코드 생성

입력 데이터의 일부가 매우 자주 변경되지 않는 경우 애드혹 프로그램을 만드는 외부 코드 생성기가 있을 수 있습니다.이는 보다 일반적인 프로그램보다 작을 뿐만 아니라 더 빠르게 실행되고 거의 변경되지 않는 입력에 대해 스토리지를 할당할 필요가 없습니다.

  • 데이터 혐오자가 되기

최소한의 데이터 구조를 저장할 수 있다면 많은 사이클을 낭비할 수 있습니다.일반적으로 성능에 문제가 거의 없다는 것을 알게 될 것입니다.

대부분의 경우 알고리즘을 신중하게 선택해야 합니다.메모리 사용량이 O(1) 또는 O(log n)인 알고리즘(즉, 낮음)을 목표로 합니다.들어 조정 배열( 예들어예, 로크조으정할수어레있이는기를지를속적▁(예(어레이:▁for▁arrays▁continue,)은 다음과 같습니다.std::vector대부분의 경우 연결된 목록보다 적은 메모리가 필요합니다.

때로는 조회 테이블을 사용하는 것이 코드 크기와 속도 모두에 더 도움이 될 수 있습니다.LUT에 64개의 항목만 필요한 경우, 큰 sin/cos/tan 함수와 비교하여 sin/cos/tan에 16*4바이트입니다.

압축은 때때로 도움이 됩니다.RLE와 같은 간단한 알고리즘은 순차적으로 읽을 때 압축/해제하기 쉽습니다.

그래픽이나 오디오를 다루는 경우에는 다른 형식을 고려해야 합니다.팔레트 또는 비트 포장* 그래픽은 품질에 대한 좋은 트레이드오프가 될 수 있으며 팔레트는 많은 이미지 간에 공유될 수 있으므로 데이터 크기를 더욱 줄일 수 있습니다.오디오는 16비트에서 8비트 또는 심지어 4비트로 축소될 수 있으며 스테레오는 모노로 변환될 수 있습니다.샘플링 속도는 44.1에서 감소할 수 있습니다.KHz ~ 22kHz 또는 11kHz.이러한 오디오 변환은 데이터 크기(그리고 슬프게도 품질)를 크게 감소시키고 사소한 것입니다(리샘플링을 제외하고는). 하지만 오디오 소프트웨어는 =를 위한 것입니다.

압축을 풀 수 있을 것 같습니다.그래픽을 위한 비트패킹은 일반적으로 각 픽셀이 원래 3개 또는 4개(각각 RGB888 또는 ARGB8888)에서 2바이트(예: RGB565 또는 ARGB155) 또는 1바이트(ARGB232 또는 RGB332)로 들어갈 수 있도록 채널당 비트 수를 줄이는 것을 의미합니다.

고유한 메모리 할당자를 사용하거나 시스템의 할당자를 사용하여 메모리 조각화를 방지합니다.

한 가지 방법은 크기가 다른 개체에 대해 'lab allocator'(: 이 문서 참조)와 여러 메모리 풀을 사용하는 것입니다.

모든 메모리를 사전에 할당하는 것(즉, 시작 초기화를 제외하고 malloc 호출 없음)은 결정론적 메모리 사용에 확실히 도움이 됩니다.그렇지 않은 경우, 서로 다른 아키텍처가 도움이 되는 기술을 제공합니다.예를 들어, 특정 ARM 프로세서는 일반 32비트 대신 16비트 명령을 사용하여 코드 크기를 거의 절반으로 줄이는 대체 명령 집합(썸)을 제공합니다.물론 그렇게 함으로써 속도는 희생됩니다.

일부 구문 분석 작업은 버퍼에 복사하여 구문 분석하는 대신 바이트가 도착할 때 스트림에서 수행할 수 있습니다.

이에 대한 몇 가지 예:

  1. 상태 시스템으로 NMEA 증기를 구문 분석하여 필요한 필드만 훨씬 효율적인 구조로 수집합니다.
  2. DOM 대신 SAX를 사용하여 XML을 구문 분석합니다.

프로젝트를 시작하기 전에 가능한 한 구성 요소별로 사용 중인 메모리 양을 측정하는 방식으로 구축합니다.이러한 방식으로 변경할 때마다 메모리 사용에 미치는 영향을 확인할 수 있습니다.측정할 수 없는 것은 최적화할 수 없습니다.

프로젝트가 이미 완성되어 메모리 제한에 도달하거나(또는 메모리가 적은 장치로 포팅된 경우) 이미 메모리를 사용하고 있는 용도를 확인합니다.

제가 경험한 바로는, 초과 크기의 애플리케이션을 수정할 때 거의 모든 중요한 최적화는 캐시 크기를 줄이고, 일부 텍스처를 제거(물론 이는 이해관계자의 동의를 필요로 하는 기능적 변경이므로 시간 측면에서 효율적이지 않을 수 있음), 오디오를 재샘플링,사용자 지정 할당된 힙의 초기 크기를 줄이고, 임시로만 사용되는 리소스를 확보한 후 필요할 때 다시 로드할 수 있는 방법을 참조하십시오.가끔 여러분은 64바이트의 구조를 발견할 수 있습니다. 16바이트나 그 무엇이든 줄일 수 있습니다. 하지만 그것은 가장 적게 매달린 과일은 거의 아닙니다.하지만 앱에서 가장 큰 목록과 배열이 무엇인지 안다면, 어떤 구조를 먼저 살펴봐야 하는지 알 수 있습니다.

예: 메모리 누수를 찾아 해결합니다.성능을 희생하지 않고 다시 얻을 수 있는 모든 메모리는 훌륭한 시작입니다.

저는 과거에 코드 크기에 대해 걱정하는 데 많은 시간을 보냈습니다.주요 고려 사항은 다음과 같습니다(변경 사항을 확인할 수 있도록 빌드 시점에 측정해야 함).

어떤 코드가 무엇에 의해 참조되는지 확인합니다.전체 XML 라이브러리가 2요소 구성 파일을 구문 분석하기 위해 앱에 연결되어 있는 경우에는 구성 파일 형식을 변경하거나 자체적인 사소한 파서를 작성하는 것을 고려하십시오.가능하면 소스 또는 이진 분석을 사용하여 큰 종속성 그래프를 작성하고 사용자 수가 적은 큰 구성 요소를 찾습니다. 작은 코드 재작성만으로 이러한 구성 요소를 제거할 수 있습니다.외교관 놀이를 준비하십시오. 앱에서 두 개의 다른 구성 요소가 XML을 사용하고 이를 자르려면 현재 신뢰할 수 있는 기성 라이브러리인 수동 롤링의 이점을 두 사람이 납득해야 합니다.

컴파일러 옵션을 엉망으로 만듭니다.플랫폼별 설명서를 참조하십시오.예를 들어 인라인으로 인한 기본 허용 코드 크기 증가를 줄이고자 할 수 있으며, 적어도 GCC에서는 컴파일러에게 일반적으로 코드 크기가 증가하지 않는 최적화만 적용하도록 지시할 수 있습니다.

어댑터 계층 작성을 의미하더라도 가능한 경우 대상 플랫폼에 이미 있는 라이브러리를 활용합니다.위의 XML 예제에서는 OS에서 XML 라이브러리를 사용하기 때문에 대상 플랫폼에 항상 XML 라이브러리가 있는 것을 발견할 수 있습니다. 이 경우 동적으로 연결됩니다.

다른 사람이 언급했듯이 썸 모드는 ARM에 도움이 될 수 있습니다.성능에 중요하지 않은 코드에만 사용하고 중요한 루틴을 ARM에 남겨두면 차이를 알 수 없습니다.

마지막으로, 장치를 충분히 제어할 수 있는 경우에는 교묘한 속임수를 사용할 수 있습니다.UI에서 한 번에 하나의 응용 프로그램만 실행할 수 있습니까?앱에 필요하지 않은 모든 드라이버 및 서비스를 언로드합니다.화면이 이중 버퍼링되지만 앱이 새로 고침 주기에 동기화됩니까?전체 화면 버퍼를 회수할 수 있습니다.

을 추천하는 작은 메모리 소프트웨어: 메모리가 제한된 시스템을 위한 패턴

  • 코드 공간을 줄이기 위해 가능한 한 많은 문자열 상수의 길이를 줄이고 제거합니다.

  • 필요한 경우 알고리즘과 룩업 테이블의 균형을 신중하게 고려합니다.

  • 다양한 종류의 변수가 할당되는 방식에 유의해야 합니다.

    • 상수가 코드 공간에 있을 수 있습니다.
    • 정적 변수는 아마도 고정 메모리 위치에 있을 것입니다.가능한 경우 이러한 작업은 피합니다.
    • 매개 변수는 아마도 스택 또는 레지스터에 저장됩니다.
    • 스택에서 로컬 변수를 할당할 수도 있습니다.최악의 경우 코드의 스택 공간이 부족할 수 있는 경우 큰 로컬 배열이나 문자열을 선언하지 마십시오.
    • 힙이 없을 수도 있습니다. 힙을 관리할 OS가 없을 수도 있습니다.받아들일 수 있습니까?malloc() 기능이 필요합니까?

Embedded Systems Conference에서 이 주제에 대해 발표할 내용이 있습니다.그것은 2001년의 것이지만, 여전히 매우 적용 가능합니다.종이를 보다.

또한 대상 장치의 아키텍처를 선택할 수 있다면 Thumb V2가 있는 현대 ARM 또는 Power와 같은 것으로 이동합니다.VLE가 있는 PC 또는 MIPS16이 있는 MIPS 또는 Infineon TriCore 또는 SH 제품군과 같은 알려진 소형 대상을 선택하는 것은 매우 좋은 옵션입니다.콤팩트한 NEC V850E 제품군은 말할 것도 없습니다.또는 코드 컴팩트성이 우수한 AVR로 이동합니다(단, 8비트 머신임).고정 길이의 32비트 RISC를 제외한 모든 것이 좋은 선택입니다!

다른 사람들이 제안한 사항 외에도 함수에 선언된 로컬 변수는 일반적으로 스택에 할당됩니다.

스택 메모리가 제한되어 있거나 스택 크기를 줄여 힙 또는 글로벌 RAM을 추가할 공간을 확보하려는 경우 다음 사항을 고려하십시오.

  1. 호출 트리를 평평하게 하여 지정된 시간에 스택의 변수 수를 줄입니다.
  2. 큰 로컬 변수를 전역으로 변환합니다(사용되는 스택의 양은 줄지만 전역 RAM의 양은 증가함).변수는 다음과 같이 선언할 수 있습니다.

    • 글로벌 범위:전체 프로그램에 표시됨
    • 파일 범위에서 정적:동일한 파일에 표시됨
    • 기능 범위에서 정적:기능 내에서 볼 수 있음
    • 참고: 그러나 이러한 변경이 이루어진 경우 환경이 있는 경우 코드 문제를 주의해야 합니다.

대부분의 임베디드 시스템에는 스택 오버플로가 탐지되도록 보장하는 스택 모니터 진단 기능이 없으므로 일부 분석이 필요합니다.

PS: 스택 오버플로의 적절한 사용에 대한 보너스?

또 다른 유용한 트릭은 값이 변경되지 않는 변수 대신 #define을 사용하는 것입니다.예를 들어, 어떤 것이 const로 정의된 경우, 사전 프로세서 단계에서 디코딩되고 공간을 차지하지 않으므로 #define으로 대체합니다.정의된 다른 데이터 유형에는 메모리 공간이 필요합니다.이렇게 하면 메모리 사용량을 줄일 수 있습니다.이는 #define이 변수를 처리하지 않으므로 변수 유형이 문제가 되지 않는 경우에만 적용됩니다.

응용 프로그램에서 유용한 한 가지 요령은 기억의 만일의 사태에 대비하는 기금을 만드는 것입니다.시작 시 정리 작업에 충분한 크기의 단일 블록을 할당합니다.malloc/new가 실패할 경우, 비 오는 날 기금을 풀고 사용자에게 자원이 부족하므로 절약해야 한다는 메시지를 게시합니다.이것은 1990년경 많은 맥 애플리케이션에 사용된 기술이었습니다.

메모리 요구 사항을 제한하는 좋은 방법은 동적으로 연결할 수 있는 libc 또는 기타 표준 라이브러리에 가능한 한 많이 의존하는 것입니다.프로젝트에 포함해야 하는 모든 추가 DLL 또는 공유 개체는 쓰기를 방지할 수 있는 상당한 메모리 덩어리입니다.

또한 해당되는 경우 유니언과 비트 필드를 사용하여 프로그램이 작업 중인 데이터의 일부만 메모리에 로드하고 프로그램 크기를 최적화하기 위해 -O(gcc 또는 컴파일러의 동등한) 스위치로 컴파일하는지 확인합니다.

언급URL : https://stackoverflow.com/questions/404615/what-are-some-best-practices-for-reducing-memory-usage-in-c

반응형