git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC \ WISH] Add -o option to git-rev-list
@ 2006-12-10 11:38 Marco Costalba
  2006-12-10 14:54 ` Alex Riesen
  2006-12-10 18:16 ` Linus Torvalds
  0 siblings, 2 replies; 39+ messages in thread
From: Marco Costalba @ 2006-12-10 11:38 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Junio C Hamano, Alex Riesen, Shawn Pearce, Linus Torvalds

I have done some tests about reading git-rev-list output with
different IPC facilities:

- Native Qt QProcess socket based IPC

- pipe based: popen() and fread()

- redirecting git-rev-list to a file (under tmpfs) and 'block reading'
back the file: read() under Qt QFile class. The file, always in
memory, is deleted at the end of loading.


I have tested with different block sizes and different CPU speed on
linux and git trees.

The averaged results on linux tree (about 30MB of data) and CPU set at
1.2GHz are:

- QProcess 6734ms

- pipe and fread() with 64KB blocks 4832ms (38% faster then QProcess)

- temporary file and read() with 64KB blocks 4321ms (10% faster then pipe)


I have not enough knowledge to understand why temporary file is faster
then pipe. My guess is, after reading some docs around, fread() uses a
C standard I/O buffer, while read() is unbuffered.

To make git-rev-list writing a temporary file I create and run a
script more or less like:

git rev-list --header --boundary --parents --topo-order HEAD  >
/tmp/qgit_135902672.txt

There is also some additional kludge to get git-rev-list pid, so to
use in case of a cancel request arrives while loading.


So my request is if it is possible to make git-rev-list write
_directly_ to a file, without shell redirection, I would ask if it is
possible:

- Add a -o, --output option to git-rev-list to specify output file
instead of stdout

- Use an unbuffered binary block write to be as fastest as possible

- Do *not* flush ever the file, it's only a waste under this scenario.


This will let me to avoid the shell launching crap and probably gain
also some more speed.

I understand this could be not exactly a top priority feature for git
people, but I would really like to get the best possible interface
with the plumbing git and the -o options is also a very common one.

Thanks
Marco

P.S: On another thread I explained why I see problematic linking
directly against libgit.a
I rewrite here for completeness:

> I've looked again to Shawn idea (and code) of linking qgit
> against libgit.a but I found these two difficult points:
>
> - traverse_commit_list(&revs, show_commit, show_object) is blocking,
> i.e. the GUI will stop responding for few seconds while traversing the
> list. This is easily and transparently solved by the OS scheduler if
> an external process is used for git-rev-list. To solve this in qgit I
> have two ways: 1) call QEventLoop() once in a while from inside
> show_commit()/ show_object() to process pending events  2) Use a
> separate thread (QThread class). The first idea is not nice, the
> second opens a whole a new set of problems and it's a big amount of
> not trivial new code to add.
>
> -  traverse_commit_list() having an internal state it's not
> re-entrant. git-rev-list it's used to load main view data but also
> file history in another tab, and the two calls _could_ be ran
> concurrently. With external process I simply run two instances of
> DataLoader class and consequently two external git-rev-list processes,
> but If I link against libgit.a that would be a big problem.

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC \ WISH] Add -o option to git-rev-list
  2006-12-10 11:38 [RFC \ WISH] Add -o option to git-rev-list Marco Costalba
@ 2006-12-10 14:54 ` Alex Riesen
  2006-12-10 18:16 ` Linus Torvalds
  1 sibling, 0 replies; 39+ messages in thread
From: Alex Riesen @ 2006-12-10 14:54 UTC (permalink / raw)
  To: Marco Costalba
  Cc: Git Mailing List, Junio C Hamano, Shawn Pearce, Linus Torvalds

Marco Costalba, Sun, Dec 10, 2006 12:38:42 +0100:
> So my request is if it is possible to make git-rev-list write
> _directly_ to a file, without shell redirection, I would ask if it is
> possible:

Well, it is usually possible to redirect stdout directly into a file
(see dup2). "Usually", unless you want windows which as always has
it's own stupid way of doing simple things. Nevertheless, it's
possible to do it without ever touching rev-list.

> I understand this could be not exactly a top priority feature for git
> people, but I would really like to get the best possible interface
> with the plumbing git and the -o options is also a very common one.

Sadly, you're right. Almost every command-line program got the option.
What education could have caused this, I wonder...

> P.S: On another thread I explained why I see problematic linking
> directly against libgit.a

It still is the fastest you can get.

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC \ WISH] Add -o option to git-rev-list
  2006-12-10 11:38 [RFC \ WISH] Add -o option to git-rev-list Marco Costalba
  2006-12-10 14:54 ` Alex Riesen
@ 2006-12-10 18:16 ` Linus Torvalds
  2006-12-10 19:51   ` Marco Costalba
  1 sibling, 1 reply; 39+ messages in thread
From: Linus Torvalds @ 2006-12-10 18:16 UTC (permalink / raw)
  To: Marco Costalba
  Cc: Git Mailing List, Junio C Hamano, Alex Riesen, Shawn Pearce



On Sun, 10 Dec 2006, Marco Costalba wrote:
> 
> The averaged results on linux tree (about 30MB of data) and CPU set at
> 1.2GHz are:
> 
> - QProcess 6734ms
> 
> - pipe and fread() with 64KB blocks 4832ms (38% faster then QProcess)
> 
> - temporary file and read() with 64KB blocks 4321ms (10% faster then pipe)
> 
> I have not enough knowledge to understand why temporary file is faster
> then pipe. My guess is, after reading some docs around, fread() uses a
> C standard I/O buffer, while read() is unbuffered.

Why don't you use the pipe and standard read()?

Even if you use "popen()" and get a "FILE *" back, you can still do

	int fd = fileno(file);

and use the raw IO capabilities.

The thing is, temporary files can actually be faster under Linux just 
because the Linux page-cache simply kicks ass. But it's not going to be 
_that_ big of a difference, and you need all that crazy "wait for rev-list 
to finish" and the "clean up temp-file on errors" etc crap, so there's no 
way it's a better solution.

If it really is stdio overhead (possibly locking), using "fileno()" and 
the raw unistd.h interfaces is going to avoid it.

(You still need to use "fclose()" to close the struct file afterwards, 
otherwise you'll leak memory, so you shouldn't _forget_ the original 
"struct FILE *", but you don't need to use it for anything else).

Using popen() and pipes also means that if the parent dies, the child will 
get a nice EPIPE on the writing side, which is what you want.


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC \ WISH] Add -o option to git-rev-list
  2006-12-10 18:16 ` Linus Torvalds
@ 2006-12-10 19:51   ` Marco Costalba
  2006-12-10 20:00     ` globs in partial checkout? Michael S. Tsirkin
                       ` (2 more replies)
  0 siblings, 3 replies; 39+ messages in thread
From: Marco Costalba @ 2006-12-10 19:51 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Git Mailing List, Junio C Hamano, Alex Riesen, Shawn Pearce

On 12/10/06, Linus Torvalds <torvalds@osdl.org> wrote:
>
> Why don't you use the pipe and standard read()?
>
> Even if you use "popen()" and get a "FILE *" back, you can still do
>
>         int fd = fileno(file);
>
> and use the raw IO capabilities.
>
> The thing is, temporary files can actually be faster under Linux just
> because the Linux page-cache simply kicks ass. But it's not going to be
> _that_ big of a difference, and you need all that crazy "wait for rev-list
> to finish" and the "clean up temp-file on errors" etc crap, so there's no
> way it's a better solution.
>

Two things.

- memory use: the next natural step with files is, instead of loading
the file content in memory and *keep it there*, we could load one
chunk at a time, index the chunk and discard. At the end we keep in
memory only indexing info to quickly get to the data when needed, but
the big part of data stay on the file.

- This is probably my ignorance, but experimenting with popen() I
found I could not know *when* git-rev-list ends because both feof()
and ferror() give 0 after a fread() with git-rev-list already defunct.
Not having a reference to the process (it is hidden behind popen() ),
I had to check for 0 bytes read after a successful read (to avoid
racing in case I ask the pipe before the first data it's ready) to
know that job is finished and call pclose().


^ permalink raw reply	[flat|nested] 39+ messages in thread

* globs in partial checkout?
  2006-12-10 19:51   ` Marco Costalba
@ 2006-12-10 20:00     ` Michael S. Tsirkin
  2006-12-10 20:13       ` Linus Torvalds
  2006-12-10 20:08     ` [RFC \ WISH] Add -o option to git-rev-list Linus Torvalds
  2006-12-11 11:39     ` Andreas Ericsson
  2 siblings, 1 reply; 39+ messages in thread
From: Michael S. Tsirkin @ 2006-12-10 20:00 UTC (permalink / raw)
  To: Git Mailing List, Junio C Hamano

I'm trying to checkout some files after doing "clone -n".
Should using globs there work? It doesn't:

st@mst-lt:~/scm/wireless-dev$ git checkout master 'include/net/ieee80211*.h'
error: pathspec 'include/net/ieee80211*.h' did not match any file(s) known to
git.
Did you forget to 'git add'?
mst@mst-lt:~/scm/wireless-dev$ git ls-tree master -- include/net/ | grep iee
100644 blob b174ebb277a96668f058e469b0753503c34f164b include/net/ieee80211.h
100644 blob eb476414fd726701d032e9e517751b9d3f7e38df include/net/ieee80211_crypt.h
100644 blob 429b73892a5fc62f91e4a4b05da40859604fa791 include/net/ieee80211_radiotap.h
100644 blob 617b672b1132e7fa3ff5f9c940b1692520dc8483 include/net/ieee80211softmac.h
100644 blob 4ee3ad57283fa3370bd2d1f71cd6ae559b556dbc include/net/ieee80211softmac_wx.h
mst@mst-lt:~/scm/wireless-dev$ git checkout master include/net/ieee80211.h
include/net/ieee80211_crypt.h include/net/ieee80211_radiotap.h
include/net/ieee80211softmac.h include/net/ieee80211softmac_wx.h


-- 

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC \ WISH] Add -o option to git-rev-list
  2006-12-10 19:51   ` Marco Costalba
  2006-12-10 20:00     ` globs in partial checkout? Michael S. Tsirkin
