memory reduction

jw schultz jw at pegasys.ws
Fri Feb 6 10:09:07 GMT 2004


On Thu, Feb 05, 2004 at 09:27:51PM -0800, jw schultz wrote:
> As those of you who watch CVS will be aware Wayne has been
> making progress in reducing memory requirements of rsync.
> Much of what he has done has been the product of discussions
> between he and myself that started a month ago with John Van
> Essen.
> 
> Most recently Wayne has changed how the file_struct and its
> associated data are allocated, eliminating the string areas.
> Most of these changes have been small and relatively low
> impact although combining the allocation of the file_struct
> with the strings does impact the memory management of
> file_struct.
> 
> Attached is a patch that implements the next step.  It
> alters flist memory management and introduces a MM pool
> layer that reduces malloc overhead and allows destructors to
> actually release memory to the OS.
> 
> The patch adds a couple of new files so use patch -p1
> and rerun ./configure after patching.

Here is an update of the patch to cope with patch conflicts
re CVS HEAD.

diff -rupNP --exclude-from cvs/.ignore cvs/Makefile.in pool2/Makefile.in
--- cvs/Makefile.in	Tue Feb  3 16:34:24 2004
+++ pool2/Makefile.in	Tue Feb  3 16:33:00 2004
@@ -27,7 +27,7 @@ VERSION=@VERSION@
 
 HEADERS=byteorder.h config.h errcode.h proto.h rsync.h
 LIBOBJ=lib/wildmatch.o lib/compat.o lib/snprintf.o lib/mdfour.o \
-	lib/permstring.o @LIBOBJS@
+	lib/permstring.o lib/pool_alloc.o @LIBOBJS@
 ZLIBOBJ=zlib/deflate.o zlib/infblock.o zlib/infcodes.o zlib/inffast.o \
 	zlib/inflate.o zlib/inftrees.o zlib/infutil.o zlib/trees.o \
 	zlib/zutil.o zlib/adler32.o
diff -rupNP --exclude-from cvs/.ignore cvs/backup.c pool2/backup.c
--- cvs/backup.c	Wed Feb  4 03:49:36 2004
+++ pool2/backup.c	Wed Feb  4 22:11:26 2004
@@ -189,6 +189,7 @@ static int keep_backup(char *fname)
 			backup_dir[--backup_dir_len] = '\0';
 		if (verbose > 0)
 			rprintf(FINFO, "backup_dir is %s\n", backup_dir);
+
 		initialised = 1;
 	}
 
@@ -199,7 +200,7 @@ static int keep_backup(char *fname)
 	if (do_stat(fname, &st)) return 1;
 #endif
 
-	file = make_file(fname, NO_EXCLUDES);
+	file = make_file(fname, NULL, NO_EXCLUDES);
 
 	/* the file could have disappeared */
 	if (!file) return 1;
@@ -282,7 +283,7 @@ static int keep_backup(char *fname)
 		}
 	}
 	set_perms(keep_name, file, NULL, 0);
-	free_file(file, FREE_STRUCT);
+	free(file);
 
 	if (verbose > 1)
 		rprintf(FINFO, "keep_backup %s -> %s\n", fname, keep_name);
diff -rupNP --exclude-from cvs/.ignore cvs/batch.c pool2/batch.c
--- cvs/batch.c	Fri Feb  6 01:54:56 2004
+++ pool2/batch.c	Fri Feb  6 02:00:58 2004
@@ -136,9 +136,7 @@ struct file_list *create_flist_from_batc
 		exit_cleanup(1);
 	}
 
-	batch_flist = new(struct file_list);
-	if (!batch_flist)
-		out_of_memory("create_flist_from_batch");
+	batch_flist = flist_new(WITH_HLINK, "create_flist_from_batch");
 
 	save_read = stats.total_read;
 	save_pv = protocol_version;
@@ -150,9 +148,9 @@ struct file_list *create_flist_from_batc
 	for (i = 0; (flags = read_byte(f)) != 0; i++) {
 		if (protocol_version >= 28 && (flags & XMIT_EXTENDED_FLAGS))
 			flags |= read_byte(f) << 8;
-		receive_file_entry(&batch_flist->files[i], flags, f);
+		receive_file_entry(&batch_flist->files[i], flags, batch_flist, f);
 	}
-	receive_file_entry(NULL, 0, 0); /* Signal that we're done. */
+	receive_file_entry(NULL, 0, NULL, 0); /* Signal that we're done. */
 
 	protocol_version = save_pv;
 	stats.total_read = save_read;
diff -rupNP --exclude-from cvs/.ignore cvs/flist.c pool2/flist.c
--- cvs/flist.c	Fri Feb  6 01:55:41 2004
+++ pool2/flist.c	Fri Feb  6 02:00:59 2004
@@ -76,7 +76,6 @@ static unsigned int min_file_struct_len;
 static void clean_flist(struct file_list *flist, int strip_root, int no_dups);
 static void output_flist(struct file_list *flist);
 
