C언어

large file or large memory

고요한하늘... 2014. 11. 24. 23:57

64bit에서 2기가 이상 메모리 할당이 안돼서 아래와 같이 여러가지를 찾아보고 했었는데

다 부질없는 짓이었다.



char *p;

size_t size = 1024*1024*1024*10;

p = malloc(sizeof(char)*size );


위와 같이 코딩을 하면 2G이상 할당이 안되는데

size_t size = 1024*1024*1024*10; 에서 우변이 int형 즉 4byte로 인식돼 발생한 문제였다.


size_t size = 1024*1024*1024;

size *= 10;

또는

#deinfe MAX_FILE_SIZE 10737418240L  //10G  , 마지막에 "L"을 붙여 LONG INT임을 표시

이렇게 위에서 생긴 문제를 간단히 피해갈 수 있다.

---------------------------------



데이터의 크기가 2G를 넘어가게 되어

메모리에 올리지 못하는 경우


version1. file offset을 가지고 binary search를 시도했다.( 데이터는 keyword [tab] count 형태이고 keyword순으로 정렬돼 있다 )

version2. binary search라는건 결국은 2진 트리로 돼 있는 것과 마찬가지고 마지막 leaf 노드만 disk에서 찾도록 하고 tree의 header를 memory에 올려 놓고 찾을수 있도록 수정


version3. mmap을 사용하여 전체 데이터를 memory에 올린후 검색할수 있도록 수정


속도 : version1 < version2 << version3


디스크를 한번만 접근한다고 하더라도 디스크가 메모리보다 1000배 이상 느리기 때문에 종국에 QPS도 1000배 이상 차이가 난다.




2G 이상의 파일을 다루기 위해서는 아래가 정의해야 한다.

_LARGEFILE64_SOURCE & _FILE_OFFSET_BITS 64

file offset은 int로 돼 있기 때문에 2G가 넘어가면 컨트롤이 돼지 않는다.

위 내용을 정의하면 file에 대한 변수형이 int에서 long으로 바뀐다.

-----------------------------------------------------------------------------

#if defined _FILE_OFFSET_BITS && _FILE_OFFSET_BITS == 64 # define __USE_FILE_OFFSET64 1 #endif __USE_LARGEFILE64 # define __off64_t_defined #ifdef __USE_LARGEFILE64 extern __off64_t lseek64 (int __fd, __off64_t __offset, int __whence) __THROW; #endif # ifdef __USE_LARGEFILE64 /* Read NBYTES into BUF from FD at the given position OFFSET without changing the file pointer. Return the number read, -1 for errors or 0 for EOF. */ extern ssize_t pread64 (int __fd, void *__buf, size_t __nbytes, __off64_t __offset) __wur; /* Write N bytes of BUF to FD at the given position OFFSET without changing the file pointer. Return the number written, or -1. */ extern ssize_t pwrite64 (int __fd, __const void *__buf, size_t __n, __off64_t __offset) __wur; # endif # ifdef __USE_LARGEFILE64 extern int truncate64 (__const char *__file, __off64_t __length) __THROW __nonnull ((1)) __wur; # endif # ifdef __USE_LARGEFILE64 extern int ftruncate64 (int __fd, __off64_t __length) __THROW __wur; # endif # ifdef __USE_LARGEFILE64 extern int lockf64 (int __fd, int __cmd, __off64_t __len) __wur; # endif #endif /* Use misc and F_LOCK not already defined. */


-----------------------------------------------------------------------------



malloc으로 2G 이상을 할당하기 위해서는 아래 설정을 수정해야 한다.

vm.overcommit_memory = 0
vm.overcommit_ratio = 50

vm.overcommit_memory 은 0, 1, 2가 있고

vm.overcommit_ratio는 vm.overcommit_memory=2일때 동작한다.

0	-	Heuristic overcommit handling.
1	-	Always overcommit.
2	-	Don't overcommit.

https://www.kernel.org/doc/Documentation/vm/overcommit-accounting



----------------------------------------------------------------------------------

http://lxr.free-electrons.com/source/mm/mmap.c#L92