@ 2006-12-10 20:08     ` Linus Torvalds
  2006-12-10 20:19       ` Linus Torvalds
  2006-12-11 11:39     ` Andreas Ericsson
  2 siblings, 1 reply; 39+ messages in thread
From: Linus Torvalds @ 2006-12-10 20:08 UTC (permalink / raw)
  To: Marco Costalba
  Cc: Git Mailing List, Junio C Hamano, Alex Riesen, Shawn Pearce



On Sun, 10 Dec 2006, Marco Costalba wrote:
> 
> - memory use: the next natural step with files is, instead of loading
> the file content in memory and *keep it there*, we could load one
> chunk at a time, index the chunk and discard. At the end we keep in
> memory only indexing info to quickly get to the data when needed, but
> the big part of data stay on the file.

Well, that's still just going slower than swapping.

The reason loading things into memory is nice is that:
 - in the common case, you don't need to do anything else.
 - if  the machine is low on memory, it can page things out just about as 
   easily as you could write things to a file anyway.

So don't worry too much about low-memory situations. Yes, there are cases 
where it's better to keep things in files and simply not have a big 
working set AT ALL, but if you keep something in a file and the file data 
is still part of the working set (ie you read it several times, but at the 
beginning and the end), that really isn't any better than having it in 
memory.

So the time to try to optimize memory usage is really only for "streaming 
behaviour" - where you need to touch something only once. Then the best 
option is to actually use a pipe and re-use the memory, but if you have 
file data, you can use things like fadvise(DONTNEED).

> - This is probably my ignorance, but experimenting with popen() I
> found I could not know *when* git-rev-list ends because both feof()
> and ferror() give 0 after a fread() with git-rev-list already defunct.

I suspect you had a bug somewhere. It could be a bug in stdio, but I doubt 
it.

You do realize that the correct way to check "feof()" is only _after_ 
fread() returns 0? 

Stdio ferror/feof is horrible to use corrrectly, and few people get it 
right.  Mostly because it's such a crap interface thanks to being "easy" 
to use and thus hiding all the error handling details on purpose.


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: globs in partial checkout?
  2006-12-10 20:00     ` globs in partial checkout? Michael S. Tsirkin
@ 2006-12-10 20:13       ` Linus Torvalds
  2006-12-10 21:07         ` Michael S. Tsirkin
  0 siblings, 1 reply; 39+ messages in thread
From: Linus Torvalds @ 2006-12-10 20:13 UTC (permalink / raw)
  To: Michael S. Tsirkin; +Cc: Git Mailing List, Junio C Hamano



On Sun, 10 Dec 2006, Michael S. Tsirkin wrote:
>
> I'm trying to checkout some files after doing "clone -n".
> Should using globs there work? It doesn't:

Not historically at all. "git checkout" needed exact filenames in older 
versions.

However, since about 1.4.4.1 or so, it now does the same filename 
expansion as "git add" etc does, which means that you can give it a 
directory name and it will check out everything under that directory, or 
you can give it a pattern, and it should glob it. But it sounds like you 
may have a slightly older version of git (the pathname matching really is 
fairly recent).


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC \ WISH] Add -o option to git-rev-list
  2006-12-10 20:08     ` [RFC \ WISH] Add -o option to git-rev-list Linus Torvalds
@ 2006-12-10 20:19       ` Linus Torvalds
  2006-12-10 22:05         ` Marco Costalba
  0 siblings, 1 reply; 39+ messages in thread
From: Linus Torvalds @ 2006-12-10 20:19 UTC (permalink / raw)
  To: Marco Costalba
  Cc: Git Mailing List, Junio C Hamano, Alex Riesen, Shawn Pearce



On Sun, 10 Dec 2006, Linus Torvalds wrote:
> 
> So don't worry too much about low-memory situations.

Btw, I should obviously clarify that. You should _always_ worry about 
low-memory situations, but the only real issue is really "working set 
size", not "data in files or in memory".

If the file representation is very dense (like the git pack-files are, for 
example), then it may well make sense to keep the data in a file, just 
because it's smaller there than if you keep it in expanded form in memory.

Also, it's nice to keep stuff in the filesystem rather than in process VM 
memory, because filesystem data that is cached in memory is useful for 
_other_ processes, ie it has a lifetime that is longer than the process 
itself. However, that's obviously only true for long-lived files that are 
shared among processes, it's _not_ true for temporary files.

For temporary files, the memory footprint of a temp-file is usually 
_larger_ than the memory footprint of the same data kept in memory. You 
have things like page-cache alignment, and often the issue of marshalling 
data into ASCII etc. So temp-files are almost never a better solution than 
keeping things in memory (unless you use those temp-files to truly 
_share_ data between processes, ie you do a shared mmap and they can 
re-use the same pages actively in a way they couldn't otherwise).


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: globs in partial checkout?
  2006-12-10 20:13       ` Linus Torvalds
@ 2006-12-10 21:07         ` Michael S. Tsirkin
  0 siblings, 0 replies; 39+ messages in thread
From: Michael S. Tsirkin @ 2006-12-10 21:07 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: Git Mailing List, Junio C Hamano

> However, since about 1.4.4.1 or so, it now does the same filename 
> expansion as "git add" etc does, which means that you can give it a 
> directory name and it will check out everything under that directory, or 
> you can give it a pattern, and it should glob it. But it sounds like you 
> may have a slightly older version of git (the pathname matching really is 
> fairly recent).

Seems like this was post-1.4.4.1. Just updated to 1.4.4.2 and this works for me now,
thanks.

-- 

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC \ WISH] Add -o option to git-rev-list
  2006-12-10 20:19       ` Linus Torvalds
@ 2006-12-10 22:05         ` Marco Costalba
  2006-12-10 22:09           ` Marco Costalba
  2006-12-10 22:16           ` Linus Torvalds
  0 siblings, 2 replies; 39+ messages in thread
From: Marco Costalba @ 2006-12-10 22:05 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Git Mailing List, Junio C Hamano, Alex Riesen, Shawn Pearce

On 12/10/06, Linus Torvalds <torvalds@osdl.org> wrote:
>

> data into ASCII etc. So temp-files are almost never a better solution than
> keeping things in memory (unless you use those temp-files to truly
> _share_ data between processes, ie you do a shared mmap and they can
> re-use the same pages actively in a way they couldn't otherwise).
>

Ok. Perhaps I'm doing something wrong but the following code it's
always 10% slower then the temporary file one (4.7s against 4.3s for
linux tree)

bool DataLoader::start(const QStringList& args, const QString& workDir) {

	QDir::setCurrent(workDir);

	_file = popen(args.join(" ").ascii(), "r");
	if (!_file)
		return false;
	
	loadTime.start();
	guiUpdateTimer.start(10, true); // will call on_timeout() in 10ms
	return true;
}

void DataLoader::on_timeout() {

	if (canceling)
		deleteLater();

// 	int fd = fileno(_file);    // read() case

	ssize_t len = 0;
	while (1) {

		QByteArray* ba = new QByteArray(FILE_BLOCK_SIZE); // 64KB

// 		len = read(fd, ba->data(), ba->size());        // read() case

		len = fread(ba->data(), 1, ba->size(), _file); // fread() case
		if (len <= 0) {
			delete ba;
			break;

		} else if (len < (ssize_t)ba->size()) // very rare, 4 out of 40000
on Linux tree
			ba->resize(len);

		loadedBytes += len;
		fh->rowData.append(ba); // fh->rowData it's a pointer's list
		parseSingleBuffer(ba);

		// avoid reading small chunks if data producer is still running
		if (len < FILE_BLOCK_SIZE)
			break;
	}

// 	if (len == 0) {         // read() case
	if (feof(_file)) {      // fread() case
	
		emit loaded(fh, loadedBytes, loadTime.elapsed(), true, "", ""); //
show some stats

		pclose(_file);
		_file = NULL;
		deleteLater();
	} else
		guiUpdateTimer.start(100, true); // next read in 100ms	
}

Uncomment 'read() case' lines and comment out the 'fread case()' ones
and you have a way slooooower code, about 10 times slower!



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC \ WISH] Add -o option to git-rev-list
  2006-12-10 22:05         ` Marco Costalba
@ 2006-12-10 22:09           ` Marco Costalba
  2006-12-10 22:16           ` Linus Torvalds
  1 sibling, 0 replies; 39+ messages in thread
From: Marco Costalba @ 2006-12-10 22:09 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Git Mailing List, Junio C Hamano, Alex Riesen, Shawn Pearce

On 12/10/06, Marco Costalba <mcostalba@gmail.com> wrote:
> On 12/10/06, Linus Torvalds <torvalds@osdl.org> wrote:
> >
> void DataLoader::on_timeout() {
>
>         if (canceling)
>                 deleteLater();
>

Of course is

	if (canceling) {
		deleteLater();
		return;
	}


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC \ WISH] Add -o option to git-rev-list
  2006-12-10 22:05         ` Marco Costalba
  2006-12-10 22:09           ` Marco Costalba
@ 2006-12-10 22:16           ` Linus Torvalds
  2006-12-10 22:35             ` Marco Costalba
  1 sibling, 1 reply; 39+ messages in thread
From: Linus Torvalds @ 2006-12-10 22:16 UTC (permalink / raw)
  To: Marco Costalba
  Cc: Git Mailing List, Junio C Hamano, Alex Riesen, Shawn Pearce



On Sun, 10 Dec 2006, Marco Costalba wrote:
> 
> Ok. Perhaps I'm doing something wrong but the following code it's
> always 10% slower then the temporary file one (4.7s against 4.3s for
> linux tree)

Why do you seem to be doing a "new" on every iteration inside the loop?

Also, why do you have that strange FILE_BLOCK_SIZE thing, and in 
particular the "if (len < FILE_BLOCK_SIZE)" check? One thing that pipes vs 
files do is the blocking factor.

Especially with older kernels, I _guarantee_ you that you'll only ever get 
4kB at a time, so because of that "if (len < 64kB) break" thing, the only 
thing you're doing is to make sure things suck performance-wise, and you 
won't be reading the rest of the data until 100ms later.