-
 void init_flist(void)
 {
 	struct file_struct f;
@@ -507,7 +506,8 @@ void send_file_entry(struct file_struct 
 
 
 
-void receive_file_entry(struct file_struct **fptr, unsigned short flags, int f)
+void receive_file_entry(struct file_struct **fptr, unsigned short flags,
+    struct file_list *flist, int f)
 {
 	static time_t modtime;
 	static mode_t mode;
@@ -624,12 +624,12 @@ void receive_file_entry(struct file_stru
 		idev_len = 0;
 
 	sum_len = always_checksum && S_ISREG(mode) ? MD4_SUM_LENGTH : 0;
-	file_struct_len = idev_len? sizeof file[0] : min_file_struct_len;
+	file_struct_len = min_file_struct_len;
 
 	alloc_len = file_struct_len + dirname_len + basename_len
-		  + linkname_len + sum_len + idev_len;
-	if (!(bp = new_array(char, alloc_len)))
-		out_of_memory("receive_file_entry");
+		  + linkname_len + sum_len;
+	bp = pool_alloc(flist->file_pool, alloc_len, "receive_file_entry");
+
 	file = *fptr = (struct file_struct *)bp;
 	memset(bp, 0, min_file_struct_len);
 	bp += file_struct_len;
@@ -642,9 +642,9 @@ void receive_file_entry(struct file_stru
 	file->gid = gid;
 
 #if SUPPORT_HARD_LINKS
-	if (idev_len) {
-		file->link_u.idev = (struct idev *)bp;
-		bp += idev_len;
+	if (idev_len && flist->hlink_pool) {
+		file->link_u.idev = pool_talloc(flist->hlink_pool,
+		    struct idev, 1, "inode_table");
 	}
 #endif
 
@@ -676,15 +676,19 @@ void receive_file_entry(struct file_stru
 
 #if SUPPORT_HARD_LINKS
 	if (idev_len) {
+		INO64_T inode;
 		if (protocol_version < 26) {
 			dev = read_int(f);
-			file->F_INODE = read_int(f);
+			inode = read_int(f);
 		} else {
 			if (!(flags & XMIT_SAME_DEV))
 				dev = read_longint(f);
-			file->F_INODE = read_longint(f);
+			inode = read_longint(f);
+		}
+		if (flist->hlink_pool) {
+			file->F_INODE = inode;
+			file->F_DEV = dev;
 		}
-		file->F_DEV = dev;
 	}
 #endif
 
@@ -728,7 +732,8 @@ void receive_file_entry(struct file_stru
  * statting directories if we're not recursing, but this is not a very
  * important case.  Some systems may not have d_type.
  **/
-struct file_struct *make_file(char *fname, int exclude_level)
+struct file_struct *make_file(char *fname,
+    struct file_list *flist, int exclude_level)
 {
 	static char *lastdir;
 	static int lastdir_len = -1;
@@ -742,6 +747,7 @@ struct file_struct *make_file(char *fnam
 	char *basename, *dirname, *bp;
 	unsigned short flags = 0;
 
+
 	if (strlcpy(thisname, fname, sizeof thisname)
 	    >= sizeof thisname - flist_dir_len) {
 		rprintf(FINFO, "skipping overly long name: %s\n", fname);
@@ -838,12 +844,18 @@ struct file_struct *make_file(char *fnam
 		idev_len = 0;
 
 	sum_len = always_checksum && S_ISREG(st.st_mode) ? MD4_SUM_LENGTH : 0;
-	file_struct_len = idev_len? sizeof file[0] : min_file_struct_len;
+	file_struct_len = min_file_struct_len;
 
 	alloc_len = file_struct_len + dirname_len + basename_len
-		  + linkname_len + sum_len + idev_len;
-	if (!(bp = new_array(char, alloc_len)))
-		out_of_memory("receive_file_entry");
+	    + linkname_len + sum_len;
+	if (flist) {
+		bp = pool_alloc(flist->file_pool, alloc_len,
+		    "receive_file_entry");
+	} else {
+		if (!(bp = new_array(char, alloc_len)))
+			out_of_memory("receive_file_entry");
+	}
+
 	file = (struct file_struct *)bp;
 	memset(bp, 0, min_file_struct_len);
 	bp += file_struct_len;
@@ -856,9 +868,9 @@ struct file_struct *make_file(char *fnam
 	file->gid = st.st_gid;
 
 #if SUPPORT_HARD_LINKS
-	if (idev_len) {
-		file->link_u.idev = (struct idev *)bp;
-		bp += idev_len;
+	if (idev_len && flist && flist->hlink_pool) {
+		file->link_u.idev = pool_talloc(flist->hlink_pool,
+		    struct idev, 1, "inode_table");
 		file->F_DEV = st.st_dev;
 		file->F_INODE = st.st_ino;
 	}
@@ -913,9 +925,8 @@ void send_file_name(int f, struct file_l
 	extern int delete_excluded;
 
 	/* f is set to -1 when calculating deletion file list */
-	file = make_file(fname,
-			 f == -1 && delete_excluded? SERVER_EXCLUDES
-						   : ALL_EXCLUDES);
+	file = make_file(fname, flist,
+	    f == -1 && delete_excluded? SERVER_EXCLUDES : ALL_EXCLUDES);
 
 	if (!file)
 		return;
@@ -1034,9 +1045,12 @@ struct file_list *send_file_list(int f, 
 
 	start_write = stats.total_written;
 
-	flist = flist_new();
+	flist = flist_new(f == -1 ? WITHOUT_HLINK : WITH_HLINK,
+	    "send_file_list");
 
 	if (f != -1) {
+		flist->hlink_pool = pool_create(128 * 1024,
+		    sizeof (struct idev), out_of_memory, POOL_INTERN);
 		io_start_buffering_out(f);
 		if (filesfrom_fd >= 0) {
 			if (argv[0] && !push_dir(argv[0])) {
@@ -1185,6 +1199,12 @@ struct file_list *send_file_list(int f, 
 			finish_filelist_progress(flist);
 	}
 
+	if (flist->hlink_pool)
+	{
+		pool_destroy(flist->hlink_pool);
+		flist->hlink_pool = NULL;
+	}
+
 	clean_flist(flist, 0, 0);
 
 	if (f != -1) {
@@ -1224,10 +1244,10 @@ struct file_list *recv_file_list(int f)
 
 	start_read = stats.total_read;
 
-	flist = new(struct file_list);
-	if (!flist)
-		goto oom;
+	flist = flist_new(WITH_HLINK, "recv_file_list");
 
+	flist->hlink_pool = pool_create(128 * 1024, sizeof (struct idev),
+	    out_of_memory, POOL_INTERN);
 	flist->count = 0;
 	flist->malloced = 1000;
 	flist->files = new_array(struct file_struct *, flist->malloced);
@@ -1242,7 +1262,7 @@ struct file_list *recv_file_list(int f)
 
 		if (protocol_version >= 28 && (flags & XMIT_EXTENDED_FLAGS))
 			flags |= read_byte(f) << 8;
-		receive_file_entry(&flist->files[i], flags, f);
+		receive_file_entry(&flist->files[i], flags, flist, f);
 
 		if (S_ISREG(flist->files[i]->mode))
 			stats.total_size += flist->files[i]->length;
@@ -1256,7 +1276,7 @@ struct file_list *recv_file_list(int f)
 				f_name(flist->files[i]));
 		}
 	}
-	receive_file_entry(NULL, 0, 0); /* Signal that we're done. */
+	receive_file_entry(NULL, 0, NULL, 0); /* Signal that we're done. */
 
 	if (verbose > 2)
 		rprintf(FINFO, "received %d names\n", flist->count);
@@ -1345,34 +1365,42 @@ int flist_find(struct file_list *flist, 
 	return -1;
 }
 
-
 /*
- * Free up any resources a file_struct has allocated, and optionally free
- * it up as well.
+ * Free up any resources a file_struct has allocated
+ * and clear the file.
  */
-void free_file(struct file_struct *file, int free_the_struct)
+void clear_file(int i, struct file_list *flist)
 {
-	if (free_the_struct)
-		free(file);
-	else
-		memset(file, 0, min_file_struct_len);
+	if (flist->hlink_pool && flist->files[i]->link_u.idev)
+		pool_free(flist->hlink_pool, 0, flist->files[i]->link_u.idev);
+	memset(flist->files[i], 0, min_file_struct_len);
 }
 
 
 /*
  * allocate a new file list
  */
-struct file_list *flist_new(void)
+struct file_list *flist_new(int with_hlink, char *msg)
 {
 	struct file_list *flist;
 
 	flist = new(struct file_list);
 	if (!flist)
-		out_of_memory("send_file_list");
+		out_of_memory(msg);
 
-	flist->count = 0;
-	flist->malloced = 0;
-	flist->files = NULL;
+	memset(flist, 0, sizeof (struct file_list));
+
+	if (!(flist->file_pool = pool_create(FILE_EXTENT, 0,
+	    out_of_memory, POOL_INTERN)))
+		out_of_memory(msg);
+
+#if SUPPORT_HARD_LINKS
+	if (with_hlink && preserve_hard_links) {
+		if (!(flist->hlink_pool = pool_create(HLINK_EXTENT, 0,
+		    out_of_memory, POOL_INTERN)))
+			out_of_memory(msg);
+	}
+#endif
 
 	return flist;
 }
@@ -1382,9 +1410,8 @@ struct file_list *flist_new(void)
  */
 void flist_free(struct file_list *flist)
 {
-	int i;
-	for (i = 1; i < flist->count; i++)
-		free_file(flist->files[i], FREE_STRUCT);
+	pool_destroy(flist->file_pool);
+	pool_destroy(flist->hlink_pool);
 	free(flist->files);
 	free(flist);
 }
@@ -1424,7 +1451,8 @@ static void clean_flist(struct file_list
 			 * else deletions will mysteriously fail with -R). */
 			if (flist->files[i]->flags & FLAG_TOP_DIR)
 				flist->files[prev_i]->flags |= FLAG_TOP_DIR;
-			free_file(flist->files[i], CLEAR_STRUCT);
+
+			clear_file(i, flist);
 		} else
 			prev_i = i;
 	}
Binary files cvs/getgroups and pool2/getgroups differ
diff -rupNP --exclude-from cvs/.ignore cvs/hlink.c pool2/hlink.c
--- cvs/hlink.c	Mon Feb  2 22:21:19 2004
+++ pool2/hlink.c	Wed Feb  4 01:35:49 2004
@@ -46,26 +46,41 @@ int hlink_count;
 
 /* Analyze the data in the hlink_list[], remove items that aren't multiply
  * linked, and replace the dev+inode data with the hlindex+next linked list. */
-static void link_idev_data(void)
+static void link_idev_data(struct file_list *flist)
 {
 	struct file_struct *head;
 	int from, to, start;
 
+	alloc_pool_t hlink_pool;
+	alloc_pool_t idev_pool = flist->hlink_pool;
+
+	hlink_pool = pool_create(128 * 1024, sizeof (struct hlink),
+	    out_of_memory, POOL_INTERN);
+
 	for (from = to = 0; from < hlink_count; from++) {
 		start = from;
 		head = hlink_list[start];
 		while (from < hlink_count-1
 		    && LINKED(hlink_list[from], hlink_list[from+1])) {
+			pool_free(idev_pool, 0, hlink_list[from]->link_u.idev);
+			hlink_list[from]->link_u.links = pool_talloc(hlink_pool,
+			    struct hlink, 1, "hlink_list");
+
 			hlink_list[from]->F_HLINDEX = to;
 			hlink_list[from]->F_NEXT = hlink_list[from+1];
 			from++;
 		}
 		if (from > start) {
+			pool_free(idev_pool, 0, hlink_list[from]->link_u.idev);
+			hlink_list[from]->link_u.links = pool_talloc(hlink_pool,
+			    struct hlink, 1, "hlink_list");
+
 			hlink_list[from]->F_HLINDEX = to;
 			hlink_list[from]->F_NEXT = head;
 			hlink_list[from]->flags |= FLAG_HLINK_EOL;
 			hlink_list[to++] = head;
 		} else {
+			pool_free(idev_pool, 0, head->link_u.idev);
 			head->link_u.idev = NULL;
 		}
 	}
@@ -73,12 +88,16 @@ static void link_idev_data(void)
 	if (!to) {
 		free(hlink_list);
 		hlink_list = NULL;
+		pool_destroy(hlink_pool);
+		hlink_pool = NULL;
 	} else {
 		hlink_count = to;
 		if (!(hlink_list = realloc_array(hlink_list,
 		    struct file_struct *, hlink_count)))
 			out_of_memory("init_hard_links");
 	}
+	flist->hlink_pool = hlink_pool;
+	pool_destroy(idev_pool);
 }
 #endif
 
@@ -109,7 +128,7 @@ void init_hard_links(struct file_list *f
 		free(hlink_list);
 		hlink_list = NULL;
 	} else
-		link_idev_data();
+		link_idev_data(flist);
 #endif
 }
 
diff -rupNP --exclude-from cvs/.ignore cvs/lib/pool_alloc.3 pool2/lib/pool_alloc.3
--- cvs/lib/pool_alloc.3	Wed Dec 31 16:00:00 1969
+++ pool2/lib/pool_alloc.3	Tue Feb  3 16:30:24 2004
@@ -0,0 +1,199 @@
+.ds d \-\^\-
+.ds o \fR[\fP
+.ds c \fR]\fP
+.ds | \fR|\fP
+.de D
+\\.B \*d\\$1
+..
+.de DI
+\\.BI \*d\\$1 \\$2
+..
+.de DR
+\\.BR \*d\\$1 \\$2
+..
+.de Di
+\\.BI \*d\\$1 " \\$2"
+..
+.de Db
+\\.B \*d\\$1 " \\$2"
+..
+.de Df
+\\.B \*d\*ono\*c\\$1
+..
+.de See
+See \fB\\$1\fP for details.
+..
+.de SeeIn
+See \fB\\$1\fP in \fB\\$2\fP for details.
+..
+.TH POOL_ALLOC 3
+.SH NAME
+pool_alloc, pool_free, pool_talloc, pool_tfree, pool_create, pool_destroy
+\- Allocate and free memory in managed allocation pools.
+.SH SYNOPSIS
+.B #include "pool_alloc.h"
+
+\fBstruct alloc_pool *pool_create(size_t \fIsize\fB, size_t \fIquantum\fB, void (*\fIbomb\fB)(char *), int \fIflags\fB);
+
+\fBvoid pool_destroy(struct alloc_pool *\fIpool\fB);
+
+\fBvoid *pool_alloc(struct alloc_pool *\fIpool\fB, size_t \fIsize\fB, char *\fImsg\fB);
+
+\fBvoid pool_free(struct alloc_pool *\fIpool\fB, sise_t \fIsize\fB, void *\fIaddr\fB);
+
+\fBvoid *pool_talloc(struct alloc_pool *\fIpool\fB, \fItype\fB), int \fIcount\fB, char *\fImsg\fB);
+
+\fBvoid pool_tfree(struct alloc_pool *\fIpool\fB, \fItype\fB, int \fIcount\fB, void *\fIaddr\fB);
+.SH DESCRIPTION
+.P
+The pool allocation routines use
+.B malloc()
+for underlying memory management.
+What allocation pools do is cause
+memory within a given pool to be in large contigious blocks
+(called extents) that when freed will be reusable.  Unlike
+.B malloc()
+the allocations are not managed individually.
+Instead each extent tracks the total free memory within the
+extent.  Each extent can either be used to allocate memory
+or to manage the freeing of memory within that extent.
+When an extent has less free memory than a given
+allocation request or when the first request to free
+memory within that extent is received the extent ceases to
+be used for allocation.
+.P
+This form of memory management is suited to large numbers of small
+related allocations that are held for a while
+and then freed as a group.
+Because the
+underlying allocations are done in large contigious extents
+when an extent is freed it releases a large enough
+contigious block of memory to be useful to subsequent
+.B malloc()
+and
+.B pool_alloc()
+calls even if allocations from other pools or from
+.B malloc()
+are made between allocations from a given pool.
+.P
+.B pool_create()
+Creates an allocation pool for subsequent calls to the pool
+allocation functions.
+When an extent is created for allocations it will be
+.I size 
+bytes.
+Allocations from the pool have their sizes rounded up to a
+multiple of
+.I quantum
+bytes in length.
+Specifying
+.B 0
+for
+.I quantum
+Will produce a quantum that should meet maximal allignment
+on most platforms.
+If the
+.B POOL_QALIGN
+.I flag
+is set allocations will be aligned to addresses that are a
+multiple of
+.IR quantum .
+If the
+.B POOL_CLEAR
+.I flag
+is set all allocations from the pool will be zero filled.
+.P
+.B pool_destroy()
+destroys an allocation pool and frees all memory allocated
+in that pool.
+.P
+.B pool_alloc()
+allocates
+.I size
+bytes from the specified
+.IR pool .
+If
+.I size
+is
+.B 0
+.I quantum
+bytes will be freed.
+If the requested memory cannot be allocated
+.B pool_alloc()
+will call
+.I bomb()
+function, if defined, with
+.I msg
+as it's sole argument and
+.B NULL
+will be returned.
+.P
+.B pool_free()
+frees
+.I size
+bytes pointed to by
+.I addr
+previously allocated in the specified
+.IR pool .
+The memory freed within an extent will not be reusable until
+all of the memory in that extent has been freed but 
+depending on the order in which the
+allocations are freed some extents may be released for reuse
+while others are still in use.
+If
+.I size
+is
+.B 0
+.I quantum
+bytes will be freed.
+If
+.I addr
+is
+.B 0
+no memory will be freed but subsequent allocations will come
+from a new extent.
+.P
+.B pool_talloc()
+is a macro that take a
+.I type
+and
+.I count
+instead of
+.I size
+and will cast the return value to the correct type.
+.P
+.B pool_tfree
+is a macro to free memory previously allocated in the
+specified
+.IR pool .
+.SH RETURN VALUE
+.B pool_create()
+returns a pointer to
+.BR "struct alloc_pool" .
+.P
+.B pool_alloc()
+and
+.B pool_talloc()
+return pointers to the allocated memory,
+or NULL if the request fails.
+For each extent so long as no allocations are smaller than varaible
+allignment requirements this pointer will be suitably
+alligned for any kind of variable.
+The return type of
+.B pool_alloc()
+will normally require casting to the desired type but
+.B pool_talloc()
+will returns a pointer of the requested
+.IR type .
+.P
+.BR pool_free() ,
+.B pool_tfree()
+and
+.B pool_destroy()
+return no value.
+.SH SEE ALSO
+.nf
+malloc(3)
+.SH AUTHOR
+pool_alloc was created by J.W. Schultz of Pegasystems Technologies.
+.SH BUGS AND ISSUES
diff -rupNP --exclude-from cvs/.ignore cvs/lib/pool_alloc.c pool2/lib/pool_alloc.c
--- cvs/lib/pool_alloc.c	Wed Dec 31 16:00:00 1969
+++ pool2/lib/pool_alloc.c	Thu Feb  5 19:28:58 2004
@@ -0,0 +1,311 @@
+#include "rsync.h"
+
+#define POOL_DEF_EXTENT	(32 * 1024)
+
+struct alloc_pool
+{
+	size_t			size;		/* extent size		*/
+	size_t			quantum;	/* allocation quantum	*/
+	struct pool_extent	*live;		/* current extent for
+						 * allocations		*/
+	struct pool_extent	*free;		/* unfreed extent list	*/
+	void			(*bomb)();
+						/* function to call if
+						 * malloc fails		*/
+	int			flags;
+
+	/* statistical data */
+	unsigned long		e_created;	/* extents created	*/
+	unsigned long		e_freed;	/* extents detroyed	*/
+	uint64			n_allocated;	/* calls to alloc	*/
+	uint64			n_freed;	/* calls to free	*/
+	uint64			b_allocated;	/* cum. bytes allocated	*/
+	uint64			b_freed;	/* cum. bytes freed	*/
+};
+
+struct pool_extent
+{
+	void			*start;		/* starting address	*/
+	size_t			free;		/* free bytecount	*/
+	size_t			bound;		/* bytes bound by padding,
+						 * overhead and freed	*/
+	struct pool_extent	*next;
+};
+
+#define MINALIGN	(sizeof (void *))
+
+alloc_pool_t
+pool_create(size_t size, size_t quantum,
+    void (*bomb)(char *), int flags)
+{
+	struct alloc_pool	*pool;
+
+	if (!(pool = (struct alloc_pool*) malloc(sizeof (struct alloc_pool))))
+		return pool;
+	memset(pool, 0, sizeof (struct alloc_pool));
+
+	pool->size = size	/* round extent size to min alignment reqs */
+	    ? (size + MINALIGN - 1) & ~(MINALIGN - 1)
+	    : POOL_DEF_EXTENT;
+	pool->quantum = quantum ? quantum : MINALIGN;
+	pool->live = NULL;
+	pool->free = NULL;
+	pool->bomb = bomb;
+	pool->flags = flags;
+
+	return pool;
+}
+
+void
+pool_destroy(alloc_pool_t p)
+{
+	struct alloc_pool *pool = (struct alloc_pool *) p;
+	struct pool_extent	*cur, *next;
+
+	if (!pool)
+		return;
+
+	if(pool->live)
+	{
+		cur = pool->live;
+		free(cur->start);
+		if (!(pool->flags & (POOL_INTERN | POOL_APPEND)))
+			free(cur);
+	}
+	cur = pool->free;
+	while(cur)
+	{
+		next = cur->next;
+		free(cur->start);
+		if (!(pool->flags & (POOL_INTERN | POOL_APPEND)))
+			free(cur);
+		cur = next;
+	}
+	free(pool);
+}
+
+void *pool_alloc(alloc_pool_t p, size_t len, char *bomb)
+{
+	struct alloc_pool *pool = (struct alloc_pool *) p;
+	if (!pool)
+		return NULL;
+
+	if(!len)
+		len = pool->quantum;
+	else if (pool->quantum > 1 && len % pool->quantum)
+		len += pool->quantum - len % pool->quantum;
+
+	if (len > pool->size)
+		goto bomb;
+
+	if (!pool->live || len > pool->live->free)
+	{
+		void	*start;	
+		size_t	free;
+		size_t	bound;
+		size_t	sqew;
+		size_t	asize;
+
+		if (pool->live)
+		{
+			pool->live->next = pool->free;
+			pool->free = pool->live;
+		}
+
+		free = pool->size;
+		bound = 0;
+
+		asize = pool->size;
+		if (pool->flags & POOL_APPEND)
+			asize += sizeof (struct pool_extent);
+			
+		if(!(start = (void *) malloc(asize)))
+			goto bomb;
+
+		if (pool->flags & POOL_CLEAR)
+			memset(start, 0, asize);
+
+		if (pool->flags & POOL_INTERN)
+		{
+			bound = sizeof (struct pool_extent);
+			free -= sizeof (struct pool_extent);
+			pool->live = start + free;
+		} 
+		else if (pool->flags & POOL_APPEND)
+		{
+			pool->live = start + free;
+		}
+		else if(!(pool->live = (struct pool_extent *) malloc(sizeof (struct pool_extent))))
+		{
+			goto bomb;
+		}
+		if(pool->flags & POOL_QALIGN && pool->quantum > 1
+		    && (sqew = (size_t)(start + free) % pool->quantum))
+		{
+			bound  += sqew;
+			free -= sqew;
+		}
+		pool->live->start = start;
+		pool->live->free = free;
+		pool->live->bound = bound;
+		pool->live->next = NULL;
+
+		pool->e_created++;
+	}
+
+	pool->n_allocated++;
+	pool->b_allocated += len;
+
+	pool->live->free -= len;
+
+	return pool->live->start + pool->live->free;
+
+bomb:
+	if (pool->bomb)
+		(*pool->bomb)(bomb);
+	return NULL;
+}
+
+void
+pool_free(alloc_pool_t p, size_t len, void *addr)
+{
+	struct alloc_pool *pool = (struct alloc_pool *) p;
+	struct pool_extent	*cur, *next;
+	struct pool_extent	*prev = NULL;
+
+	if (!pool)
+		return;
+
+	if(!len)
+		len = pool->quantum;
+	else if (pool->quantum > 1 && len % pool->quantum)
+		len += pool->quantum - len % pool->quantum;
+
+	if (!addr && pool->live)
+	{
+		pool->live->next = pool->free;
+		pool->free = pool->live;
+		pool->live = NULL;
+		return;
+	}
+	pool->n_freed++;
+	pool->b_freed += len;
+
+	cur = pool->live;
+	if (cur
+	    && addr >= cur->start
+	    && addr < cur->start + pool->size)
+	{
+		if (addr == cur->start + cur->free)
+		{
+			if (pool->flags & POOL_CLEAR)
+				memset(addr, 0, len);
+			pool->b_freed += len;
+		} else {
+			cur->bound += len;
+		}
+		if (cur->free + cur->bound >= pool->size)
+		{
+			size_t sqew;
+
+			cur->free = pool->size;
+			cur->bound = 0;
+			if (pool->flags & POOL_INTERN)
+			{
+				cur->bound = sizeof (struct pool_extent);
+				cur->free -= sizeof (struct pool_extent);
+			}
+			if(pool->flags & POOL_QALIGN && pool->quantum > 1
+			    && (sqew = (size_t)(cur->start + cur->free) % pool->quantum))
+			{
+				cur->bound += sqew;
+				cur->free -= sqew;
+			}
+		}
+		return;
+	}
+	cur = pool->free;
+	while(cur)
+	{
+		next = cur->next;
+		if (addr >= cur->start
+		    && addr < cur->start + pool->size) 
+			break;
+		prev = cur;
+		cur = next;
+	}
+	if (!cur)
+		return;
+
+	if (cur != pool->free)
+	{
+		prev->next = cur->next;
+		cur->next = pool->free;
+		pool->free = cur;
+	}
+	cur->bound += len;
+
+	if (cur->free + cur->bound >= pool->size)
+	{
+		pool->free = cur->next;
+
+		free(cur->start);
+		if (!(pool->flags & (POOL_INTERN | POOL_APPEND)))
+			free(cur);
+		pool->e_freed++;
+	}
+	return;
+}
+
+#define FDPRINT(label, value) \
+	snprintf(buf, BUFSIZ, label, value); \
+	write(fd, buf, strlen(buf));
+
+#define FDEXTSTAT(ext) \
+	snprintf(buf, BUFSIZ, "  %12ld  %5ld\n", \
+		(long) ext->free, \
+		(long) ext->bound); \
+	write(fd, buf, strlen(buf));
+
+void
+pool_stats(alloc_pool_t p, int fd, int summarize)
+{
+	struct alloc_pool *pool = (struct alloc_pool *) p;
+	struct pool_extent	*cur;
+	char buf[BUFSIZ];
+
+	if (!pool)
+		return;
+
+	FDPRINT("  Extent size:       %12ld\n",	(long)	pool->size)
+	FDPRINT("  Alloc quantum:     %12ld\n",	(long)	pool->quantum)
+	FDPRINT("  Extents created:   %12ld\n",		pool->e_created)
+	FDPRINT("  Extents freed:     %12ld\n",		pool->e_freed)
+	FDPRINT("  Alloc count:       %12.0f\n", (double) pool->n_allocated)
+	FDPRINT("  Free Count:        %12.0f\n", (double) pool->n_freed)
+	FDPRINT("  Alloc bytes:       %12.0f\n", (double) pool->b_allocated)
+	FDPRINT("  Free bytes:        %12.0f\n", (double) pool->b_freed)
+
+	if (summarize)
+		return;
+
+	if (!pool->live && !pool->free)
+		return;
+
+	write(fd, "\n", 1);
+
+	if (pool->live)
+	{
+		FDEXTSTAT(pool->live) 
+	}
+	strcpy(buf, "   FREE    BOUND\n");
+	write(fd, buf, strlen(buf));
+
+	cur = pool->free;
+	while(cur)
+	{
+		FDEXTSTAT(cur) 
+		cur = cur->next;
+	}
+}
+
diff -rupNP --exclude-from cvs/.ignore cvs/lib/pool_alloc.h pool2/lib/pool_alloc.h
--- cvs/lib/pool_alloc.h	Wed Dec 31 16:00:00 1969
+++ pool2/lib/pool_alloc.h	Tue Feb  3 16:30:24 2004
@@ -0,0 +1,20 @@
+#include <stddef.h>
+
+#define POOL_CLEAR	(1<<0)		/* zero fill allocations	*/
+#define POOL_QALIGN	(1<<1)		/* align data to quanta		*/
+#define POOL_INTERN	(1<<2)		/* Allocate extent structures	*/
+#define POOL_APPEND	(1<<3)		/*   or appended to extent data	*/
+
+typedef void *alloc_pool_t;
+
+alloc_pool_t pool_create(size_t size, size_t quantum, void (*bomb)(char *), int flags);
+void pool_destroy(alloc_pool_t pool);
+void *pool_alloc(alloc_pool_t pool, size_t size, char *bomb);
+void pool_free(alloc_pool_t pool, size_t size, void *addr);
+
+#define pool_talloc(pool, type, count, bomb) \
+	((type *)pool_alloc(pool, sizeof(type) * count, bomb))
+
+#define pool_tfree(pool, type, count, addr) \
+	(pool_free(pool, sizeof(type) * count, addr))
+
diff -rupNP --exclude-from cvs/.ignore cvs/proto.h pool2/proto.h
--- cvs/proto.h	Fri Feb  6 01:55:48 2004
+++ pool2/proto.h	Fri Feb  6 02:01:00 2004
@@ -74,16 +74,18 @@ int readlink_stat(const char *path, STRU
 int link_stat(const char *path, STRUCT_STAT * buffer);
 void flist_expand(struct file_list *flist);
 void send_file_entry(struct file_struct *file, int f, unsigned short base_flags);
-void receive_file_entry(struct file_struct **fptr, unsigned short flags, int f);
-struct file_struct *make_file(char *fname, int exclude_level);
+void receive_file_entry(struct file_struct **fptr, unsigned short flags,
+    struct file_list *flist, int f);
+struct file_struct *make_file(char *fname,
+    struct file_list *flist, int exclude_level);
 void send_file_name(int f, struct file_list *flist, char *fname,
 		    int recursive, unsigned short base_flags);
 struct file_list *send_file_list(int f, int argc, char *argv[]);
 struct file_list *recv_file_list(int f);
 int file_compare(struct file_struct **file1, struct file_struct **file2);
 int flist_find(struct file_list *flist, struct file_struct *f);
-void free_file(struct file_struct *file, int free_the_struct);
-struct file_list *flist_new(void);
+void clear_file(int i, struct file_list *flist);
+struct file_list *flist_new(int with_hlink, char *msg);
 void flist_free(struct file_list *flist);
 int f_name_cmp(struct file_struct *f1, struct file_struct *f2);
 char *f_name_to(struct file_struct *f, char *fbuf);
diff -rupNP --exclude-from cvs/.ignore cvs/receiver.c pool2/receiver.c
--- cvs/receiver.c	Wed Feb  4 03:49:36 2004
+++ pool2/receiver.c	Wed Feb  4 03:08:45 2004
@@ -305,6 +305,12 @@ int recv_files(int f_in,struct file_list
 		rprintf(FINFO,"recv_files(%d) starting\n",flist->count);
 	}
 
+	if (flist->hlink_pool)
+	{
+		pool_destroy(flist->hlink_pool);
+		flist->hlink_pool = NULL;
+	}
+
 	while (1) {
 		cleanup_disable();
 
diff -rupNP --exclude-from cvs/.ignore cvs/rsync.h pool2/rsync.h
--- cvs/rsync.h	Fri Feb  6 01:13:58 2004
+++ pool2/rsync.h	Fri Feb  6 02:02:23 2004
@@ -112,8 +112,12 @@
 #define FULL_FLUSH	1
 #define NORMAL_FLUSH	0
 
-#define CLEAR_STRUCT	0
-#define FREE_STRUCT	1
+#if HAVE_LINK
+#define WITH_HLINK	1
+#else
+#define WITH_HLINK	0
+#endif
+#define WITHOUT_HLINK	0
 
 /* Log-message categories.  FLOG is only used on the daemon side to
  * output messages to the log file. */
@@ -254,6 +258,7 @@ enum msgcode {
 
 #include <assert.h>
 
+#include "lib/pool_alloc.h"
 
 #define BOOL int
 
@@ -434,19 +439,25 @@ struct file_struct {
  */
 #define FLIST_START	(32 * 1024)
 #define FLIST_LINEAR	(FLIST_START * 512)
+/*
+ * Extent size for allocation pools A minimum size of 128KB
+ * is needed to mmap them so that freeing will release the
+ * space to the OS.
+ *
+ * Larger sizes reduce leftover fragments and speed free calls
+ * (when they happen) Smaller sizes increase the chance of
+ * freed allocations freeing whole extents.
+ */
 
-struct string_area {
-	char *base;
-	char *end;
-	char *current;
-	struct string_area *next;
-};
+#define FILE_EXTENT	(256 * 1024)
+#define HLINK_EXTENT	(128 * 1024)
 
 struct file_list {
 	int count;
 	int malloced;
+	alloc_pool_t file_pool;
+	alloc_pool_t hlink_pool;
 	struct file_struct **files;
-	struct string_area *string_area;
 };
 
 struct sum_buf {
Binary files cvs/t_unsafe and pool2/t_unsafe differ
Binary files cvs/tls and pool2/tls differ
Binary files cvs/trimslash and pool2/trimslash differ
Binary files cvs/wildtest and pool2/wildtest differ


More information about the rsync mailing list