본문 바로가기
알고리즘/[EXP] 삼성 SW 역량 테스트 C형

타입 캐스팅으로 deep copy, memcpy 구현하기 (Memory Copy with Type Casting)

by 피로물든딸기 2023. 8. 15.
반응형

삼성 C형 전체 링크

 

참고

- 타입 캐스팅으로 한 번에 메모리 쓰기, 읽기

 

삼성 C형에서는 string.h 라이브러리를 사용할 수 없기 때문에 memcpy를 사용할 수 없다.

그래서 배열을 복사할 때, for문을 이용해 직접 복사한다.

 

그런데 임시 변수 없이 변수 바꾸기에서 long 타입을 캐스팅하여 배열을 한꺼번에 교체하였다.

이 방법을 더 확장하여 더 빠른 copy를 메모리 casting으로 구현할 수 있다.


1차원 배열 복사 - 배열 복사

 

먼저 단순 복사 코드를 보고 실행 시간을 확인해 보자. (SW Expert Academy 문제에서 비교)

char 1000개의 배열 a를 for를 이용해 b에 복사한다.

#include <stdio.h>
#include <string.h> /* memcpy test */

#define MAX (1000)

typedef unsigned long long int ull;

typedef struct st
{
	char copy[MAX];
}CAST;

int main(void)
{
	char a[MAX];
	char b[MAX];

	for (register int i = 0; i < 1000; i++) a[i] = i;
	
	for (register int tc = 0; tc < 10000000; tc++)
	{
		for (register int i = 0; i < 1000; i++)
			b[i] = a[i];
	}

	for (register int i = 0; i < 1000; i++)
		if (a[i] != b[i]) printf("wrong!\n");

	printf("pass\n");

	return 0;
}

배열을 하나씩 옮기는 것은 약 7초 정도 나왔다.


1차원 배열 복사 - 타입 캐스팅

 

char 1개를 복사한다는 것은 결국 메모리 1byte를 복사하는 것과 같다.

따라서 type casting을 이용하여 long(8byte)으로 변경하면 8byte씩 복사가 가능해진다.

for (register int tc = 0; tc < 10000000; tc++)
{
	for (register int i = 0; i < 1000; i += 8 /* 8바이트 복사 */)
		*(ull*)&b[i] = *(ull*)&a[i];
}

 

(ull*)는 b[i]의 주소부터 unsigned long long int로 읽겠다는 것이고, 포인터에 값을 쓰려면 * 연산자를 앞에 추가한다.

a[i]의 주소부터 8바이트 주소를 덮어씌우게 되므로, i++는 i += 8로 변경해야 한다.

 

char 복사보다 8배 큰 복사를 하게 되어 실행 시간이 1 / 5 정도 줄어들었다.


1차원 배열 복사 - register + pointer

 

시험 환경에서는 register를 이용할 수 있으므로 register 포인터로 변경해서 복사할 수도 있다.

for (register int tc = 0; tc < 10000000; tc++)
{
	register char* p = a;
	register char* q = b;

	for (register int i = 0; i < 1000; i++) 
		*q++ = *p++;	
}

 

하지만 유의미하게 빨라지지는 않는다.

 

보통 포인터로 다시 선언하는 것이 유리한 경우는

구조체 내부에 배열이 있어서 접근을 조금이라도 줄이고 싶을 때 사용한다.

예시는 다음과 같다.

typedef struct st
{
	int len;
	char data[4096 + 1000];
	struct st *prev;
	struct st *next;
}NODE;

register char* data = &node->data[0];

 

하지만 long long type으로 포인터를 잡은 후 1000 → 125번만 실행하게 되면, 꽤 빨라진다.

for (register int tc = 0; tc < 10000000; tc++)
{
	register ull* p = (ull*)a;
	register ull* q = (ull*)b;

	for (register int i = 0; i < 125; i++) 
		*q++ = *p++;	
}


1차원 배열 복사 - 구조체 타입 캐스팅

 

long type으로 타입 캐스팅을 했을 때, 속도가 좀 더 빨라졌다.

그렇다면, 이제 더 큰 byte로 캐스팅을 하면 더 빠르지 않을까 생각해 볼 수 있다.

그런데 본 타입은 long = 8바이트가 가장 크다.

따라서 더 큰 type을 구조체로 만든다.

typedef struct st
{
	char copy[MAX];
}CAST;

 

MAX = 1000이므로 1000byte의 타입이 만들어졌다.

따라서 char 1000바이트를 한 번에 복사할 수 있게 되었다.

for (register int tc = 0; tc < 10000000; tc++)
{
	*(CAST*)&b = *(CAST*)&a;
}

메모리를 통째로 복사했기 때문에 실행 시간이 훨씬 줄어들었음을 알 수 있다.

 

이러한 메모리 복사의 방법으로 reinterpret_cast(c++만 사용 가능)이란 것을 사용할 수도 있다.

reinterpret_cast는 형 변환이 이루어질 때, 해당 자료형의 bit 수만큼 casting 된다.

for (register int tc = 0; tc < 10000000; tc++)
{
	*(reinterpret_cast<CAST*>(b)) = *(reinterpret_cast<CAST*>(a));
}

속도의 차이가 크게 나지 않으므로 편한 방법을 사용하면 된다.

 

마지막으로 memcpy와의 속도 비교를 해보자.

for (register int tc = 0; tc < 10000000; tc++)
{
	memcpy(b, a, sizeof(a));
}

 

memcpy가 더 빠르지만, B형(C형)에서 사용할 수 없음을 감안하면, cast 복사가 나쁜 속도는 아니다.


3차원 배열 복사

 

CAST 복사는 N차원 배열에서도 가능하다.

먼저 단순 복사를 해보자.

#include <stdio.h>
#include <string.h> /* memcpy test */

#define MAX (30)

typedef struct st
{
	int copy[MAX][MAX][MAX];
}CAST;

int main(void)
{
	int a[MAX][MAX][MAX];
	int b[MAX][MAX][MAX];

	for (register int i = 0; i < MAX;i++)
		for (register int j = 0; j < MAX; j++)
			for (register int k = 0; k < MAX; k++)
				a[i][j][k] = i + j + k;
	
	for (register int tc = 0; tc < 10000; tc++)
	{
		for (register int i = 0; i < MAX;i++)
			for (register int j = 0; j < MAX; j++)
				for (register int k = 0; k < MAX; k++)
					b[i][j][k] = a[i][j][k];
	}
	
	for (register int i = 0; i < MAX;i++)
		for (register int j = 0; j < MAX; j++)
			for (register int k = 0; k < MAX; k++)
				if (b[i][j][k] != (i + j + k)) printf("wrong!\n");

	printf("pass\n");

	return 0;
}

 

이제 구조체 CAST를 이용하여 전체 복사를 해보자. 실행 시간이 많이 줄어들게 된다.

	for (register int tc = 0; tc < 10000; tc++)
	{
		*(CAST*)b = *(CAST*)a;
	}

 

이제 copy 부분을 memcpy를 이용해서 바꿔보자.

for (register int tc = 0; tc < 10000; tc++)
{
	memcpy(b, a, sizeof(a));
}


결론 : C형에서 memory copy를 할 때, 구조체를 이용해서 casting하면 memcpy와 비슷한 속도를 낼 수 있다.

반응형

댓글