refs: write packed_refs file using stdio
We write each line of a new packed-refs file individually using a write() syscall (and sometimes 2, if the ref is peeled). Since each line is only about 50-100 bytes long, this creates a lot of system call overhead. We can instead open a stdio handle around our descriptor and use fprintf to write to it. The extra buffering is not a problem for us, because nobody will read our new packed-refs file until we call commit_lock_file (by which point we have flushed everything). On a pathological repository with 8.5 million refs, this dropped the time to run `git pack-refs` from 20s to 6s. Signed-off-by: Jeff King <peff@peff.net> Reviewed-by: Michael Haggerty <mhagger@alum.mit.edu> Signed-off-by: Junio C Hamano <gitster@pobox.com>maint
							parent
							
								
									0c72b98f31
								
							
						
					
					
						commit
						9540ce5030
					
				
							
								
								
									
										2
									
								
								cache.h
								
								
								
								
							
							
						
						
									
										2
									
								
								cache.h
								
								
								
								
							|  | @ -1395,6 +1395,8 @@ extern const char *git_mailmap_blob; | ||||||
|  |  | ||||||
| /* IO helper functions */ | /* IO helper functions */ | ||||||
| extern void maybe_flush_or_die(FILE *, const char *); | extern void maybe_flush_or_die(FILE *, const char *); | ||||||
|  | __attribute__((format (printf, 2, 3))) | ||||||
|  | extern void fprintf_or_die(FILE *, const char *fmt, ...); | ||||||
| extern int copy_fd(int ifd, int ofd); | extern int copy_fd(int ifd, int ofd); | ||||||
| extern int copy_file(const char *dst, const char *src, int mode); | extern int copy_file(const char *dst, const char *src, int mode); | ||||||
| extern int copy_file_with_time(const char *dst, const char *src, int mode); | extern int copy_file_with_time(const char *dst, const char *src, int mode); | ||||||
|  |  | ||||||
							
								
								
									
										39
									
								
								refs.c
								
								
								
								
							
							
						
						
									
										39
									
								
								refs.c
								
								
								
								
							|  | @ -2191,25 +2191,12 @@ struct ref_lock *lock_any_ref_for_update(const char *refname, | ||||||
|  * Write an entry to the packed-refs file for the specified refname. |  * Write an entry to the packed-refs file for the specified refname. | ||||||
|  * If peeled is non-NULL, write it as the entry's peeled value. |  * If peeled is non-NULL, write it as the entry's peeled value. | ||||||
|  */ |  */ | ||||||
| static void write_packed_entry(int fd, char *refname, unsigned char *sha1, | static void write_packed_entry(FILE *fh, char *refname, unsigned char *sha1, | ||||||
| 			       unsigned char *peeled) | 			       unsigned char *peeled) | ||||||
| { | { | ||||||
| 	char line[PATH_MAX + 100]; | 	fprintf_or_die(fh, "%s %s\n", sha1_to_hex(sha1), refname); | ||||||
| 	int len; | 	if (peeled) | ||||||
|  | 		fprintf_or_die(fh, "^%s\n", sha1_to_hex(peeled)); | ||||||
| 	len = snprintf(line, sizeof(line), "%s %s\n", |  | ||||||
| 		       sha1_to_hex(sha1), refname); |  | ||||||
| 	/* this should not happen but just being defensive */ |  | ||||||
| 	if (len > sizeof(line)) |  | ||||||
| 		die("too long a refname '%s'", refname); |  | ||||||
| 	write_or_die(fd, line, len); |  | ||||||
|  |  | ||||||
| 	if (peeled) { |  | ||||||
| 		if (snprintf(line, sizeof(line), "^%s\n", |  | ||||||
| 			     sha1_to_hex(peeled)) != PEELED_LINE_LENGTH) |  | ||||||
| 			die("internal error"); |  | ||||||
| 		write_or_die(fd, line, PEELED_LINE_LENGTH); |  | ||||||
| 	} |  | ||||||
| } | } | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  | @ -2217,13 +2204,12 @@ static void write_packed_entry(int fd, char *refname, unsigned char *sha1, | ||||||
|  */ |  */ | ||||||
| static int write_packed_entry_fn(struct ref_entry *entry, void *cb_data) | static int write_packed_entry_fn(struct ref_entry *entry, void *cb_data) | ||||||
| { | { | ||||||
| 	int *fd = cb_data; |  | ||||||
| 	enum peel_status peel_status = peel_entry(entry, 0); | 	enum peel_status peel_status = peel_entry(entry, 0); | ||||||
|  |  | ||||||
| 	if (peel_status != PEEL_PEELED && peel_status != PEEL_NON_TAG) | 	if (peel_status != PEEL_PEELED && peel_status != PEEL_NON_TAG) | ||||||
| 		error("internal error: %s is not a valid packed reference!", | 		error("internal error: %s is not a valid packed reference!", | ||||||
| 		      entry->name); | 		      entry->name); | ||||||
| 	write_packed_entry(*fd, entry->name, entry->u.value.sha1, | 	write_packed_entry(cb_data, entry->name, entry->u.value.sha1, | ||||||
| 			   peel_status == PEEL_PEELED ? | 			   peel_status == PEEL_PEELED ? | ||||||
| 			   entry->u.value.peeled : NULL); | 			   entry->u.value.peeled : NULL); | ||||||
| 	return 0; | 	return 0; | ||||||
|  | @ -2259,15 +2245,22 @@ int commit_packed_refs(void) | ||||||
| 		get_packed_ref_cache(&ref_cache); | 		get_packed_ref_cache(&ref_cache); | ||||||
| 	int error = 0; | 	int error = 0; | ||||||
| 	int save_errno = 0; | 	int save_errno = 0; | ||||||
|  | 	FILE *out; | ||||||
|  |  | ||||||
| 	if (!packed_ref_cache->lock) | 	if (!packed_ref_cache->lock) | ||||||
| 		die("internal error: packed-refs not locked"); | 		die("internal error: packed-refs not locked"); | ||||||
| 	write_or_die(packed_ref_cache->lock->fd, |  | ||||||
| 		     PACKED_REFS_HEADER, strlen(PACKED_REFS_HEADER)); |  | ||||||
|  |  | ||||||
|  | 	out = fdopen(packed_ref_cache->lock->fd, "w"); | ||||||
|  | 	if (!out) | ||||||
|  | 		die_errno("unable to fdopen packed-refs descriptor"); | ||||||
|  |  | ||||||
|  | 	fprintf_or_die(out, "%s", PACKED_REFS_HEADER); | ||||||
| 	do_for_each_entry_in_dir(get_packed_ref_dir(packed_ref_cache), | 	do_for_each_entry_in_dir(get_packed_ref_dir(packed_ref_cache), | ||||||
| 				 0, write_packed_entry_fn, | 				 0, write_packed_entry_fn, out); | ||||||
| 				 &packed_ref_cache->lock->fd); | 	if (fclose(out)) | ||||||
|  | 		die_errno("write error"); | ||||||
|  | 	packed_ref_cache->lock->fd = -1; | ||||||
|  |  | ||||||
| 	if (commit_lock_file(packed_ref_cache->lock)) { | 	if (commit_lock_file(packed_ref_cache->lock)) { | ||||||
| 		save_errno = errno; | 		save_errno = errno; | ||||||
| 		error = -1; | 		error = -1; | ||||||
|  |  | ||||||
|  | @ -49,6 +49,21 @@ void maybe_flush_or_die(FILE *f, const char *desc) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void fprintf_or_die(FILE *f, const char *fmt, ...) | ||||||
|  | { | ||||||
|  | 	va_list ap; | ||||||
|  | 	int ret; | ||||||
|  |  | ||||||
|  | 	va_start(ap, fmt); | ||||||
|  | 	ret = vfprintf(f, fmt, ap); | ||||||
|  | 	va_end(ap); | ||||||
|  |  | ||||||
|  | 	if (ret < 0) { | ||||||
|  | 		check_pipe(errno); | ||||||
|  | 		die_errno("write error"); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| void fsync_or_die(int fd, const char *msg) | void fsync_or_die(int fd, const char *msg) | ||||||
| { | { | ||||||
| 	if (fsync(fd) < 0) { | 	if (fsync(fd) < 0) { | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 Jeff King
						Jeff King