Message ID | pull.914.v2.git.1616762291574.gitgitgadget@gmail.com (mailing list archive) |
---|---|
State | Accepted |
Commit | 701f5c0696a277cb5c8e8a84e1db8e392685418f |
Headers | show |
Series | [v2] csum-file: make hashwrite() more readable | expand |
"Derrick Stolee via GitGitGadget" <gitgitgadget@gmail.com> writes: > if (nr == sizeof(f->buffer)) { > - /* process full buffer directly without copy */ > - data = buf; > + /* > + * Flush a full batch worth of data directly > + * from the input, skipping the memcpy() to > + * the hashfile's buffer. In this block, > + * f->offset is necessarily zero. > + */ What made me a bit confused was the fact that, in order to exercise the "bypass memcpy and take a full bufferful from the incoming data directly" optimization, there are two preconditions. The incoming data must be large enough, and we do not have anything kept in the buffer that needs to be emitted before the incoming data. And the cleverness of the original code was that both are checked by this single "nr == sizeof(f->buffer)" condition. So I do appreciate this extra comment, and I think future readers of the code will, too. > + the_hash_algo->update_fn(&f->ctx, buf, nr); > + flush(f, buf, nr); > } else { > - memcpy(f->buffer + offset, buf, nr); > - data = f->buffer; > + /* > + * Copy to the hashfile's buffer, flushing only > + * if it became full. > + */ > + memcpy(f->buffer + f->offset, buf, nr); > + f->offset += nr; > + left -= nr; > + if (!left) > + hashflush(f); > } > > count -= nr; > - offset += nr; > buf = (char *) buf + nr; > - left -= nr; > - if (!left) { > - the_hash_algo->update_fn(&f->ctx, data, offset); > - flush(f, data, offset); > - offset = 0; > - } > - f->offset = offset; > } > } > > > base-commit: 142430338477d9d1bb25be66267225fb58498d92
On Fri, Mar 26, 2021 at 12:38:11PM +0000, Derrick Stolee via GitGitGadget wrote: > However, during this investigation, I inspected hashwrite() and > misunderstood it, even after looking closely and trying to make it > faster. This change simply reorganizes some parts of the loop within > hashwrite() to make it clear that each batch either uses memcpy() to the > hashfile's buffer or writes directly from the input buffer. The previous > code relied on indirection through local variables and essentially > inlined the implementation of hashflush() to reduce lines of code. Thanks, I think this nicely argues for the change here (and I agree the result, especially with the comments, is much easier to understand). -Peff
diff --git a/csum-file.c b/csum-file.c index 0f35fa5ee47c..7510950fa3e9 100644 --- a/csum-file.c +++ b/csum-file.c @@ -89,32 +89,35 @@ int finalize_hashfile(struct hashfile *f, unsigned char *result, unsigned int fl void hashwrite(struct hashfile *f, const void *buf, unsigned int count) { while (count) { - unsigned offset = f->offset; - unsigned left = sizeof(f->buffer) - offset; + unsigned left = sizeof(f->buffer) - f->offset; unsigned nr = count > left ? left : count; - const void *data; if (f->do_crc) f->crc32 = crc32(f->crc32, buf, nr); if (nr == sizeof(f->buffer)) { - /* process full buffer directly without copy */ - data = buf; + /* + * Flush a full batch worth of data directly + * from the input, skipping the memcpy() to + * the hashfile's buffer. In this block, + * f->offset is necessarily zero. + */ + the_hash_algo->update_fn(&f->ctx, buf, nr); + flush(f, buf, nr); } else { - memcpy(f->buffer + offset, buf, nr); - data = f->buffer; + /* + * Copy to the hashfile's buffer, flushing only + * if it became full. + */ + memcpy(f->buffer + f->offset, buf, nr); + f->offset += nr; + left -= nr; + if (!left) + hashflush(f); } count -= nr; - offset += nr; buf = (char *) buf + nr; - left -= nr; - if (!left) { - the_hash_algo->update_fn(&f->ctx, data, offset); - flush(f, data, offset); - offset = 0; - } - f->offset = offset; } }