C++이나 C#에서 RAM내부의데이터를 다른위치로 옮기는 효율적인 방법이 있을까요?
글쓴이: waka@Google / 작성시간: 목, 2023/04/13 - 11:17오전
RAM에 올라와있는 데이터중 대략 1~3GB정도의 데이터를 RAM의 다른 위치로 옮기는 작업을 하려고 합니다.
(여러번 반복해서 수행합니다)
Parallel.For문 안에서 Buffer.MemoryCopy함수를 이용해서 옮겨보니, CPU점유율도 너무 많이 사용하고, 시간도 오래걸리네요
지금 프로그램에서 다른 연산을 수행하느라 이미 점유율을 8~90%정도 사용하고있어서 리소스를 대기하느라
오래걸리는 것 같기도 합니다.
DMA같은 방식도 찾아보았는데, 이건 주변장치와 통신할때만 가능한 것으로 보이네요.
혹시 CPU점유율을 최소화 하거나, 빠른 속도로 RAM내부의 데이터를 다른 위치로 옮기는 방법을 아시는분이 있으실까요??
Forums:
시간이 얼마나 걸리는 데요? Buffer
시간이 얼마나 걸리는 데요? Buffer.MemoryCopy() 함수를 사용할 때 몇 바이트씩 옮기고 계신가요?
이 문제는 메모리 대역폭 보다는 캐쉬나 최적화 문제와 가깝습니다. 최근 기술의 메모리 대역폭은 정말 빠르거든요.
데이터를 옮길 때 캐쉬에 잘 들어갈 수 있도록 100kb 블럭 단위로 해보시고 사이즈도 점점 키워가면서 테스트해 보세요. 한 5 ~ 10 Mbytes 씩 옮겨도 될 것 같은데요.
50000 * 50000정도의 이미지를 복사하는데,
50000 * 50000정도의 이미지를 복사하는데, 이미지가 1TB정도의 램에 적재되어 있습니다.
이미지가 연속적으로 그려져있지 않아서, 50000을 복사하고 다음위치를 찾아서 50000을 복사하고
이런 복사를 50000번 반복합니다.
Parallel.For문으로 병렬로 복사하면 170~200ms정도 소요되지만, 4~50%정도의 CPU부하가 발생합니다.
For문으로 복사하면 4~500ms정도 소요되고 10%내외의 부하가 발생합니다.
만약에 8~90%의 외부부하가 추가되면 속도는 4~5배정도 더 소요됩니다.
연속적인 데이터가 아니라서, 100kb정도를 한번에 인자로 넣을 수는 없을 것 같은데 다른방법은 없을까요?
그 정도 수준이라면 더 최적화할 수 있는 여지는 많지
그 정도 수준이라면 더 최적화할 수 있는 여지는 많지 않아 보입니다.
다음 위치를 찾는 path를 좀 더 단축하고, 한꺼번에 수 Mbyte씩 옮기는 것이 가능한지 찾아보시라고 하고 싶네요.
이렇게 한번 해보세요. 두부분으로 나누어서 처리하면
이렇게 한번 해보세요. 두부분으로 나누어서 처리하면 좀 더 캐시 활용에 유리한 방향으로 될 수 있습니다.
1) 먼저, 다음 위치를 찾는 작업을 미리 다 수행해서 다음 위치를 배열로 따로 저장
2) 루프 내에서는 배열을 참조해서 메모리 복사만 수행
현재 Copy하는 코드는 아래와 같습니다.
현재 Copy하는 코드는 아래와 같습니다.
public void BufferCopy(InspInfo info)
{
Info = info;
unsafe
{
IntPtr src = (IntPtr)(m_srcPtr.ToInt64() + (info.lY * m_lSrcWidth) + info.lX);
long lsrc = src.ToInt64();
long ldst = m_dstPtr.ToInt64();
Parallel.For(0, info.lChipHeight, (y) =>
{
IntPtr srcPtr = (IntPtr)(lsrc + m_lSrcWidth * y);
IntPtr dstPtr = (IntPtr)(ldst + m_lDstWidth * y);
Buffer.MemoryCopy(srcPtr.ToPointer(), dstPtr.ToPointer(), info.lChipWidth, info.lChipWidth);
});
}
}
이 코드에서,
IntPtr srcPtr = (IntPtr)(lsrc + m_lSrcWidth * y);
IntPtr dstPtr = (IntPtr)(ldst + m_lDstWidth * y); 이렇게 주소 계산하는 코드를
밖에서 배열로 담아서 참조만 하는식으로 계산하라는 말씀이신거죠??
그런데, Buffer.MemoryCopy코드를 한번에 MByte단위로 할 수 있는 방법이 있을까요??
2차원 이미지에서 특정 영역만 골라서 Copy하는 방식이라서 어떻게 해야할지 모르겠네요 ㅠ
맞습니다. 따로 주소 계산 결과를 배열로 저장하는
맞습니다. 따로 주소 계산 결과를 배열로 저장하는 것을 먼저 수행하고 그 다음에 이를 참조해서 메모리 복사만 수행하는 것입니다.
복사하는 주소 연산의 결과가 최대한 근접하면 좋습니다. 계산 결과를 의도적으로 이것을 감안해서 낸다면 캐시 활용에 좋은 결과를 낼 것이고요. 예를 들어 한 영역을 복사하고 다음 영역을 복사할 때 가능한 근접한 주소 영역이 선택되도록 하는 것이 좋습니다. ABCDEF.....XYZ 이렇게 주소 영역이 serialize되어 있다고 한다면 가장 안좋은 방법이 AZBYCX.... 이런식으로 점프해서 액세스하는 것이 되겠죠.
IntPtr srcPtr = (IntPtr)(lsrc
IntPtr srcPtr = (IntPtr)(lsrc + m_lSrcWidth * y);
IntPtr dstPtr = (IntPtr)(ldst + m_lDstWidth * y);
메모리 주소계산 코드가 이렇다면..
데이터가 모여있다는것 아닌가요?
예를들면, 첫줄은 y = ax + b와 형식이 같네요
예를들면, 첫줄은 y = ax + b와 형식이 같네요.
a(기울기)가 충분히 크면 x의 사소한 변화에도 큰 결과값의 변화가 생기게 됩니다.
그 변화가 캐쉬의 여유 공간보다 크게 되면 캐쉬를 비우고 다시 채우고, 또 비우고 다시 채우는 일이 반복됩니다. 성능 저하가 예상될 수 있죠.
IntPtr srcPtr = (IntPtr)(lsrc
IntPtr srcPtr = (IntPtr)(lsrc + m_lSrcWidth * y);
IntPtr dstPtr = (IntPtr)(ldst + m_lDstWidth * y);
메모리 주소계산 코드가 이렇다면..
데이터가 모여있다는것 아닌가요?
실수 했습니다.
실수 했습니다.
lChipWidth 값에 따라 연속적이 아닐 수 있겠네요.
잘못알고 글을 썼습니다.
댓글 달기