Remove CVS $Id$ strings.
[ocaml-ancient.git] / README.txt
1 'Ancient' module for OCaml
2 ----------------------------------------------------------------------
3
4 What does this module do?
5 ----------------------------------------------------------------------
6
7 This module allows you to use in-memory data structures which are
8 larger than available memory and so are kept in swap.  If you try this
9 in normal OCaml code, you'll find that the machine quickly descends
10 into thrashing as the garbage collector repeatedly iterates over
11 swapped memory structures.  This module lets you break that
12 limitation.  Of course the module doesn't work by magic :-) If your
13 program tries to access these large structures, they still need to be
14 swapped back in, but it is suitable for large, sparsely accessed
15 structures.
16
17 Secondly, this module allows you to share those structures between
18 processes.  In this mode, the structures are backed by a disk file,
19 and any process that has read/write access that disk file can map that
20 file in and see the structures.
21
22 To understand what this module really does, you need to know a little
23 bit of background about the OCaml garbage collector (GC).  OCaml's GC
24 has two heaps, called the minor and major heaps.  The minor heap is
25 used for short-term storage of small objects which are usually created
26 and then quickly become unreachable.  Any objects which persist longer
27 (or objects which are very big to start with) get moved into the major
28 heap.  Objects in the major heap are assumed to be around for some
29 time, and the major heap is GC'd more slowly.
30
31 This module adds a third heap, called the "ancient heap", which is
32 never checked by the GC.  Objects must be moved into ancient manually,
33 using a process called "marking".  Once an object is in the ancient
34 heap, memory allocation is handled manually.  In particular objects in
35 the ancient heap may need to be manually deallocated.  The ancient
36 heap may either exist as ordinary memory, or may be backed by a file,
37 which is how shared structures are possible.
38
39 Structures which are moved into ancient must be treated as STRICTLY
40 NON-MUTABLE.  If an ancient structure is changed in any way then it
41 may cause a crash.
42
43 There are some limitations which apply to ancient data structures.
44 See the section "Shortcomings & bugs" below.
45
46 This module is most useful on 64 bit architectures where large address
47 spaces are the norm.  We have successfully used it with a 38 GB
48 address space backed by a file and shared between processes.
49
50 API
51 ----------------------------------------------------------------------
52
53 Please see file ancient.mli .
54
55 Compiling
56 ----------------------------------------------------------------------
57
58   cd mmalloc && ./configure
59   make
60
61 Make sure you run this command before running any program which
62 uses the Ancient module:
63
64   ulimit -s unlimited
65
66 Example
67 ----------------------------------------------------------------------
68
69 Run:
70
71   ulimit -s unlimited
72   wordsfile=/usr/share/dict/words
73   baseaddr=0x440000000000               # System specific - see below.
74   ./test_ancient_dict_write.opt $wordsfile dictionary.data $baseaddr
75   ./test_ancient_dict_verify.opt $wordsfile dictionary.data
76   ./test_ancient_dict_read.opt dictionary.data
77
78 (You can run several instances of test_ancient_dict_read.opt on the
79 same machine to demonstrate sharing).
80
81 Shortcomings & bugs
82 ----------------------------------------------------------------------
83
84 (0) Stack overflows are common when marking/sharing large structures
85 because we use a recursive algorithm to visit the structures.  If you
86 get random segfaults during marking/sharing, then try this before
87 running your program:
88
89   ulimit -s unlimited
90
91 (1) Ad-hoc polymorphic primitives (structural equality, marshalling
92 and hashing) do not work on ancient data structures, meaning that you
93 will need to provide your own comparison and hashing functions.  For
94 more details see Xavier Leroy's response here:
95
96 http://caml.inria.fr/pub/ml-archives/caml-list/2006/09/977818689f4ceb2178c592453df7a343.en.html
97
98 (2) Ancient.attach suggests setting a baseaddr parameter for newly
99 created files (it has no effect when attaching existing files).  We
100 strongly recommend this because in our tests we found that mmap would
101 locate the memory segment inappropriately -- the basic problem is that
102 because the file starts off with zero length, mmap thinks it can place
103 it anywhere in memory and often does not leave it room to grow upwards
104 without overwriting later memory mappings.  Unfortunately this
105 introduces an unwanted architecture dependency in all programs which
106 use the Ancient module with shared files, and it also requires
107 programmers to guess at a good base address which will be valid in the
108 future.  There are no other good solutions we have found --
109 preallocating the file is tricky with the current mmalloc code.
110
111 (3) The current code requires you to first of all create the large
112 data structures on the regular OCaml heap, then mark them as ancient,
113 effectively copying them out of the OCaml heap, then garbage collect
114 the (hopefully unreferenced) structures on the OCaml heap.  In other
115 words, you need to have all the memory available as physical memory.
116 The way to avoid this is to mark structures as ancient incrementally
117 as they are created, or in chunks, whatever works for you.
118
119 We typically use Ancient to deal with web server logfiles, and in this
120 case loading one file of data into memory and marking it as ancient
121 before moving on to the next file works for us.
122
123 (4) Why do ancient structures need to be read-only / not mutated?  The
124 reason is that you might create a new OCaml heap structure and point
125 the ancient structure at this heap structure.  The heap structure has
126 no apparent incoming pointers (the GC will not by its very nature
127 check the ancient structure for pointers), and so the heap structure
128 gets garbage collected.  At this point the ancient structure has a
129 dangling pointer, which will usually result in some form of crash.
130 Note that the restriction here is on creating pointers from ancient
131 data to OCaml heap data.  In theory it should be possible to modify
132 ancient data to point to other ancient data, but we have not tried
133 this.
134
135 (5) [Limit on number of keys -- issue fixed]
136
137 (6) [Advanced topic] The _mark function in ancient_c.c makes no
138 attempt to arrange the data structures in memory / on disk in a way
139 which optimises them for access.  The worst example is when you have
140 an array of large structures, where only a few fields in the structure
141 will be accessed.  Typically these will end up on disk as:
142
143   array of N pointers
144   structure 1
145   field A
146   field B
147     ...
148   field Z
149   structure 2
150   field A
151   field B
152     ...
153   field Z
154   structure 3
155   field A
156   field B
157     ...
158   field Z
159    ...
160    ...
161    ...
162   structure N
163   field A
164   field B
165     ...
166   field Z
167
168 If you then iterate accessing only fields A, you end up swapping the
169 whole lot back into memory.  A better arrangement would have been:
170
171   array of N pointers
172   structure 1
173   structure 2
174   structure 3
175     ...
176   structure N
177   field A from structure 1
178   field A from structure 2
179   field A from structure 3
180     ...
181   field A from structure N
182   field B from structure 1
183   field B from structure 2
184     etc.
185
186 which avoids loading unused fields at all.  In some circumstances we
187 have shown that this could make a huge difference to performance, but
188 we are not sure how to implement this cleanly in the current library.
189
190 [Update: I have fixed issue 6 manually for my Weblogs example and
191 confirmed that it does make a huge difference to performance, although
192 at considerable extra code complexity.  Interested people can see the
193 weblogs library, file import_weblogs_ancient.ml.in].
194
195 (7) [Advanced topic] Certain techniques such as Address Space
196 Randomisation (http://lwn.net/Articles/121845/) are probably not
197 compatible with the Ancient module and shared files.  Because the
198 ancient data structures contain real pointers, these pointers would be
199 invalidated if the shared file was not mapped in at precisely the same
200 base address in all processes which are sharing the file.
201
202 One solution might be to use private mappings and a list of fixups.
203 In fact, the code actually builds a list of fixups currently while
204 marking, because it needs to deal with precisely this issue (during
205 marking, memory is allocated with realloc which might move the memory
206 segment, thus real pointers cannot be stored while marking, but need
207 to be fixed up afterwards).  The list of fixups would need to be
208 stored alongside the memory segment (currently it is discarded after
209 marking), and the file would need to be mapped in using MAP_PRIVATE
210 (see below).
211
212 A possible problem with this is that because OCaml objects tend to be
213 small and contain a lot of pointers, it is likely that fixing up the
214 pointers would result in every page in the memory segment becoming
215 dirty, which would basically cancel out any benefit of using shared
216 mappings in the first place.  However it is likely that some users of
217 this module have large amounts of opaque data and few pointers, and
218 for them this would be worthwhile.
219
220 (8) Currently mmalloc is implemented so that the file is mapped in
221 PROT_READ|PROT_WRITE and MAP_SHARED.  Ancient data structures are
222 supposed to be immutable so strictly speaking write access shouldn't
223 be required.  It may be worthwhile modifying mmalloc to allow
224 read-only mappings, and private mappings.
225
226 (9) The library assumes that every OCaml object is at least one word
227 long.  This seemed like a good assumption up until I found that
228 zero-length arrays are valid zero word objects.  At the moment you
229 cannot mark structures which contain zero-length arrays -- you will
230 get an assert-failure in the _mark function.
231
232 Possibly there are other types of OCaml structure which are zero word
233 objects and also cannot be marked.  I'm not sure what these will be:
234 for example empty strings are stored as one word OCaml objects, so
235 they are OK.
236
237 The solution to this bug is non-trivial.
238
239 Authors
240 ----------------------------------------------------------------------
241
242 Primary code was written by Richard W.M. Jones <rich at annexia.org>
243 with help from Markus Mottl, Martin Jambon, and invaluable advice from
244 Xavier Leroy and Damien Doligez.
245
246 mmalloc was written by Mike Haertel and Fred Fish.
247
248 License
249 ----------------------------------------------------------------------
250
251 The module is licensed under the LGPL + OCaml linking exception.  This
252 module includes mmalloc which was originally distributed with gdb
253 (although it has since been removed), and that code was distributed
254 under the plain LGPL.
255
256 Latest version
257 ----------------------------------------------------------------------
258
259 The latest version can be found on the website:
260 http://merjis.com/developers/ancient