참고
삼성 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와 비슷한 속도를 낼 수 있다.
'알고리즘 > [EXP] 삼성 SW 역량 테스트 C형' 카테고리의 다른 글
2차원 배열 탐색과 캐시 미스 (Cache Misses in 2D Arrays) (0) | 2023.08.15 |
---|---|
타입 캐스팅으로 한 번에 메모리 쓰기, 읽기 (Memory Write and Read with Type Casting) (0) | 2023.08.15 |
간단한 윤곽선 검출로 정사각형 찾기 (Find Squares with Simple Edge Detection) (0) | 2023.08.14 |
메모리 주소를 기록하여 배열에 접근하기 (0) | 2023.07.30 |
비트 압축 - 37.5% 압축하기 2 (16bit : 10bit ~) (0) | 2023.07.30 |
댓글