git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: How git affects kernel.org performance

From
Linus Torvalds <torvalds@osdl.org>
Date
Jan 7, 2007, 19:13 UTC
Message-ID
<Pine.LNX.4.64.0701071028450.3661@woody.osdl.org>
In-Reply-To
<Pine.LNX.4.64.0701070957080.3661@woody.osdl.org>
On Sun, 7 Jan 2007, Linus Torvalds wrote:
Show 5 quoted lines
> 
> A year or two ago I did a totally half-assed code for the non-hashed 
> readdir that improved performance by an order of magnitude for ext3 for a 
> test-case of mine, but it was subtly buggy and didn't do the hashed case 
> AT ALL.

Btw, this isn't the test-case, but it's a half-way re-creation of something like it. It's _really_ stupid, but here's what you can do:

 - compile and run this idiotic program. It creates a directory called 
   "throwaway" that is ~44kB in size, and if I did things right, it should 
   not be totally contiguous on disk with the current ext3 allocation 
   logic.
 - as root, do "echo 3 > /proc/sys/vm/drop_caches" to get a cache-cold 
   schenario.
 - do "time ls throwaway > /dev/null".

I don't know what people consider to be reasonable performance, but for me, it takes about half a second to do a simple "ls". NOTE! This is _not_ reading inode stat information or anything like that. It literally takes 0.3-0.4 seconds to read ~44kB off the disk. That's a whopping 125kB/s throughput on a reasonably fast modern disk.

That's what we in the industry call  "sad".

And that's on a totally unloaded machine. There was _nothing_ else going on. No IO congestion, no nothing. Just the cost of synchronously doing ten or eleven disk reads.

The fix?
 - proper read-ahead. Right now, even if the directory is totally 
   contiguous on disk (just remove the thing that writes data to the 
   files, so that you'll have empty files instead of 8kB files), I think 
   we do those reads totally synchronously if the filesystem was mounted 
   with directory hashing enabled.
   Without hashing, the directory will be much smaller too, so readdir() 
   will have less data to read. And it _should_ do some readahead, 
   although in my testing, the best I could do was still 0.185s for a (now 
   shrunken) 28kB directory. 
 - better directory block allocation patterns would likely help a lot, 
   rather than single blocks. That's true even without any read-ahead (at 
   least the disk wouldn't need to seek, and any on-disk track buffers etc 
   would work better), but with read-ahead and contiguous blocks it should 
   be just a couple of IO's (the indirect stuff means that it's more than 
   one), and so you should see much better IO patterns because the 
   elevator can try to help too.

Maybe I just have unrealistic expectations, but I really don't like how a fairly small 50kB directory takes an appreciable fraction of a second to read.

Once it's cached, it still takes too long, but at least at that point the individual getdents calls take just tens of microseconds.

Here's cold-cache numbers (notice: 34 msec for the first one, and 17 msec in the middle.. The 5-6ms range indicates a single IO for the intermediate ones, which basically says that each call does roughly one IO, except the first one that does ~5 (probably the indirect index blocks), and two in the middle who are able to fill up the buffer from the IO done by the previous one (4kB buffers, so if the previous getdents() happened to just read the beginning of a block, the next one might be able to fill everything from that block without having to do IO).

	getdents(3, /* 103 entries */, 4096)    = 4088 <0.034830>
	getdents(3, /* 102 entries */, 4096)    = 4080 <0.006703>
	getdents(3, /* 102 entries */, 4096)    = 4080 <0.006719>
	getdents(3, /* 102 entries */, 4096)    = 4080 <0.000354>
	getdents(3, /* 102 entries */, 4096)    = 4080 <0.000017>
	getdents(3, /* 102 entries */, 4096)    = 4080 <0.005302>
	getdents(3, /* 102 entries */, 4096)    = 4080 <0.016957>
	getdents(3, /* 102 entries */, 4096)    = 4080 <0.000017>
	getdents(3, /* 102 entries */, 4096)    = 4080 <0.003530>
	getdents(3, /* 83 entries */, 4096)     = 3320 <0.000296>
	getdents(3, /* 0 entries */, 4096)      = 0 <0.000006>

