-
Notifications
You must be signed in to change notification settings - Fork 19
[python] LMDB
Myungchul Shin edited this page Jul 21, 2016
·
43 revisions
LevelDB에 이어서 LMDB 테스트
- site
-
source
- install
git clone https://github.com/LMDB/lmdb.git cd lmdb/libraries/liblmdb make; sudo make install
-
python binding
- install
pip install lmdb setup하면 lib 디렉토리 밑에 있는 default source가 binding된다. 만약, 다른 버전을 설치하려면 아래와 같이 처리 export LMDB_FORCE_SYSTEM=1 pip install lmdb
- python binding documentation
- append only btree
- 현재까지 테스트해본 Berkeley DB, LevelDB 등은 random search의 경우 disk i/o가 bottle neck
- random search 속도를 높이려면 데이터가 전부 메모리에 올라가는 것이 best
- 불가능하다면, memory map을 사용하는 라이브러리가 있는지 찾아보자.
- 이런 과정에서 발견한 것이 LMDB
- make db
- 입력파일의 사이즈가 15G이므로 map_size를 아주 크게 잡아야한다.
대략 (입력파일사이즈) * 1.5 = 22.5 -> 24G
- map_size = 24*(1024**3) = 24G
- 시간 : 3430s = 173084 line / sec
- db files
20G 2014-11-18 15:49 meta.db
- search db(random)
- 시간 : 154s = 649 line / sec
- 역시 Berkeley DB, LevelDB와 마찬가지로 cache를 지운 상태에서는 느리다.
- 하지만, LMDB는 mmap을 사용하므로 대용량 처리를 할때 매우 효과적인다. 물론, 메모리사이즈가 24G까지 올라갈수 있음. 이때 maximum performance
- multi processing/threading 환경에서 아주 효과적이다.
- Environment() 혹은 open_db() 타임에 전체 db를 memory에 적재하는 것은 구조상 불가능하다. 만약 이런 needs가 있다면 어떻게 할것인가?
- c에서
mdb.c 파일을 열어보면 아래와 같이 되어 있다. env->me_map = mmap(addr, env->me_mapsize, prot, MAP_SHARED, env->me_fd, 0); ... void *m = mmap(NULL, rsize, PROT_READ|PROT_WRITE, MAP_SHARED, env->me_lfd, 0); mmap은 'MAP_PRIVATE | MAP_POPULATE' 옵션이 아닌 이상 'read-ahead'를 지원하지 않는다. 그런데 소스가 저렇게 되어 있으므로, 소스를 수정하지 않는 이상 힘들다. 참고 : http://techoverflow.net/blog/2013/08/21/a-simple-mmap-readonly-example/ 따라서, mmap flags를 수정해서 한번에 메모리에 올리는 것도 가능하다. 또다른 방법으로는 아래 링크에 설명된 것 처럼, mmap 영역 전체를 page 단위로 점프하면서 첫번째 바이트만 읽는 방법도 있음 http://stackoverflow.com/a/752269 void load_mmap(char *mmapPtr) { // We'll load 10MB of data from mmap int offset = 0; for(int offset = 0; offset < 10 * 1024 * 1024; offset += 4 * 1024) { char *p = mmapPtr + offset; // deref pointer to force mmap load char c = *p; } }
- 수정없이
- 입력파일은 bigram 데이터이고 빈도수를 가지고 있다. 이 빈도수가 클수록 앞으로 들어올 확률이 높다는 의미. 따라서, 프로세스를 띄운 상태에서 적당한 빈도수 이상의 bigram을 query로 날려서, 강제로 memory에 적재시키는 방법도 유용하다.
- bigram 파일이 수정되었을때, 프로세스를 재시작해야하는 경우, 프로세스 재시작 이후에 이런 종류의 처리를 해준다는 의미
- make db
- 시간 : 648s = 916170 line / sec
- db files
21G 2014-11-18 23:08 meta_c.db
- search db(random)
- os cache 삭제
sudo sh -c "sync; echo 3 > /proc/sys/vm/drop_caches"
- 시간 : 174s = 574 line / sec
- 위에서 언급한것처럼 'mdb.c'의 'MAP_SHARED' 플래그를 수정해서 컴파일한 상태에서 테스트
- make db : 차이없음
- search db(random)
- 시간 : 1s = 100000 line / sec
- 로딩 시간 : 10m
- 메모리 사용량
21 giga
- 시스템 memory가 충분하다면, 'mdb.c'를 수정해서 위와 같은 방법으로 사용하는 것도 좋을 것 같다. 로딩시간이 10분 정도라서 느리긴 하지만, 일단 한번 메모리에 로딩되면 LM을 사용하는 모듈 performance도 크게 향상될수 있기 때문이다. 자주 업데이트되는 자원도 아니고~
- multi-processing
- 별 문제없이 shared 영역이 공유되는 것을 확인
24.0g 6.8g 6.8g D 7.9 14.3 0:04.34 search_lmdb 24.0g 6.8g 6.8g D 7.6 14.3 0:04.30 search_lmdb ...
- 기타 주의 사항
-
make_lmdb.py
등으로 db를 생성할 때, 기존 파일이 존재한다면 거기에 추가하게된다. 따라서, 미리 삭제를 해야함 -
map_size
를 크게 잡는 경우 virtual memory 사이즈도 그만큼 잡힌다. 32bit machine에서는 2G를 넘지 않게 해야한다.- 하지만, 64bit에는 문제없음. VIRT가 크게 잡혀도 performance에 문제는 없음.
-