memory reduction

jw schultz jw at pegasys.ws
Fri Feb 6 05:27:51 GMT 2004


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.


-- 
________________________________________________________________
	J.W. Schultz            Pegasystems Technologies
	email address:		jw at pegasys.ws

		Remember Cernan and Schmitt
-------------- next part --------------
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	Thu Feb  5 20:47:31 2004
+++ pool2/batch.c	Thu Feb  5 20:51:14 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;
@@ -153,9 +151,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	Wed Feb  4 17:39:32 2004
+++ pool2/flist.c	Thu Feb  5 18:41:21 2004
@@ -76,13 +76,12 @@ 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;
+	struct file_struct f;
 
-    /* Figure out how big the file_struct is without trailing padding */
-    min_file_struct_len = ((char*)&f.flags - (char*)&f) + sizeof f.flags;
+	/* Figure out how big the file_struct is without trailing padding */
+	min_file_struct_len = ((char*)&f.flags - (char*)&f) + sizeof f.flags;
 }
 
 
@@ -499,7 +498,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;
@@ -616,12 +616,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;
@@ -634,9 +634,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
 
@@ -668,15 +668,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
 
@@ -720,7 +724,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;
@@ -734,6 +739,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);
@@ -830,12 +836,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;
@@ -848,9 +860,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;
 	}
@@ -905,9 +917,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;
@@ -1026,9 +1037,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])) {
@@ -1177,6 +1191,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) {
@@ -1216,10 +1236,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);
@@ -1234,7 +1254,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;
@@ -1248,7 +1268,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);
@@ -1337,34 +1357,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;
 }
@@ -1374,9 +1402,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);
 }
@@ -1416,7 +1443,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	Wed Feb  4 01:00:45 2004
+++ pool2/proto.h	Thu Feb  5 18:49:59 2004
@@ -73,16 +73,18 @@ void show_flist_stats(void);
 int readlink_stat(const char *path, STRUCT_STAT *buffer, char *linkbuf);
 int link_stat(const char *path, STRUCT_STAT * buffer);
 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	Wed Feb  4 15:04:10 2004
+++ pool2/rsync.h	Thu Feb  5 14:18:32 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
 
@@ -428,20 +433,25 @@ struct file_struct {
 	uchar flags;	/* this item MUST remain last */
 };
 
-#define ARENA_SIZE	(32 * 1024)
+/*
+ * 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