IOW, your code is written for a file, and makes no sense there either 
(checking "feof(file)" is wrong, since you may well have hit the EOF 
*at*that*time*, but the file may GROW since you are doing it all in the 
background, so you can't rely on feof() anyway).

For a pipe, what you should do is to make sure it's in nonblocking mode, 
and just continue reading until it gets no more. And instead of using a 
timeout, you should use poll() or something to get notified when there is 
more data.

IOW, the reason it's slow is because you're doing the wrong thing.


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC \ WISH] Add -o option to git-rev-list
  2006-12-10 22:16           ` Linus Torvalds
@ 2006-12-10 22:35             ` Marco Costalba
  2006-12-10 22:53               ` Linus Torvalds
  0 siblings, 1 reply; 39+ messages in thread
From: Marco Costalba @ 2006-12-10 22:35 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Git Mailing List, Junio C Hamano, Alex Riesen, Shawn Pearce

On 12/10/06, Linus Torvalds <torvalds@osdl.org> wrote:
>
>
> On Sun, 10 Dec 2006, Marco Costalba wrote:
> >
> > Ok. Perhaps I'm doing something wrong but the following code it's
> > always 10% slower then the temporary file one (4.7s against 4.3s for
> > linux tree)
>
> Why do you seem to be doing a "new" on every iteration inside the loop?
>

Becuase it's there where I store the file content.

Function parseSingleBuffer(ba) does only the indexing. But the file
content is stored in QByteArray objects (little wrappers around a
const char* []). So the fread() in the byte array object is the _only_
data copy operation in whole qgit.


> Also, why do you have that strange FILE_BLOCK_SIZE thing, and in
> particular the "if (len < FILE_BLOCK_SIZE)" check? One thing that pipes vs
> files do is the blocking factor.
>
> Especially with older kernels, I _guarantee_ you that you'll only ever get
> 4kB at a time, so because of that "if (len < 64kB) break" thing, the only
> thing you're doing is to make sure things suck performance-wise, and you
> won't be reading the rest of the data until 100ms later.
>

I consistently have len == 65536 bytes until the last fread() where
it's less. See below run against qgit own repository with 'len'
printed inside while loop.

$ ./qgit
Found GNU source-highlight 2.5
len is <65536>
len is <65536>
len is <65536>
len is <65536>
len is <65536>
len is <65536>
len is <65536>
len is <54479>
bash-3.1$

> IOW, your code is written for a file, and makes no sense there either
> (checking "feof(file)" is wrong, since you may well have hit the EOF
> *at*that*time*, but the file may GROW since you are doing it all in the
> background, so you can't rely on feof() anyway).
>

Yes. feof() it's difficult to handle correctly.

> For a pipe, what you should do is to make sure it's in nonblocking mode,
> and just continue reading until it gets no more. And instead of using a
> timeout, you should use poll() or something to get notified when there is
> more data.
>

How can open in nonblocking mode with popen() ?

FILE *popen(const char *command, const char *type);


> IOW, the reason it's slow is because you're doing the wrong thing.
>

That's for sure :-)  My problem is to guess what's is wrong.



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC \ WISH] Add -o option to git-rev-list
  2006-12-10 22:35             ` Marco Costalba
@ 2006-12-10 22:53               ` Linus Torvalds
  2006-12-11  0:15                 ` Marco Costalba
  0 siblings, 1 reply; 39+ messages in thread
From: Linus Torvalds @ 2006-12-10 22:53 UTC (permalink / raw)
  To: Marco Costalba
  Cc: Git Mailing List, Junio C Hamano, Alex Riesen, Shawn Pearce



On Sun, 10 Dec 2006, Marco Costalba wrote:
> 
> I consistently have len == 65536 bytes until the last fread() where
> it's less.

That's because fread() will block until it gets all data. Did you actually 
ever try this with a uncached tree and did you compare what you got with a 
plain "read()". 

On older kernels, I guarantee that you get 4kB at a time for reads, even 
for a blocking pipe. Because we have bigger pipe buffers these days, it 
_may_ return 64kB at a time every time, but only if the writer is much 
faster than the reader.

Based on the fact that you say that "read()" was ten times slower than 
fread(), I very much suspect you got 4kB at a time, and then slept 100ms 
each time or something.

Anyway, you should seriously also check the case when "git rev-list" is 
slow because you have cold caches and unpacked objects. You can't wait for 
it to synchronously write a thousand lines or so - that could take 
seconds.

> How can open in nonblocking mode with popen() ?
> 
> FILE *popen(const char *command, const char *type);

something like

	fcntl(fileno(file), F_SETFL, O_NONBLOCK);

will do it, and then your loop should look something like

	for (;;) {
		int count = read(fileno(file), buf, BUFSIZE);
		if (!count) {
			/* All done, no more to read */
			return 0;
		}
		if (count < 0) {
			if (errno == EAGAIN)
				break;
			if (errno == EINTR)
				continue;
			/* Anything else is fatal - report error */
			return -1;
		}
		... handle 'count' new bytes here ...
	}
	.. this is the EAGAIN case ..
	either set polling on the file descriptor, or use a
	timer here yo get back to this loop eventually.

looks about right.


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC \ WISH] Add -o option to git-rev-list
  2006-12-10 22:53               ` Linus Torvalds
@ 2006-12-11  0:15                 ` Marco Costalba
  2006-12-11  0:51                   ` Linus Torvalds
  2006-12-11  9:26                   ` Josef Weidendorfer
  0 siblings, 2 replies; 39+ messages in thread
From: Marco Costalba @ 2006-12-11  0:15 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Git Mailing List, Junio C Hamano, Alex Riesen, Shawn Pearce

On 12/10/06, Linus Torvalds <torvalds@osdl.org> wrote:
>
> looks about right.
>

Yes it's right. Thanks!

But it's still slow. Almost one second (985ms) to read the little qgit repo:

$ ./qgit HEAD
Found GNU source-highlight 2.5
count is <-1>
count is <60169>
count is <-1>
count is <60505>
count is <-1>
count is <61462>
count is <-1>
count is <61911>
count is <-1>
count is <61392>
count is <-1>
count is <61880>
count is <-1>
count is <62009>
count is <-1>
count is <62549>
count is <-1>
count is <21354>
count is <0>
$

As a compare the temporary file version needs a mere 105ms (1030 revs).

This is the code under test:

bool DataLoader::start(const QStringList& args, const QString& workDir) {

	QDir::setCurrent(workDir);
	_file = popen(args.join(" ").ascii(), "r");
	if (!_file)
		return false;

	fcntl(fileno(_file), F_SETFL, O_NONBLOCK);
	
	loadTime.start();
	guiUpdateTimer.start(10, true);
	return true;
}

void DataLoader::on_timeout() {

	if (canceling) {
		deleteLater();
		return;
	}

	int count;
	for (;;) {

		QByteArray* ba = new QByteArray(FILE_BLOCK_SIZE); // 64KB

		// this is the ONLY deep copy involved in the whole loading
		count = read(fileno(_file), ba->data(), ba->size());

		dbg(count); // DEBUG print

		if (count == 0) {
			/* All done, no more to read */
			delete ba;
			break;
		}
		if (count < 0) {
			delete ba;
			if (errno == EAGAIN)
				break;
			if (errno == EINTR)
				continue;

                       /* Anything else is fatal - report error */
                       dbg("Fatal error");
                       on_cancel();
                       deleteLater();
                       return;
		}
		if (count < (int)ba->size()) // very rare
			ba->resize(count);

		loadedBytes += count;
		fh->rowData.append(ba);
		parseSingleBuffer(*ba);
	}

	if (count == 0) {
		emit loaded(fh, loadedBytes, loadTime.elapsed(), true, "", "");
		pclose(_file);
		_file = NULL;
		deleteLater();
	} else
		guiUpdateTimer.start(100, true);

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC \ WISH] Add -o option to git-rev-list
  2006-12-11  0:15                 ` Marco Costalba
@ 2006-12-11  0:51                   ` Linus Torvalds
  2006-12-11  7:17                     ` Marco Costalba
  2006-12-11  9:26                   ` Josef Weidendorfer
  1 sibling, 1 reply; 39+ messages in thread
From: Linus Torvalds @ 2006-12-11  0:51 UTC (permalink / raw)
  To: Marco Costalba
  Cc: Git Mailing List, Junio C Hamano, Alex Riesen, Shawn Pearce



On Mon, 11 Dec 2006, Marco Costalba wrote:

> 
> But it's still slow. Almost one second (985ms) to read the little qgit repo:

Right. Because every time you sleep, you sleep for 100 ms.

That's why I was saying that you need to add polling to the thing. I don't 
know what the QT interfaces to asynchronous polling file descriptors are, 
but as long as you just blindly wait for 100ms whenever you run out of 
data, things will always suck.

Using "fread()" hid this problem, because the thing would block in 
fread(), and thus you'd nor see as many of these events.

> As a compare the temporary file version needs a mere 105ms (1030 revs).

How about you just compare something simpler:

	git-rev-list | cat > /dev/null

vs 

	git-rev-list > tmpfile ; cat tmpfile > /dev/null

and see which one works better.

If the pipe works better, that means that it's your code that is buggy and 
broken.

Which gets us back to the basic issue: you're asking for a bad interface.

This is your problem:

> 		guiUpdateTimer.start(100, true);

rather than just blindly starting a timer, you should ask it to wait until 
more data is available.


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC \ WISH] Add -o option to git-rev-list
  2006-12-11  0:51                   ` Linus Torvalds
@ 2006-12-11  7:17                     ` Marco Costalba
  2006-12-11 10:00                       ` Alex Riesen
  2006-12-11 16:59                       ` Linus Torvalds
  0 siblings, 2 replies; 39+ messages in thread
From: Marco Costalba @ 2006-12-11  7:17 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Git Mailing List, Junio C Hamano, Alex Riesen, Shawn Pearce

On 12/11/06, Linus Torvalds <torvalds@osdl.org> wrote:

>
> How about you just compare something simpler:
>
>         git-rev-list | cat > /dev/null
>
> vs
>
>         git-rev-list > tmpfile ; cat tmpfile > /dev/null
>
> and see which one works better.
>

These are tipical values (warm cache):

  $ time git rev-list --header --boundary --parents --topo-order HEAD  /dev/null
