직접 짠 malloc/free 최적화 도와주세요
얼마전 자유게시판에 C/Java 속도비교글 올렸던 사람입니다. 관련해서 올립니다.
자잘한 메모리 동적 할당을 아주 많이 하게될 프로젝트가 있습니다. 기가바이트 단위로 트리를 만들었다 지웠다 하면서 연산을 해야합니다. C로 짜지 않으면 안되겠다 했는데 테스트를 해 보니 MinGW/GCC에서 기본으로 제공하는 C 라이브러리의 malloc/free 함수가 너무 느린것 같습니다.
[코드 1]: C로 짠 테스트코드
[코드 2]: Java로 짠 테스트코드
[코드 3]: 직접 짠 malloc/free 함수
[코드 1]을 컴파일 할 때에 매크로 MJ_ALLOCATOR
를 정의해주면 제가 짠 malloc/free 함수를 대신 사용하게 됩니다. 지금 문제는 malloc/free함수의 성능이 기본버전이나 제가 짠 버전이나 Java의 메모리 관리자보다 많이 느립니다. 아래는 테스트 결과입니다.
운영체제: Windows 7
언어: C
malloc/free 라이브러리: 기본 (msvcrt?)
최적화옵션: -O3 -march=native
실행시간 (초): 120 +/- 1
언어: C
malloc/free 라이브러리: 자작
최적화옵션: -O3 -march=native
실행시간 (초): 24 +/- 1
언어: Java
실행시간 (초): 12 +/- 1
[코드 3]에서 조금이라도 속도를 빠르게 할수 있는 부분이 보인다면 도와주세요^^. 미리 감사합니다~
[코드 1]
#include <stdio.h> #include <stdlib.h> #include <time.h> #ifdef MJ_ALLOCATOR #include "mj_allocator.h" #endif enum {ARRAY_LEN = 50000}; int *array[ARRAY_LEN]; long long sum = 0; int confuse_the_compiler(int n) { return n % 10 > 5 ? -n / 2 : n * 2; } void start() { for (int i = 1; i <= ARRAY_LEN; ++i) { for (int j = 0; j < i; ++j) { array[j] = (int *)malloc(sizeof(int)); array[j][0] = j + 1; } for (int j = i - 1; j >= 0; --j) { sum -= j; int n = confuse_the_compiler(array[j][0]); if (n != 0) { sum += n; free(array[j]); } } } } int main() { #ifdef MJ_ALLOCATOR use_mj_allocator(ARRAY_LEN * sizeof(int)); #endif time_t start_time = clock(); start(); time_t end_time = clock(); printf("\nelapsed time: %.3f\n", (double)(end_time - start_time) / CLOCKS_PER_SEC); printf("%lld", sum); #ifdef MJ_ALLOCATOR free_mj_allocator(); #endif return 0; }
[코드 2]
class test { final static int ARRAY_LEN = 50000; static int[][] array = new int[ARRAY_LEN][]; static long sum = 0; static int confuse_the_compiler(int n) { return n % 10 > 5 ? -n / 2 : n * 2; } static void start() { for (int i = 1; i <= ARRAY_LEN; ++i) { for (int j = 0; j < i; ++j) { array[j] = new int[1]; array[j][0] = j + 1; } for (int j = i - 1; j >= 0; --j) { sum -= j; int n = confuse_the_compiler(array[j][0]); if (n != 0) { sum += n; // free(array[j]); } } } } public static void main(String[] args) { long start_time = System.currentTimeMillis(); start(); System.gc(); long end_time = System.currentTimeMillis(); System.out.printf("\nelapsed time: %.3f\n", (end_time - start_time) / 1000.0); System.out.println(sum); } }
[코드 3]
첨부파일 :)
첨부 | 파일 크기 |
---|---|
mj_allocator.zip | 1.17 KB |
region-based memory allocator
https://github.com/mycoboco/ocelot/blob/master/doc/cbl/arena.md
제가 필요한 기능과는 다른 코드인것 같습니다
해당 documentation을 읽어보았을땐 arena_t 오브젝트에 메모리 주소를 모아두었다 한번에 해제하는 방식인것 같은데요, 지금같은 경우는 프로그램 중간중간에 메모리를 일부분씩 free해주는 기능이 꼭 필요합니다.
메모리 할당자를 직접 짜시는건 그닥 좋은 방법이
메모리 할당자를 직접 짜시는건 그닥 좋은 방법이 아닌것 같습니다. 잘 짜기도 어려울 뿐더러, 기존의 구현체에 비해 좋은 성능을 내기 쉽지 않습니다.
tcmalloc등의 구현체를 사용하시면 코드를 바꾸지 않고도 적용이 가능합니다.
올려주신 코드의 성능 차이는 아마도 malloc/free와 java 메모리할당자의 차이로 인한 것으로 생각합니다.
http://programmers.stackexchange.com/questions/208656/java-heap-allocation-faster-than-c 의 답변을 참고하시면 될 것 같습니다.
..
이런것도 있었군요......역시...구글..........입니다...
---------------------------------------------------------------
Opensource에 기여하는 것이 꿈입니다.
내가 만든 코드를 모두가 사용할 때 까지~
음 ..
직접 프로파일링 해볼 수 있는 머신이 없는 상태라.. 그냥 떠오르는 생각 몇 개 적어 봅니다.
어차피 필요한 만큼의 메모리는 반드시 확보되어야 한다는 전제는 변하지 않으니..;;
alloc/free 가 빈번하다면.. alloc 비용은 어쩔 수 없다 쳐도.. free 는 굳이 할 필요가..;;
free list 를 하나 만들어서 list 의 head 가 null 일 때에만 alloc 하고.. free 할 때에는 head 에 insert ..
같은 함수 내에서 alloc/free 를 반복하는 경우라면..
static 으로 잡아서 free 하지 말고 필요한 경우에만 realloc .. (capacity 는 x2 로 증가)
1k 대신 kib 단위로(2^n 사용).. x1024 면 <<10 으로 알아서 최적화 될겁니다..
어차피 최대 갯수가 정해져 있다면, 그거 한꺼번에 통으로 alloc "한 번" 해서.. array 나 list 로 쪼개서 쓰기..
alloc/free list 만 관리하면 굳이 free 호출할 필요 없죠.
최대 갯수가 가변이거나.. 한번 alloc 할 때 fail 떨어질 가능성이 있을 정도로 사이즈가 크면..
일정 갯수 만큼 한 번에 alloc 해서 available list 에 추가해도 되겠죠.
끝으로.. list alloc 비용은? 할 수 있는데..
애초부터 list emelment 단위로 alloc 하고 그 안에 list ptr 와 실제 buffer 를 둡니다..
되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』
흠..
버디 알고리즘 이라는 녀석을 한번 참고해보시죠?
아마도 도움이 되실 듯 합니다.
흔히 운영체제 책에서 말하는 요청한 사이즈에 적당한 공간을 찾기위한 서칭에서 많은 시간이 소모 됩니다.
또 이것이 내/외부 단편화라는 것과도 영향이 있지요.....
기본 malloc은 성능이 매우 좋지 못한것으로 알고있습니다.( 제 기억에는 말이죠.. )
---------------------------------------------------------------
Opensource에 기여하는 것이 꿈입니다.
내가 만든 코드를 모두가 사용할 때 까지~
기본 malloc의 성능이 구리다는 편견이 있긴 하나
기본 malloc의 성능이 구리다는 편견이 있긴 하나 실제로는 굉장히 좋은 편입니다. 다만 일반적인 상황에서의 사용을 고려해야하기 때문에 특정 환경에서는 다소 성능이 떨어질 수 있습니다.
일정 이상의 퍼포먼스가 필요하면 단편화, 탐색시간, 캐시 등등을 고려하여 사용할 코드의 특성에 가장 알맞는 구현체를 찾아 사용하는것이 바람직합니다.
꼭 C로 짤 필요없으면, Java로 하면
꼭 C로 짤 필요없으면, Java로 하면 안되나요?
저도 C와 C++가 모국어지만, 생산성 측면에서 봐야지 언어를 고집해야할 이유가 없을 것 같은데요?
저렇게 작은 사이즈로 빈번하게 할당하고 해제하는
저렇게 작은 사이즈로 빈번하게 할당하고 해제하는 경우는 java 가 빠를 수 밖에 없습니다.
C 에서는 해제 호출시 실제 해제 코드를 타고 해제가 이루어집니다.
java는 더 이상 참조가 이루어지지 않으면 가비지 콜렉터가 실행되겠죠.
할당도 위한 같은 상황이 발생할 수 있는게 C는 작은 사이즈를 할당을
호출할때마다 할당을 합니다. java 는 jvm 내에서 이를 최적화할 수 있죠.
예를 들어 크게 한번 할당하고 그 공간을 나중에 쪼개서 할당할 수 있겠죠.
C 로도 이런 비슷한 전략을 사용할 수 있습니다. ymir 님이 설명한게
그런 형태입니다.
일종의 메모리 풀 전략으로 가면 됩니다. 해제시는 실제 해제를 하는게 아니라
freed_list 목록에 넣어 두는거죠. 할당때는 저기서 빈것을 가져와서
할당합니다.
---------
간디가 말한 우리를 파괴시키는 7가지 요소
첫째, 노동 없는 부(富)/둘째, 양심 없는 쾌락
셋째, 인격 없는 지! 식/넷째, 윤리 없는 비지니스
이익추구를 위해서라면..
다섯째, 인성(人性)없는 과학
여섯째, 희생 없는 종교/일곱째, 신념 없는 정치
댓글 달기