* [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).