3.04user 0.05system 0:03.09elapsed 100%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+10141minor)pagefaults 0swaps

$ time git rev-list --header --boundary --parents --topo-order HEAD |
cat > /dev/null
3.67user 0.36system 0:04.29elapsed 93%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+18033minor)pagefaults 0swaps

$ time git rev-list --header --boundary --parents --topo-order HEAD >
/tmp/tmp.txt; cat /tmp/tmp.txt > /dev/null
3.44user 0.28system 0:03.74elapsed 99%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+18033minor)pagefaults 0swaps

For some reason the CPU *never* goes up 93% with pipe (while it's easy
in the other two cases) and I repeated that test at lest 10 times
consecutively. Is it perhaps the signature of some blocking around? Or
too much high frequency of receiver's read call (see below) ?

>
> This is your problem:
>
> >               guiUpdateTimer.start(100, true);
>
> rather than just blindly starting a timer, you should ask it to wait until
> more data is available.
>

OK. I just don't understand how after waiting 100ms I get only 60KB
and stay in the loop only one cycle instead of reading, for many
cycles, much more then 60KB and then wait another 100ms and found
another big (about 1MB) amount of data ready to be read as with the
file case.

Perhaps the pipe buffers are small and block the writer when full. In
the file case when I come back in the loop after 100ms I found _a lot_
of data to be read and I stay in the loop for much more then 1 cycle
before to wait again 100ms.

On the other hand, going to read each say less then 10ms is exactly
what QProcess (socket based) was doing and I ended up with my read
function being called at furious pace slowing down everything.
Experimenting with QProcess I found that, for performance reasons,
it's better to read big chunks few times then small chunks a lot of
times.

  Marco

P.S: 30MB for 64KB each chunk it's 468, in 3.67s it's 1 call each
7.8ms. If the pipe calls the receiver for data ready after each 4KB
(old kernels) then we should have 7.500 calls. Impossible to read in
3.67s, it would be a theoretical 1 call each 0.48ms average.

So IMHO bigger buffers for each read call could be the way to get

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC \ WISH] Add -o option to git-rev-list
  2006-12-11  0:15                 ` Marco Costalba
  2006-12-11  0:51                   ` Linus Torvalds
@ 2006-12-11  9:26                   ` Josef Weidendorfer
  2006-12-11 12:52                     ` Marco Costalba
  1 sibling, 1 reply; 39+ messages in thread
From: Josef Weidendorfer @ 2006-12-11  9:26 UTC (permalink / raw)
  To: Marco Costalba
  Cc: Linus Torvalds, Git Mailing List, Junio C Hamano, Alex Riesen,
	Shawn Pearce

On Monday 11 December 2006 01:15, Marco Costalba wrote:
> 		guiUpdateTimer.start(100, true);

What is the result with	"guiUpdateTimer.start(0, true);" ?
There is no need to put in any time interval at all, because
the timeout is a normal event which will be queued in the GUI
event queue.

If there were X events in the mean time, they are queued and handled
before your timeOut function is called again. So the GUI will be
responsive, even if you have a 0 ms timer. 


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC \ WISH] Add -o option to git-rev-list
  2006-12-11  7:17                     ` Marco Costalba
@ 2006-12-11 10:00                       ` Alex Riesen
  2006-12-11 16:59                       ` Linus Torvalds
  1 sibling, 0 replies; 39+ messages in thread
From: Alex Riesen @ 2006-12-11 10:00 UTC (permalink / raw)
  To: Marco Costalba
  Cc: Linus Torvalds, Git Mailing List, Junio C Hamano, Shawn Pearce

On 12/11/06, Marco Costalba <mcostalba@gmail.com> wrote:
>   $ time git rev-list --header --boundary --parents --topo-order HEAD
> /dev/null
> 3.04user 0.05system 0:03.09elapsed 100%CPU (0avgtext+0avgdata 0maxresident)k
> 0inputs+0outputs (0major+10141minor)pagefaults 0swaps

here you filtered output of git-rev-list by /dev/null, which makes no sense

> $ time git rev-list --header --boundary --parents --topo-order HEAD >
> /tmp/tmp.txt; cat /tmp/tmp.txt > /dev/null
> 3.44user 0.28system 0:03.74elapsed 99%CPU (0avgtext+0avgdata 0maxresident)k
> 0inputs+0outputs (0major+18033minor)pagefaults 0swaps

should be: time { git-rev-list >/tmp/tmp; cat /tmp/tmp >/dev/null; }

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC \ WISH] Add -o option to git-rev-list
  2006-12-10 19:51   ` Marco Costalba
  2006-12-10 20:00     ` globs in partial checkout? Michael S. Tsirkin
  2006-12-10 20:08     ` [RFC \ WISH] Add -o option to git-rev-list Linus Torvalds
@ 2006-12-11 11:39     ` Andreas Ericsson
  2006-12-11 12:59       ` Marco Costalba
  2 siblings, 1 reply; 39+ messages in thread
From: Andreas Ericsson @ 2006-12-11 11:39 UTC (permalink / raw)
  To: Marco Costalba
  Cc: Linus Torvalds, Git Mailing List, Junio C Hamano, Alex Riesen,
	Shawn Pearce

Marco Costalba wrote:
> On 12/10/06, Linus Torvalds <torvalds@osdl.org> wrote:
>>
>> Why don't you use the pipe and standard read()?
>>
>> Even if you use "popen()" and get a "FILE *" back, you can still do
>>
>>         int fd = fileno(file);
>>
>> and use the raw IO capabilities.
>>
>> The thing is, temporary files can actually be faster under Linux just
>> because the Linux page-cache simply kicks ass. But it's not going to be
>> _that_ big of a difference, and you need all that crazy "wait for 
>> rev-list
>> to finish" and the "clean up temp-file on errors" etc crap, so there's no
>> way it's a better solution.
>>
> 
> Two things.
> 
> - memory use: the next natural step with files is, instead of loading
> the file content in memory and *keep it there*, we could load one
> chunk at a time, index the chunk and discard. At the end we keep in
> memory only indexing info to quickly get to the data when needed, but
> the big part of data stay on the file.
> 

memory usage vs speed tradeoff. Since qgit is a pure user-app, I think 
it's safe to opt for the memory hungry option. If people run it on too 
lowbie hardware they'll just have to make do with other ways of viewing 
the DAG or shutting down some other programs.

> - This is probably my ignorance, but experimenting with popen() I
> found I could not know *when* git-rev-list ends because both feof()
> and ferror() give 0 after a fread() with git-rev-list already defunct.
> Not having a reference to the process (it is hidden behind popen() ),
> I had to check for 0 bytes read after a successful read (to avoid
> racing in case I ask the pipe before the first data it's ready) to
> know that job is finished and call pclose().
> 

(coding in MUA, so highly untested)

pid_t runcmd(const char **argv, const char **env, int *out, *int err)
{
	int pid;

	pipe(out);
	pipe(err);

	pid = fork();


	/* parent returns upon forking */
	if (pid) {
		/* close childs file-descriptors in our address space */
		close(out[0]);
		close(err[0]);
		return pid;
	}

	/* child */
	/* close parents file-descriptors */
	close(out[0]);
	close(err[0]);

	/* stdout and stderr writes to childs ends of the pipes */
	dup2(out[1], STDOUT_FILENO);
	dup2(err[1], STDERR_FILENO);
	execve(argv[0], argv, NULL);

	_exit(0); /* not reached unless execve() failed */
}

The caller can now read out[0] and err[0] as regular file-descriptors 
and has the pid of the child-process in the return value. The parent 
receives SIGCHILD if the child exits, the child receives EPIPE when 
writing if the parent crashes.

In a prudent implementation, the parent should waitpid(pid_t pid, int 
*status, WNOHANG) for the child every once in a while to collect its 
exit status gracefully.

Do some experimenting and I'm sure you'll find something suitable for you.

-- 
Andreas Ericsson                   andreas.ericsson@op5.se
OP5 AB                             www.op5.se

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC \ WISH] Add -o option to git-rev-list
  2006-12-11  9:26                   ` Josef Weidendorfer
@ 2006-12-11 12:52                     ` Marco Costalba
  2006-12-11 13:28                       ` Josef Weidendorfer
  0 siblings, 1 reply; 39+ messages in thread
From: Marco Costalba @ 2006-12-11 12:52 UTC (permalink / raw)
  To: Josef Weidendorfer
  Cc: Linus Torvalds, Git Mailing List, Junio C Hamano, Alex Riesen,
	Shawn Pearce

On 12/11/06, Josef Weidendorfer <Josef.Weidendorfer@gmx.de> wrote:
> On Monday 11 December 2006 01:15, Marco Costalba wrote:
> >               guiUpdateTimer.start(100, true);
>
> What is the result with "guiUpdateTimer.start(0, true);" ?
> There is no need to put in any time interval at all, because
> the timeout is a normal event which will be queued in the GUI
> event queue.
>
> If there were X events in the mean time, they are queued and handled
> before your timeOut function is called again. So the GUI will be
> responsive, even if you have a 0 ms timer.
>

IOW you suggest to use a brute force polling of the pipe.

I'will try this, just to test the pipe, not the application because I
would really need to read data in big chunks.

Data read from pipe/file or something else is read in an array (char*
[]) and do not touched anymore, no more copies are made. For every
read a new array is allocated on the heap.

With chunks of 64KB, because each rev descriptions is about few
hundered bytes only few revs (about 1% with current linux tree) fall
at the boundary of the chunk, half in one and the rest in the next.
This boundary revs require a special handling that at the end involves
an additional copy.

If I get from the pipe very small data (few KB) probably it would be
better to read always in a local array of fixed size and then copy to
final destination in memory. But this imply that all the data work
copy is duplicated: instead of 30MB of data we'll going to move 60MB
(linux tree)

With big chunks of 64KB only about 1% of data (revs that fall at the
array boundary) is duplicated.


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC \ WISH] Add -o option to git-rev-list
  2006-12-11 11:39     ` Andreas Ericsson
@ 2006-12-11 12:59       ` Marco Costalba
  2006-12-11 13:40         ` Andreas Ericsson
  0 siblings, 1 reply; 39+ messages in thread