Here's the pure CPU overhead: still pretty high (200 usec! For a single system call! That's disgusting! In contrast, a 4kB read() call takes 7 usec on this machine, so the overhead of doing things one dentry at a time, and calling down to several layers of filesystem is quite high):

	getdents(3, /* 103 entries */, 4096)    = 4088 <0.000204>
	getdents(3, /* 102 entries */, 4096)    = 4080 <0.000122>
	getdents(3, /* 102 entries */, 4096)    = 4080 <0.000112>
	getdents(3, /* 102 entries */, 4096)    = 4080 <0.000153>
	getdents(3, /* 102 entries */, 4096)    = 4080 <0.000018>
	getdents(3, /* 102 entries */, 4096)    = 4080 <0.000103>
	getdents(3, /* 102 entries */, 4096)    = 4080 <0.000217>
	getdents(3, /* 102 entries */, 4096)    = 4080 <0.000018>
	getdents(3, /* 102 entries */, 4096)    = 4080 <0.000095>
	getdents(3, /* 83 entries */, 4096)     = 3320 <0.000089>
	getdents(3, /* 0 entries */, 4096)      = 0 <0.000006>
but you can see the difference.. The real cost is obviously the IO.
		Linus

---- #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/stat.h> #include <sys/types.h>

static char buffer[8192];
static int create_file(const char *name)
{
	int fd = open(name, O_RDWR | O_CREAT | O_TRUNC, 0666);
	if (fd < 0)
		return fd;
	write(fd, buffer, sizeof(buffer));
	close(fd);
	return 0;
}
int main(int argc, char **argv)
{
	int i;
	char name[256];
	/* Fill up the buffer with some random garbage */
	for (i = 0; i < sizeof(buffer); i++)
		buffer[i] = "abcdefghijklmnopqrstuvwxyz\n"[i % 27];
	if (mkdir("throwaway", 0777) < 0 || chdir("throwaway") < 0) {
		perror("throwaway");
		exit(1);
	}
	/*
	 * Create a reasonably big directory by having a number
	 * of files with non-trivial filenames, and with some
	 * real content to fragment the directory blocks..
	 */
	for (i = 0; i < 1000; i++) {
		snprintf(name, sizeof(name),
			"file-name-%d-%d-%d-%d",
			i / 1000,
			(i / 100) % 10,
			(i / 10) % 10,
			(i / 1) % 10);
		create_file(name);
	}
	return 0;
}
Previous: Linus TorvaldsNext: Jan Engelhardt
Message 17 of 52 in “Re: [KORG] Re: kernel.org lies about latest -mm kernel”
  1. Jeff GarzikJan 7, 2007
  2. Linus TorvaldsJan 7, 2007
  3. Greg KHJan 7, 2007
  4. H. Peter AnvinJan 7, 2007
  5. Junio C HamanoJan 7, 2007
  6. Jeff GarzikJan 7, 2007
  7. Linus TorvaldsJan 7, 2007
  8. Martin LanghoffJan 7, 2007
  9. How git affects kernel.org performanceH. Peter Anvin, Jan 7, 2007
  10. Linus TorvaldsJan 7, 2007
  11. Willy TarreauJan 7, 2007
  12. H. Peter AnvinJan 7, 2007
  13. Willy TarreauJan 7, 2007
  14. Christoph HellwigJan 7, 2007
  15. Willy TarreauJan 7, 2007
  16. Linus TorvaldsJan 7, 2007
  17. Linus TorvaldsJan 7, 2007
  18. Jan EngelhardtJan 7, 2007
  19. Randy DunlapJan 7, 2007
  20. Jan EngelhardtJan 7, 2007
  21. Randy DunlapJan 7, 2007
  22. Linus TorvaldsJan 7, 2007
  23. Andrew MortonJan 7, 2007
  24. Rene HermanJan 7, 2007
  25. Suparna BhattacharyaJan 8, 2007
  26. Theodore TsoJan 8, 2007
  27. Johannes StezenbachJan 8, 2007
  28. Theodore TsoJan 8, 2007
  29. Pavel MachekJan 8, 2007
  30. Theodore TsoJan 8, 2007
  31. Jeff GarzikJan 8, 2007
  32. Paul JacksonJan 9, 2007
  33. Jeremy HigdonJan 9, 2007
  34. Fengguang WuJan 9, 2007
  35. Linus TorvaldsJan 9, 2007
  36. Fengguang WuJan 10, 2007
  37. Fengguang WuJan 10, 2007
  38. Fengguang WuJan 10, 2007
  39. Fengguang WuJan 9, 2007
  40. Fengguang WuJan 9, 2007
  41. Robert FitzsimonsJan 7, 2007
  42. J.H.Jan 7, 2007
  43. Jakub NarebskiJan 8, 2007
  44. Krzysztof HalasaJan 7, 2007
  45. Shawn O. PearceJan 7, 2007
  46. Nicolas PitreJan 8, 2007
  47. Linus TorvaldsJan 7, 2007
  48. Nigel CunninghamJan 10, 2007
  49. Fengguang WuJan 10, 2007
  50. Fengguang WuJan 10, 2007
  51. Fengguang WuJan 10, 2007
  52. Nigel CunninghamJan 12, 2007

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.