134 int __vm_enough_memory(struct mm_struct *mm, long pages, int cap_sys_admin)
135 {
136         unsigned long free, allowed, reserve;
137 
138         VM_WARN_ONCE(percpu_counter_read(&vm_committed_as) <
139                         -(s64)vm_committed_as_batch * num_online_cpus(),
140                         "memory commitment underflow");
141 
142         vm_acct_memory(pages);
143 
144         /*
145          * Sometimes we want to use more memory than we have
146          */
147         if (sysctl_overcommit_memory == OVERCOMMIT_ALWAYS)
148                 return 0;
149 
150         if (sysctl_overcommit_memory == OVERCOMMIT_GUESS) {
151                 free = global_page_state(NR_FREE_PAGES);
152                 free += global_page_state(NR_FILE_PAGES);
153 
154                 /*
155                  * shmem pages shouldn't be counted as free in this
156                  * case, they can't be purged, only swapped out, and
157                  * that won't affect the overall amount of available
158                  * memory in the system.
159                  */
160                 free -= global_page_state(NR_SHMEM);
161 
162                 free += get_nr_swap_pages();
163 
164                 /*
165                  * Any slabs which are created with the
166                  * SLAB_RECLAIM_ACCOUNT flag claim to have contents
167                  * which are reclaimable, under pressure.  The dentry
168                  * cache and most inode caches should fall into this
169                  */
170                 free += global_page_state(NR_SLAB_RECLAIMABLE);
171 
172                 /*
173                  * Leave reserved pages. The pages are not for anonymous pages.
174                  */
175                 if (free <= totalreserve_pages)
176                         goto error;
177                 else
178                         free -= totalreserve_pages;
179 
180                 /*
181                  * Reserve some for root
182                  */
183                 if (!cap_sys_admin)
184                         free -= sysctl_admin_reserve_kbytes >> (PAGE_SHIFT - 10);
185 
186                 if (free > pages)
187                         return 0;
188 
189                 goto error;
190         }
191 
192         allowed = vm_commit_limit();
193         /*
194          * Reserve some for root
195          */
196         if (!cap_sys_admin)
197                 allowed -= sysctl_admin_reserve_kbytes >> (PAGE_SHIFT - 10);
198 
199         /*
200          * Don't let a single process grow so big a user can't recover
201          */
202         if (mm) {
203                 reserve = sysctl_user_reserve_kbytes >> (PAGE_SHIFT - 10);
204                 allowed -= min(mm->total_vm / 32, reserve);
205         }
206 
207         if (percpu_counter_read_positive(&vm_committed_as) < allowed)
208                 return 0;
209 error:
210         vm_unacct_memory(pages);
211 
212         return -ENOMEM;
213 }
----------------------------------------------------------------------------------


커널에서 위와 같이 제약을 하는 것은 경우에 따라 memory leak이 전체 시스템에 영향을 줄수 있을 만큼 메모리를 할당 받을수 있기 때문이 아닐까 싶다.

 

2G가 이상의 malloc이나 mmap 모두 커널 파라미터를 변경해서 컨트롤 하지 말고

조금 번거롭더라도 2G 단위로 컨트롤 하는 것이 속도면에서 이득이다.

2G 이상을 컨트롤 하기위해서는 위에서 언급한대로 int형 변수를 모두 long형으로 바꿔야 하기 때문에

int만으로 구성된 파일의 경우 2배 크기가 된다.





---------------------------------------------------- 추가 ----------------------------------------------------

MAP_POPULATE옵션을 주었음에도 파일이 메모리에 한번에 로딩되지 않아 아래 URL에 있는 것처럼

http://stackoverflow.com/questions/752192/linux-mmap-internals

page 단위로 1byte씩 읽는 방법으로 모든 page를 로드한다.


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;
    }
}


'C언어' 카테고리의 다른 글

LCS(Longest common subsequence) example code   (0) 2014.12.02
mmap 주의점  (0) 2014.11.27
mmap 삽질  (0) 2014.11.14
sort backslash  (0) 2014.08.19
const 포인터( pointer )  (0) 2014.06.25