From: Marco Costalba @ 2006-12-11 12:59 UTC (permalink / raw)
  To: Andreas Ericsson
  Cc: Linus Torvalds, Git Mailing List, Junio C Hamano, Alex Riesen,
	Shawn Pearce

On 12/11/06, Andreas Ericsson <ae@op5.se> wrote:
> Marco Costalba wrote:
> > On 12/10/06, Linus Torvalds <torvalds@osdl.org> wrote:
> >>
> >> Why don't you use the pipe and standard read()?
> >>
> >> Even if you use "popen()" and get a "FILE *" back, you can still do
> >>
> >>         int fd = fileno(file);
> >>
> >> and use the raw IO capabilities.
> >>
> >> The thing is, temporary files can actually be faster under Linux just
> >> because the Linux page-cache simply kicks ass. But it's not going to be
> >> _that_ big of a difference, and you need all that crazy "wait for
> >> rev-list
> >> to finish" and the "clean up temp-file on errors" etc crap, so there's no
> >> way it's a better solution.
> >>
> >
> > Two things.
> >
> > - memory use: the next natural step with files is, instead of loading
> > the file content in memory and *keep it there*, we could load one
> > chunk at a time, index the chunk and discard. At the end we keep in
> > memory only indexing info to quickly get to the data when needed, but
> > the big part of data stay on the file.
> >
>
> memory usage vs speed tradeoff. Since qgit is a pure user-app, I think
> it's safe to opt for the memory hungry option. If people run it on too
> lowbie hardware they'll just have to make do with other ways of viewing
> the DAG or shutting down some other programs.
>
> > - This is probably my ignorance, but experimenting with popen() I
> > found I could not know *when* git-rev-list ends because both feof()
> > and ferror() give 0 after a fread() with git-rev-list already defunct.
> > Not having a reference to the process (it is hidden behind popen() ),
> > I had to check for 0 bytes read after a successful read (to avoid
> > racing in case I ask the pipe before the first data it's ready) to
> > know that job is finished and call pclose().
> >
>
> (coding in MUA, so highly untested)
>

Thanks Andreas, I will do some tests with your code. But at first
sight I fail to see (I'm not an expert on this tough ;-)  ) where is
the difference from using popen() and fileno() to get the file
descriptors.

Thanks

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC \ WISH] Add -o option to git-rev-list
  2006-12-11 12:52                     ` Marco Costalba
@ 2006-12-11 13:28                       ` Josef Weidendorfer
  2006-12-11 17:28                         ` Marco Costalba
  0 siblings, 1 reply; 39+ messages in thread
From: Josef Weidendorfer @ 2006-12-11 13:28 UTC (permalink / raw)
  To: Marco Costalba
  Cc: Linus Torvalds, Git Mailing List, Junio C Hamano, Alex Riesen,
	Shawn Pearce

On Monday 11 December 2006 13:52, Marco Costalba wrote:
> On 12/11/06, Josef Weidendorfer <Josef.Weidendorfer@gmx.de> wrote:
> > On Monday 11 December 2006 01:15, Marco Costalba wrote:
> > >               guiUpdateTimer.start(100, true);
> >
> > What is the result with "guiUpdateTimer.start(0, true);" ?
> ...
> IOW you suggest to use a brute force polling of the pipe.

Ah, yes. That is probably not what you want.

Why did you introduce the timer at all? What was the problem
with QProcess and handling its signal "data available" ?

If you do

	git-rev-list | cat > /dev/null

the consuming "cat" will do a blocking read, and this should have
the same property "handle data as fast as possible when they
come available" as QProcess with handling the data in
the handler of QProcess' signal "data available".

If it is about postprocessing the small data chunks you get (and the
double copy in memory), I do not think this is a problem: a machine
noadays usually should handle 1 GB/s bandwidth to memory. You probably
are loosing more with your 100ms timer.

Or am I missing something?


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC \ WISH] Add -o option to git-rev-list
  2006-12-11 12:59       ` Marco Costalba
@ 2006-12-11 13:40         ` Andreas Ericsson
  0 siblings, 0 replies; 39+ messages in thread
From: Andreas Ericsson @ 2006-12-11 13:40 UTC (permalink / raw)
  To: Marco Costalba
  Cc: Linus Torvalds, Git Mailing List, Junio C Hamano, Alex Riesen,
	Shawn Pearce

Marco Costalba wrote:
> On 12/11/06, Andreas Ericsson <ae@op5.se> wrote:
>> Marco Costalba wrote:
>> > On 12/10/06, Linus Torvalds <torvalds@osdl.org> wrote:
>> >>
>> >> Why don't you use the pipe and standard read()?
>> >>
>> >> Even if you use "popen()" and get a "FILE *" back, you can still do
>> >>
>> >>         int fd = fileno(file);
>> >>
>> >> and use the raw IO capabilities.
>> >>
>> >> The thing is, temporary files can actually be faster under Linux just
>> >> because the Linux page-cache simply kicks ass. But it's not going 
>> to be
>> >> _that_ big of a difference, and you need all that crazy "wait for
>> >> rev-list
>> >> to finish" and the "clean up temp-file on errors" etc crap, so 
>> there's no
>> >> way it's a better solution.
>> >>
>> >
>> > Two things.
>> >
>> > - memory use: the next natural step with files is, instead of loading
>> > the file content in memory and *keep it there*, we could load one
>> > chunk at a time, index the chunk and discard. At the end we keep in
>> > memory only indexing info to quickly get to the data when needed, but
>> > the big part of data stay on the file.
>> >
>>
>> memory usage vs speed tradeoff. Since qgit is a pure user-app, I think
>> it's safe to opt for the memory hungry option. If people run it on too
>> lowbie hardware they'll just have to make do with other ways of viewing
>> the DAG or shutting down some other programs.
>>
>> > - This is probably my ignorance, but experimenting with popen() I
>> > found I could not know *when* git-rev-list ends because both feof()
>> > and ferror() give 0 after a fread() with git-rev-list already defunct.
>> > Not having a reference to the process (it is hidden behind popen() ),
>> > I had to check for 0 bytes read after a successful read (to avoid
>> > racing in case I ask the pipe before the first data it's ready) to
>> > know that job is finished and call pclose().
>> >
>>
>> (coding in MUA, so highly untested)
>>
> 
> Thanks Andreas, I will do some tests with your code. But at first
> sight I fail to see (I'm not an expert on this tough ;-)  ) where is
> the difference from using popen() and fileno() to get the file
> descriptors.
> 

read() vs fread(), so no libc buffers. When I did comparisons with this 
(a long time ago, I don't have the test-program around) in style of

	read(out[0], buf, sizeof(buf));
	write(fileno(stdout), buf, sizeof(buf));

with a command line like this;

	cat any-file | test-program > /dev/null

I saw a static ~10ms increase in execution time compared to

	cat any-file > /dev/null

regardless of the size of "any-file", so I assume this overhead comes 
from the extra fork(), which you'll never get rid of unless you use 
libgit.a.

-- 
Andreas Ericsson                   andreas.ericsson@op5.se
OP5 AB                             www.op5.se

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC \ WISH] Add -o option to git-rev-list
  2006-12-11  7:17                     ` Marco Costalba
  2006-12-11 10:00                       ` Alex Riesen
@ 2006-12-11 16:59                       ` Linus Torvalds
  2006-12-11 17:07                         ` Linus Torvalds
  1 sibling, 1 reply; 39+ messages in thread
From: Linus Torvalds @ 2006-12-11 16:59 UTC (permalink / raw)
  To: Marco Costalba
  Cc: Git Mailing List, Junio C Hamano, Alex Riesen, Shawn Pearce



On Mon, 11 Dec 2006, Marco Costalba wrote:
> 
> These are tipical values (warm cache):
> 
> $ time git rev-list --header --boundary --parents --topo-order HEAD | cat > /dev/null
> 3.67user 0.36system 0:04.29elapsed 93%CPU (0avgtext+0avgdata 0maxresident)k
> 0inputs+0outputs (0major+18033minor)pagefaults 0swaps

That's not timing what I asked. That's just timing the "git-rev-list". You 
need to time the "cat" part too. Either use a script, or do something like 

	time sh -c "git-rev-list ... | cat > /dev/null".

> $ time git rev-list --header --boundary --parents --topo-order HEAD >
> /tmp/tmp.txt; cat /tmp/tmp.txt > /dev/null
> 3.44user 0.28system 0:03.74elapsed 99%CPU (0avgtext+0avgdata 0maxresident)k
> 0inputs+0outputs (0major+18033minor)pagefaults 0swaps

Again - you're only timing the _writer_, not the "cat" at all.

> For some reason the CPU *never* goes up 93% with pipe (while it's easy
> in the other two cases)

That's because you're only timing 93% of the work (ignoring the "cat" 
part), and in the other cases you're ignoring the "cat" (that happens 
_afterwards_, not concurrently) entirely.

> OK. I just don't understand how after waiting 100ms I get only 60KB
> and stay in the loop only one cycle instead of reading,


That's because the _writer_ will block. It notices that the reader cannot
read ass fast as it can write, so it blocks when its buffers fill up.

If you want to be fast, you need to be fast at reading. It's that simple.


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC \ WISH] Add -o option to git-rev-list
  2006-12-11 16:59                       ` Linus Torvalds
@ 2006-12-11 17:07                         ` Linus Torvalds
  2006-12-11 17:39                           ` Marco Costalba
  0 siblings, 1 reply; 39+ messages in thread
From: Linus Torvalds @ 2006-12-11 17:07 UTC (permalink / raw)
  To: Marco Costalba
  Cc: Git Mailing List, Junio C Hamano, Alex Riesen, Shawn Pearce



On Mon, 11 Dec 2006, Linus Torvalds wrote:
> 
> That's not timing what I asked. That's just timing the "git-rev-list". You 
> need to time the "cat" part too. Either use a script, or do something like 
> 
> 	time sh -c "git-rev-list ... | cat > /dev/null".

Btw, if you do this, you'll actually see one of the _real_ advantages of 
pipes.

On an SMP system, you'll easily get output like "110%CPU", since now the 
git-rev-list and the user can run in parallel. Of course, they can do so 
with temp-files too, but then you have all the "is it a true EOF, or is it 
just the fact that the writer hasn't written everything yet" problem (and 
the _normal_ solution for that is to simply not allow any overlapping 
work).


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC \ WISH] Add -o option to git-rev-list
  2006-12-11 13:28                       ` Josef Weidendorfer
@ 2006-12-11 17:28                         ` Marco Costalba
  0 siblings, 0 replies; 39+ messages in thread
