본문 바로가기
개념공부/임베디드 C 프로그래밍 최적화

임베디드 C 프로그래밍 속도 최적화 - 포인터 최적화

by Zach Choi 2024. 7. 10.
728x90
반응형

1. 함수의 인자로 포인터를 사용해 속도를 향상하자.

 구조체처럼 부피가 큰 것은 함수의 인자로 넘길 때 call by value, call by reference 중 어떤 방식을 사용해야 할까. 구조체를 함수의 인자로 넘기는 call by value의 경우, 인자를 복사해야 하므로 속도와 메모리 자원이 추가로 필요하다.반면, 포인터를 인자로 넘겨 call by reference 방식을 사용하면 속도 / 메모리 모두 이득을 볼 수 있다.

 

아래는 예시 코드 이다. int, float, char 변수를 멤버로 가진 구조체 Test가 있다. 이를 call by value와 call by reference 방식으로 foo, boo 함수에서 넘겨 받는 코드이다.

#include <stdio.h>

void foo(struct Test x);
void boo(struct Test* x);

struct Test {
	int a;
	float b;
	char c;
};

void main() {
	struct Test T1;
	T1.a = 3;
	T1.b = 4.0;
	T1.c = 'a';

	foo(T1);
	boo(&T1);

}

void foo(struct Test x)
{
	printf("%d, %f, %c\n", x.a, x.b, x.c);
}

void noo(struct Test* x)
{
	printf("%d, %f, %c\n", x->a, x->b, x->c);
}

 

 코드의 성능을 확인하기 위해 디스어셈블리 코드를 보자. call by value로 인자를 받는 foo() 함수의 경우, 총 10개의 연산이 수행된다. 구조체의 크기가 12byte 이므로 4byte씩 데이터를 읽어서 스택에 저장하는 동작이 3번 수행된다. 이 동작은 main() 함수가 사용하는 스택에서 구조체의 데이터를 읽어오는 명령과 foo() 함수가 사용하는 스택에 저장하는 명령, 2가지 동작으로 구성된다.

 

 

[IDE] 비주얼 스튜디오 디스어셈블리 코드 확인 방법

C, C++ 코드 성능을 확인하거나 동작 방식을 로우 레벨에서 확인해야할 때 어셈블리 코드를 직접 보는 것이 도움된다. Visual Studio 에서는 컴파일된 어셈블리 코드를 확인할 수 있는 창을 제공한다.

iridescentboy.tistory.com

 

 반면, call by reference로 인자를 받는 boo() 함수의 경우, 4개의 연산이 수행된다. 포인터는 4byte 이므로 포인터 값을 읽어 스택에 저장하는데 2개의 명령만 필요하다. 

 

 아래 디스 어셈블리 코드를 통해 call by value로 인자를 호출하는 것이 call by reference 보다 더 많은 자원을 사용하는 것을 알 수 있다.

 

 

 크기가 4 byte를 넘는 구조체 뿐만 아니라 배열, 문자열, 구조체 배열, 함수 또한 call by reference를 사용하는 것이 바람직하다.


728x90

 

2. 포인터를 빠르게 하는 방법 : 포인터 체인 제거

 포인터 체인이란 구조체 멤버의 멤버의 멤버의 멤버에 접근하는 것을 말한다. 포인터 체인은 컴파일러가 하나의 이름으로 인식하지 못하고 각 코드 라인 별 값을 읽을때마다 포인터를 다시 읽어온다. 그러므로 체인은 제거하는 것이 좋다.

 

 아래에서 Rect 구조체 내 a 멤버에 값을 저장할 때는 포인터 체인을 사용한다. 반면, b 멤버에 값을 저장할 때는 지역변수를 사용한다. 지역변수를 사용해 포인터 체인을 제거하면 반복적인 메모리 엑세스를 줄일 수 있다.

#include <stdio.h>


struct Point {
	int x, y, z;
};
struct Line {
	struct Point a, b;
};
struct Rectangle {
	struct Line line;
};

void main() {
	struct Rectangle Rect = { 0 };

	Rect.line.a.x = 1; // 포인터 체인
	Rect.line.a.y = 2;
	Rect.line.a.z = 3;

	struct Point Pnt = Rect.line.b;

	Pnt.x = 1; // 포인터 체인 제거
	Pnt.y = 2;
	Pnt.z = 3;
}

 

 


참조 : 임베디드 프로그래밍 C코드 최적화, 김유진 저, 한빛미디어

728x90
반응형