From: Marco Costalba @ 2006-12-11 17:28 UTC (permalink / raw)
  To: Josef Weidendorfer
  Cc: Linus Torvalds, Git Mailing List, Junio C Hamano, Alex Riesen,
	Shawn Pearce

On 12/11/06, Josef Weidendorfer <Josef.Weidendorfer@gmx.de> wrote:
> On Monday 11 December 2006 13:52, Marco Costalba wrote:
> > On 12/11/06, Josef Weidendorfer <Josef.Weidendorfer@gmx.de> wrote:
> > > On Monday 11 December 2006 01:15, Marco Costalba wrote:
> > > >               guiUpdateTimer.start(100, true);
> > >
> > > What is the result with "guiUpdateTimer.start(0, true);" ?
> > ...
> > IOW you suggest to use a brute force polling of the pipe.
>
> Ah, yes. That is probably not what you want.
>
> Why did you introduce the timer at all? What was the problem
> with QProcess and handling its signal "data available" ?
>

I did. Actually the released 1.5.3 version and all the verions before
that do use QProcess, but I experimented an increase in performance
reading the data  on a timer instead of on "data available" signal.
BTW that observation led to all this stuff ;-)

The problem is that the frequency of the signal is very very high
because the producer can write a big amount of data with great speed.
We are talking of a signal "data available" each few ms.

By the way of experimenting with QProcess I have found it's better a
timer when the stream is very very fast, _then_ I have update the
reading code to take _more_ adavntage of the big chunks we were
reading with a low frequency timer and avoid a double copy, but the
increase was _already_ present also with the double copy needed by the
original 'small chunks handling' code activated by "data available"
signal.

If you take a look at released qgit you will see, in dataloader.cpp
the reading slot called on the signal.


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC \ WISH] Add -o option to git-rev-list
  2006-12-11 17:07                         ` Linus Torvalds
@ 2006-12-11 17:39                           ` Marco Costalba
  2006-12-11 18:15                             ` Linus Torvalds
  0 siblings, 1 reply; 39+ messages in thread
From: Marco Costalba @ 2006-12-11 17:39 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Git Mailing List, Junio C Hamano, Alex Riesen, Shawn Pearce

On 12/11/06, Linus Torvalds <torvalds@osdl.org> wrote:
>
>
> On Mon, 11 Dec 2006, Linus Torvalds wrote:
> >
> > That's not timing what I asked. That's just timing the "git-rev-list". You
> > need to time the "cat" part too. Either use a script, or do something like
> >
> >       time sh -c "git-rev-list ... | cat > /dev/null".
>
> Btw, if you do this, you'll actually see one of the _real_ advantages of
> pipes.
>
> On an SMP system, you'll easily get output like "110%CPU", since now the
> git-rev-list and the user can run in parallel. Of course, they can do so
> with temp-files too, but then you have all the "is it a true EOF, or is it
> just the fact that the writer hasn't written everything yet" problem (and
> the _normal_ solution for that is to simply not allow any overlapping
> work).
>

Sorry for wrong data, I will repost as soon as I can.

Regarding the _normal_  solution we have one more hipotesys to take
advantage of: git-rev-list when has nothing more to read..exits.

This is what lead to the exit loop condition based on git-rev-list is
still alive, instead of messing with an EOF.

     eof  = (no more data && !rev_list_is_running);

With this we could enter the loop one more time then necessary, but
nothing else bad should happen.


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC \ WISH] Add -o option to git-rev-list
  2006-12-11 17:39                           ` Marco Costalba
@ 2006-12-11 18:15                             ` Linus Torvalds
  2006-12-11 18:59                               ` Marco Costalba
  0 siblings, 1 reply; 39+ messages in thread
From: Linus Torvalds @ 2006-12-11 18:15 UTC (permalink / raw)
  To: Marco Costalba
  Cc: Git Mailing List, Junio C Hamano, Alex Riesen, Shawn Pearce



On Mon, 11 Dec 2006, Marco Costalba wrote:
> 
> Regarding the _normal_  solution we have one more hipotesys to take
> advantage of: git-rev-list when has nothing more to read..exits.

Yes. You can just wait for the child exit signal.

However, you seem to continually ignore the thing I've asked you to do 
several times: try with a cold-cache situation.

The thing is, using pipes and poll()/select()/epoll()/whatever will not 
only be efficient, but it will also WORK CORRECTLY in the presense of a 
writer that is _slower_ than the reader.

Right now, you're testing the exact opposite. You're testing the case 
where the reader is slower than the writer, which is actually not that 
interesting - because if "git-rev-list" is instantaneous, then the fastest 
thing to do is to simply _always_ just read the result buffer directly 
into memory without any select() loop etc what-so-ever.

So just by testing that case (and you've slowed down the reader 
artificially even _apart_ from the fact that qgit will probably always be 
slower than git-rev-list off a packed and hot-cache environment), you're 
always going to skew your results into the "do everything in one go" 
direction.

But the point of the pipe and the poll() is that it works nicely even when 
the data trickles in slowly.

[ Of course, as long as you ask for "--topo-order", you'll never see a lot 
  of trickling, and you'll never be able to do really well for the 
  cold-cache case. To see the _real_ advantage of pipes, you should void 
  "--topo-order" entirely, and do it dynamically within qgit, repainting 
  the graph as needed.

  At that point, you'd actually do something that gitk can't do at all, 
  namely work well for the cold-cache not-very-packed large-repository 
  case. ]

To see this in practice (even with hot-caches), do something like the 
following on the full historic Linux archive:

	time sh -c "git rev-list HEAD | head"
	time sh -c "git rev-list --topo-order HEAD | head"

where for me, the firstone takes 0.002s, and the second one takes 0.878s.

Now THAT is an optimization. Not just "10%" or even "ten times", but "four 
HUNDRED times" faster.

And why is that? Simply because it only needed to look at _part_ of the 
data. For the exact same reason, if you were to do the topological sort 
only on the part that you had _looked_ at first, you'd be able to do these 
kinds of several-orders-of-magnitude improvements.


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC \ WISH] Add -o option to git-rev-list
  2006-12-11 18:15                             ` Linus Torvalds
@ 2006-12-11 18:59                               ` Marco Costalba
  2006-12-11 19:25                                 ` Linus Torvalds
  2006-12-11 20:28                                 ` Josef Weidendorfer
  0 siblings, 2 replies; 39+ messages in thread
From: Marco Costalba @ 2006-12-11 18:59 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Git Mailing List, Junio C Hamano, Alex Riesen, Shawn Pearce

On 12/11/06, Linus Torvalds <torvalds@osdl.org> wrote:
>
>
>
> However, you seem to continually ignore the thing I've asked you to do
> several times: try with a cold-cache situation.

Yes. I will test it. I was testing with warm cache also to my
curiosity about comparing pipes with temporary file reading as data
exchange facility. So I needed to avoid HD artifacts.

>
>
>   At that point, you'd actually do something that gitk can't do at all,
>   namely work well for the cold-cache not-very-packed large-repository
>   case. ]
>

Removing --topo-order from launch parameters is very simple, see from
gitstartup.cpp:

bool Git::startRevList(SCRef args, FileHistory* fh) {

	QString initCmd("git rev-list --header --boundary --parents ");
	if (!isMainHistory(fh)) {
		// fetch history from all branches so any revision in
		// main view that changes the file is always found
		SCRef allBranches = getAllRefSha(BRANCH).join(" ");
		initCmd.append("--remove-empty " + allBranches + " -- ");
	} else
		initCmd.append("--topo-order ");

	return startParseProc(initCmd + args, fh);
}

It is already removed from file history fetching because there the
history it's _almost_ linear and you gain the speed without noticing
the different graph layout. But for main view I fear that complicated
branch structures could became difficult to follow without
--topo-order. Perhaps an option in settings?

> To see this in practice (even with hot-caches), do something like the
> following on the full historic Linux archive:
>
>         time sh -c "git rev-list HEAD | head"
>         time sh -c "git rev-list --topo-order HEAD | head"
>
> where for me, the firstone takes 0.002s, and the second one takes 0.878s.
>

This is  the data:

$ pwd
/git/linux-2.6
bash-3.1$ time sh -c "git rev-list HEAD | head"
200d018eff4be3a1fb9823441cfcebb7de86a677
f0647a52974daccbe20990fb6341f07792445fe0
c25c79d80e02db1bd993426f979c5f1b42a0f132
9fd32cfbb602f3e5e898faa61d83c4a7897bd48a
ed99e2bc1dc5dc54eb5a019f4975562dbef20103
773ff78838ca3c07245e45c06235e0baaa5f710a
52ffe760ea9ec407292d093c3f06c1cda5187228
14b36af46a1d3652aff6734ea24816995dff8123
eb991b39385c7b04923d701764a34694ec54b53d
88032b322a38b37335c8cb2e3473a45c81d280eb
0.08user 0.02system 0:00.10elapsed 100%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+3631minor)pagefaults 0swaps

bash-3.1$  time sh -c "git rev-list --topo-order HEAD | head"
200d018eff4be3a1fb9823441cfcebb7de86a677
f0647a52974daccbe20990fb6341f07792445fe0
c25c79d80e02db1bd993426f979c5f1b42a0f132
9fd32cfbb602f3e5e898faa61d83c4a7897bd48a
ed99e2bc1dc5dc54eb5a019f4975562dbef20103
773ff78838ca3c07245e45c06235e0baaa5f710a
52ffe760ea9ec407292d093c3f06c1cda5187228
14b36af46a1d3652aff6734ea24816995dff8123
eb991b39385c7b04923d701764a34694ec54b53d
33ec32fae0e2c4433bfd1e74cbde6cb16604a719
2.84user 0.12system 0:03.05elapsed 97%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+12086minor)pagefaults 0swaps

Also the long awaited ;-)

$ time sh -c "git rev-list --header --boundary --parents --topo-order
HEAD | cat > /dev/null"
3.75user 0.55system 0:04.42elapsed 97%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+18822minor)pagefaults 0swaps

$ time sh -c "git rev-list --header --boundary --parents --topo-order
HEAD > /tmp/tmp.txt; cat /tmp/tmp.txt > /dev/null"
3.50user 0.30system 0:03.92elapsed 97%CPU (0avgtext+0avgdata 0maxresident)k
0inputs+0outputs (0major+18800minor)pagefaults 0swaps



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC \ WISH] Add -o option to git-rev-list
  2006-12-11 18:59                               ` Marco Costalba
@ 2006-12-11 19:25                                 ` Linus Torvalds
  2006-12-11 20:28                                 ` Josef Weidendorfer
  1 sibling, 0 replies; 39+ messages in thread
From: Linus Torvalds @ 2006-12-11 19:25 UTC (permalink / raw)
  To: Marco Costalba
  Cc: Git Mailing List, Junio C Hamano, Alex Riesen, Shawn Pearce



On Mon, 11 Dec 2006, Marco Costalba wrote:
> 
> $ time sh -c "git rev-list --header --boundary --parents --topo-order HEAD | cat > /dev/null"
> $ time sh -c "git rev-list --header --boundary --parents --topo-order HEAD > /tmp/tmp.txt; cat /tmp/tmp.txt > /dev/null"

Btw, for me the numbers aren't stable enough to be al lthat final, but 
doing it multiple times on the kernel archive I get:

	real    0m0.947s
	real    0m1.046s
	real    0m0.943s
	real    0m0.868s

for the pipe case, and

	real    0m0.953s
	real    0m1.056s
	real    0m1.060s
	real    0m0.968s

for the temp-file. IOW, no real difference. Just doing the "> /dev/null" 
directly is more consistent (no task-switching, SMP effects, or cache 
conflicts) at

	real    0m0.806s
	real    0m0.806s
	real    0m0.805s
	real    0m0.808s

but one recurrent theme is definitely true: the "cat" part is a noticeable 
overhead. But that in itself means that git-rev-list is just so efficient 
that even just basically copying its output is going to be measurable. 

Which is obviously a good thing, but it means that it's not very 
interesting in the big picture (ie I bet the real overheads are going to 
be elsewhere if you actually expect to draw anything on the screen).


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC \ WISH] Add -o option to git-rev-list
  2006-12-11 18:59                               ` Marco Costalba
  2006-12-11 19:25                                 ` Linus Torvalds
@ 2006-12-11 20:28                                 ` Josef Weidendorfer
  2006-12-11 20:40                                   ` Linus Torvalds
  1 sibling, 1 reply; 39+ messages in thread
From: Josef Weidendorfer @ 2006-12-11 20:28 UTC (permalink / raw)
  To: Marco Costalba
  Cc: Linus Torvalds, Git Mailing List, Junio C Hamano, Alex Riesen,
	Shawn Pearce

On Monday 11 December 2006 19:59, Marco Costalba wrote:
> On 12/11/06, Linus Torvalds <torvalds@osdl.org> wrote:
> >
> >
> >
> > However, you seem to continually ignore the thing I've asked you to do
> > several times: try with a cold-cache situation.
> 
> Yes. I will test it. I was testing with warm cache also to my
> curiosity about comparing pipes with temporary file reading as data
> exchange facility. So I needed to avoid HD artifacts.

Hi,

I just looked at the QProcess implementation in Qt 3.3.6, as I was
curious.

Qt does a big select() call in the event loop. If there is data
available from the child process, it is reading in chunks of a
maximum of 4096 bytes, with a select() call inbetween to see if
there is still data available. After every read, the read data
is concatenated into the read buffer.

For the slow/cold cache case, this probably is the best *if* the
consumer application can act as fast as possible when data is
sent. Which makes it a good fit to avoid --topo-order and do
some redrawing of the graph yourself if needed. For sure, this
gives the fastest visual appeareance. You could start filling
the list after e.g. 30 revs are read.

Obviously, there is some possibility for improvement _when_ you
know that you want to read in large amounts of data in big
chunks, given that QProcess uses at least two system calls every
4 kB.

A general question: How many context switches are involved in such
a producer/consumer scenario, given that the consumer writes one
line at a time, and the consumer uses poll/select to wait for the
data?
Is there some possibility to make the kernel write-combine single
small producer writes into bigger chunks, which will be delivered
at once (or smaller data only after a small timeout)?


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC \ WISH] Add -o option to git-rev-list
  2006-12-11 20:28                                 ` Josef Weidendorfer
@ 2006-12-11 20:40                                   ` Linus Torvalds
  2006-12-11 20:54                                     ` Josef Weidendorfer
  0 siblings, 1 reply; 39+ messages in thread
From: Linus Torvalds @ 2006-12-11 20:40 UTC (permalink / raw)
  To: Josef Weidendorfer
  Cc: Marco Costalba, Git Mailing List, Junio C Hamano, Alex Riesen,
	Shawn Pearce



On Mon, 11 Dec 2006, Josef Weidendorfer wrote:
> 
> A general question: How many context switches are involved in such
> a producer/consumer scenario, given that the consumer writes one
> line at a time, and the consumer uses poll/select to wait for the
> data?
> Is there some possibility to make the kernel write-combine single
> small producer writes into bigger chunks, which will be delivered
> at once (or smaller data only after a small timeout)?

The data will be write-combined.

The kernel doesn't context-switch after a write() to a pipe if there is 
space left (and usually the pipe buffer is 64kB with current kernels, so 
there obviously will be), unless the reader has a higher priority for some 
reason (ie the reader has been waiting a long time).

So _normally_ you'll see many many writes in one go, and only see context 
switching when the kernel pipe buffer fills up.


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC \ WISH] Add -o option to git-rev-list
  2006-12-11 20:40                                   ` Linus Torvalds
@ 2006-12-11 20:54                                     ` Josef Weidendorfer
  2006-12-11 21:14                                       ` Linus Torvalds
  0 siblings, 1 reply; 39+ messages in thread
From: Josef Weidendorfer @ 2006-12-11 20:54 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Marco Costalba, Git Mailing List, Junio C Hamano, Alex Riesen,
	Shawn Pearce

On Monday 11 December 2006 21:40, Linus Torvalds wrote:
> On Mon, 11 Dec 2006, Josef Weidendorfer wrote:
> > A general question: How many context switches are involved in such
> > a producer/consumer scenario, given that the consumer writes one
> > line at a time, and the consumer uses poll/select to wait for the
> > data?
> > Is there some possibility to make the kernel write-combine single
> > small producer writes into bigger chunks, which will be delivered
> > at once (or smaller data only after a small timeout)?
> 
> The data will be write-combined.
> 
> The kernel doesn't context-switch after a write() to a pipe if there is 
> space left (and usually the pipe buffer is 64kB with current kernels, so 
> there obviously will be), unless the reader has a higher priority for some 
> reason (ie the reader has been waiting a long time).

Ah, thanks.
So the implementation in Qt's QProcess is a little bit pessimistic, probably
to make it work fine with other UNIXs out there.


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC \ WISH] Add -o option to git-rev-list
  2006-12-11 20:54                                     ` Josef Weidendorfer
@ 2006-12-11 21:14                                       ` Linus Torvalds
  2006-12-15 18:45                                         ` Marco Costalba
  0 siblings, 1 reply; 39+ messages in thread
From: Linus Torvalds @ 2006-12-11 21:14 UTC (permalink / raw)
  To: Josef Weidendorfer
  Cc: Marco Costalba, Git Mailing List, Junio C Hamano, Alex Riesen,
	Shawn Pearce



On Mon, 11 Dec 2006, Josef Weidendorfer wrote:
> 
> So the implementation in Qt's QProcess is a little bit pessimistic, probably
> to make it work fine with other UNIXs out there.

I suspect it may actually have been performance-optimized for Linux-2.4, 
where the pipe buffer size was one page (so if you got a full 4kB on x86, 
you'd know that the buffer was fully emptied).

And it actually does end up depending on the load too. For example, the 
4kB can also happen to match the default buffer size in stdio (or in the 
QT IO buffering equivalent), and in that case you know that any _writer_ 
that uses stdio buffering will usually use that as the blocksize, so you 
have reasons beyond the kernel to think that maybe a certain particular 
blocksize is more common than others.

And if that's the case, then if you know that _usually_ the read() would 
return -EAGAIN, then it's actually better to return to the main loop and 
select(), on the assumption that quite often, the select will be the right 
thing (and if it isn't, it will just return immediately anyway, and you 
can do the second read).

In other words, it's really a "tuning thing" that depends on the IO 
patterns of the apps involved, and the underlying pattern of the OS can 
affect it too.

Of course, most of the time it doesn't make _that_ much of a difference, 
especially when system calls are reasonably cheap (and they largely are, 
under Linux). So you can certainly see the effects of this, but the 
effects will vary so much on the load and other issues that it's hard to 
say that there is "one correct way" to do things.

For example, very subtle issues can be at play:

 - using a larger buffer and having fewer system calls can be 
   advantageous in many cases. But certainly not always.

   HOWEVER. If you have two processes that both do very little with the 
   data (generating it _or_ using it), and process switching is cheap 
   thanks to address space identifiers or clever TLB's, then a small 
   buffer may actually be much better, if it means that the whole working 
   set fits in the L2 cache.

   So a large buffer with fewer system calls may actually be _bad_, if 
   only because it causes things to go to L3 or memory. In contrast, if 
   you can re-use a smaller buffer, you'll stay in the L2 cache better. 

   Same goes for the L1 cache too, but that's really only noticeable for 
   true micro-benchmarks. Sadly, people often do performance analysis with 
   microbenches, so they can actually tune their setup for something that 
   is NOT what people actually see in real life.

   In general, if you re-use your buffer, try to keep it small enough that 
   it stays hot in the L2 can be a good thing. Using 8kB or 16kB buffers 
   and 64-128 system calls can actually be a lot BETTER than using a 1MB 
   buffer and a single system call.

   Examples of this is stdio. Making your stdio buffers bigger simply 
   often isn't a good idea. The system call cost of flushing the buffer 
   simply isn't likely to be the highest cost.

 - real-time or fairness issues. Sometimes you are better off doing a 
   round-robin thing in the main select() loop over 10 smaller buffers 
   than over ten big buffers (or over a single mainloop call-out that 
   keeps doing read() until the buffer is entirely empty). 

   This is not likely to be an issue with qgit-like behaviour (whether you 
   process a 4kB buffer or 64kB buffer in one go is simply not going to 
   make a visible difference to a user interface), but it can make a 
   difference for "server" apps with latency issues. You do not want one 
   busy client to basically freeze out all the other clients by just 
   keeping its queue filled all teh time.

   So returning to the main loop may actually be the right thing after 
   each read(), not for performance reasons, but because it gives a nicer 
   load pattern. It may perform _worse_, but it may well give a smoother 
   feel to the system!

 - simplicity. The main select() loop does the looping anyway, so avoiding 
   looping in the callouts simplifies the system. And since it's not 
   necessarily the right thing to do to loop in the individual routines 
   _anyway_ (see above), why not?

So I don't think there is a right answer here. I suspect QProcess does ok.


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC \ WISH] Add -o option to git-rev-list
  2006-12-11 21:14                                       ` Linus Torvalds
@ 2006-12-15 18:45                                         ` Marco Costalba
  2006-12-15 19:20                                           ` Linus Torvalds
  0 siblings, 1 reply; 39+ messages in thread
From: Marco Costalba @ 2006-12-15 18:45 UTC (permalink / raw)
  To: Git Mailing List
  Cc: Josef Weidendorfer, Junio C Hamano, Alex Riesen, Shawn Pearce,
	Linus Torvalds

On 12/11/06, Linus Torvalds <torvalds@osdl.org> wrote:
>
>
> So I don't think there is a right answer here. I suspect QProcess does ok.
>

I have just pushed a patch (wiil be available in few hours I suspect)
to easily choose the data exchange facility with git-rev-list.

Default its temporary file based, but after uncommenting the
USE_QPROCESS define in dataloader.cpp and compile again, qgit will use
a QProcess based approach to connect with git-rev-list.

Only the low level connection is different in the two cases, _all_ the
rest, expecially data storing and parsing, is the same. If you take a
look at src/dataloader.cpp you will see that the difference between
QProcess and temp file code is minumum.

I've also tried to clearly report in comments the different approaches
and the data copy involved in both cases. I've searched a little bit
in QProcess sources too to get some additional info.


And finally these are mine warm and cold cache tests.

Load time test on Linux tree (44557 revs, 32167KB)
CPU Mobile Pentium 4 set at 1.2GHZ

To set data read interval in ms use (src/dataloader.cpp):
#define GUI_UPDATE_INTERVAL 500

Warmed-up cache
QProcess 7632ms (500ms data read interval)
QProcess 7972ms (100ms data read interval)

Temporary file 4408ms (500ms data read interval)
Temporary file 4591ms (100ms data read interval)


Cold cache
QProcess 25611ms (500ms data read interval)

File 22399ms (500ms data read interval)



^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC \ WISH] Add -o option to git-rev-list
  2006-12-15 18:45                                         ` Marco Costalba
@ 2006-12-15 19:20                                           ` Linus Torvalds
  2006-12-15 20:41                                             ` Marco Costalba
  0 siblings, 1 reply; 39+ messages in thread
From: Linus Torvalds @ 2006-12-15 19:20 UTC (permalink / raw)
  To: Marco Costalba
  Cc: Git Mailing List, Josef Weidendorfer, Junio C Hamano, Alex Riesen,
	Shawn Pearce



On Fri, 15 Dec 2006, Marco Costalba wrote:
> 
> Warmed-up cache
> QProcess 7632ms (500ms data read interval)
> QProcess 7972ms (100ms data read interval)

Why do you even bother posting numbers, when multiple people have told you 
that the numbers you post are meaningless?

As long as you throttle the writer by not reading data in a timely fashion 
(by using poll() or select() in the main loop and reading when it's 
available), and you continue to talk about "data read intervals", all your 
numbers are CRAP.

OF COURSE a temp-file will work better, because then you basically have an 
infinite buffer, and data read intervals don't _matter_. But we've tried 
to tell you that this is not because temp-files are better, but because 
your reader artificially throttles the writer for a pipe.


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC \ WISH] Add -o option to git-rev-list
  2006-12-15 19:20                                           ` Linus Torvalds
@ 2006-12-15 20:41                                             ` Marco Costalba
  2006-12-15 21:04                                               ` Marco Costalba
  0 siblings, 1 reply; 39+ messages in thread
From: Marco Costalba @ 2006-12-15 20:41 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Git Mailing List, Josef Weidendorfer, Junio C Hamano, Alex Riesen,
	Shawn Pearce

On 12/15/06, Linus Torvalds <torvalds@osdl.org> wrote:
>
>
> On Fri, 15 Dec 2006, Marco Costalba wrote:
> >
> > Warmed-up cache
> > QProcess 7632ms (500ms data read interval)
> > QProcess 7972ms (100ms data read interval)
>
> Why do you even bother posting numbers, when multiple people have told you
> that the numbers you post are meaningless?
>
> As long as you throttle the writer by not reading data in a timely fashion
> (by using poll() or select() in the main loop and reading when it's
> available), and you continue to talk about "data read intervals", all your
> numbers are CRAP.
>

QProcess implementation is like this FWIK (from
qt-x11-free-3.3.4/src/kernel/qprocess_unix.cpp):

The SIGCHLD handler writes to a socket to tell the manager that
something happened. Then in  QProcessManager::sigchldHnd() data is read by

process->socketRead( proc->socketStdout );


In QProcess::socketRead() a call to C function read() is done to get
4KB of data:

const int basize = 4096;
QByteArray *ba = new QByteArray( basize );
n = ::read( fd, ba->data(), basize );


The pointer ba is then appended to a pointer list.

This happens _ALWAYS_ indipendently of _how_ the application calls the
Qt library for reading.

When application read recieved data with QProcess::readStdout(), then
data stored in buffers pointed by the pointer list is memcpy() to a
temporary array that is the return value of QProcess::readStdout().
See in qt-x11-free-3.3.4/src/kernel/qprocess.cpp:

QByteArray QProcess::readStdout()
{
    if ( readStdoutCalled ) {
	return QByteArray();
    }
    readStdoutCalled = TRUE;
    QMembuf *buf = membufStdout();
    readStdoutCalled = FALSE;

    return buf->readAll();  // here a memcpy() is involved
}


So it' true that we use a timeout, but only to trigger a memcpy() from
Qt internal library buffers that, instead are feeded as soon as
possible by Qt implementation.

IOW the timely select() is already done by Qt library. We just read
what has been already received and stored.

   Marco


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [RFC \ WISH] Add -o option to git-rev-list
  2006-12-15 20:41                                             ` Marco Costalba
@ 2006-12-15 21:04                                               ` Marco Costalba
  0 siblings, 0 replies; 39+ messages in thread
From: Marco Costalba @ 2006-12-15 21:04 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Git Mailing List, Josef Weidendorfer, Junio C Hamano, Alex Riesen,
	Shawn Pearce

On 12/15/06, Marco Costalba <mcostalba@gmail.com> wrote:
>
> IOW the timely select() is already done by Qt library. We just read
> what has been already received and stored.
>

Try to set the timer to a very high value so that never triggers and
there is only one read call at the end, after git-rev-list exited,
when there is a forced call to the data read function.

Times seems to get even better! This is because timely read of the
pipe is _already_ done inside Qt library, not by qgit application.


^ permalink raw reply	[flat|nested] 39+ messages in thread

end of thread, other threads:[~2006-12-15 21:04 UTC | newest]

Thread overview: 39+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-12-10 11:38 [RFC \ WISH] Add -o option to git-rev-list Marco Costalba
2006-12-10 14:54 ` Alex Riesen
2006-12-10 18:16 ` Linus Torvalds
2006-12-10 19:51   ` Marco Costalba
2006-12-10 20:00     ` globs in partial checkout? Michael S. Tsirkin
2006-12-10 20:13       ` Linus Torvalds
2006-12-10 21:07         ` Michael S. Tsirkin
2006-12-10 20:08     ` [RFC \ WISH] Add -o option to git-rev-list Linus Torvalds
2006-12-10 20:19       ` Linus Torvalds
2006-12-10 22:05         ` Marco Costalba
2006-12-10 22:09           ` Marco Costalba
2006-12-10 22:16           ` Linus Torvalds
2006-12-10 22:35             ` Marco Costalba
2006-12-10 22:53               ` Linus Torvalds
2006-12-11  0:15                 ` Marco Costalba
2006-12-11  0:51                   ` Linus Torvalds
2006-12-11  7:17                     ` Marco Costalba
2006-12-11 10:00                       ` Alex Riesen
2006-12-11 16:59                       ` Linus Torvalds
2006-12-11 17:07                         ` Linus Torvalds
2006-12-11 17:39                           ` Marco Costalba
2006-12-11 18:15                             ` Linus Torvalds
2006-12-11 18:59                               ` Marco Costalba
2006-12-11 19:25                                 ` Linus Torvalds
2006-12-11 20:28                                 ` Josef Weidendorfer
2006-12-11 20:40                                   ` Linus Torvalds
2006-12-11 20:54                                     ` Josef Weidendorfer
2006-12-11 21:14                                       ` Linus Torvalds
2006-12-15 18:45                                         ` Marco Costalba
2006-12-15 19:20                                           ` Linus Torvalds
2006-12-15 20:41                                             ` Marco Costalba
2006-12-15 21:04                                               ` Marco Costalba
2006-12-11  9:26                   ` Josef Weidendorfer
2006-12-11 12:52                     ` Marco Costalba
2006-12-11 13:28                       ` Josef Weidendorfer
2006-12-11 17:28                         ` Marco Costalba
2006-12-11 11:39     ` Andreas Ericsson
2006-12-11 12:59       ` Marco Costalba
2006-12-11 13:40         ` Andreas Ericsson

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).