Hi all,Here's my analysis about camput performance, please correct me if I'm wrong ormissing something.
- client.Client.StatBlobs() supports stating multiple files in a single requestbut is currently unbounded which could cause issues with overly long query url.I wrote https://camlistore.org/r/249 to limit request length and make therequest size parallel "while at it". In particular,pkg/blobserver/handlers/stat.go(37) "const maxStatBlobs = 1000" so the clientcode was probably never used in batching mode.
- There's concurrency in in cmd/camput/files.go; with 5 concurrent requests forupload and 10 for local cache lookup. Hypothesis is that cache lookup durationis minimal and performance is largely bound by the latency of the httprequests due to the sheer number of http requests done serially.
- Cache lookup seems to be totally CPU bound so it should useruntime.GOMAXPROCS(-1) instead of hardcoding to 10.
- camput starts a loop in cmd/camput/files.go to upload each file individualwith its own client.Client instead.
It's created withclient.NewUploadHandleFromString() and then client.Client.Upload() isinherently serial in its operation.
- schema.serverHasBlob() is eventually called from schema.writeFileChunks() for
the rolling hash blobs, interestingly this is also done serially with theuploadLastSpan() inner function, compounding request latency.
I would have like to help with it but I'm not sure where I could do a meaningfulcontribution taking in account the different layers involved (schema, client,camput), in theory:- client.Client.StatBlobs() should be run in a loop, accepting a mix of filelookup and chunk lookup.- That mainly requires changing the caller so that the task items are surfacedto client.Client.- camput should defer upload parallelism to client.Client.- Remote store chunks lookup should be done in parallel after the fileis detected to not be present.
- Remote store file lookup should be batched, especially in case of >thousandsfiles.
- A priority queue should be used to determine which batch to stat first, thelargest files first, since they are the slowest to upload if they are a 100%remote store miss.
- client.Client parallelism should parallelize both stating and uploading, butshould give priority to stating requests.I didn't profile the app yet, I only looked at the code, so the above hypothesiscould be all wrong. I've saw related TODOs in the code so I guess it's just amatter of priorities that explain the current state.I wrote https://camlistore.org/r/248 to enable localhost HTTP PROXY toeventually try out with a 500ms latency inducing proxy and optimizeaccordingly.
And sorry Brad, I didn't know misc/review sent email by default. I wanted you to get this email before the reviews.
On Mon, Jan 28, 2013 at 7:09 AM, Marc-Antoine Ruel <mar...@google.com> wrote:- Cache lookup seems to be totally CPU bound so it should useruntime.GOMAXPROCS(-1) instead of hardcoding to 10.I don't think -1 does what you think it means. There's no way to make it go unbounded. You probably mean runtime.NumCPU().
- camput starts a loop in cmd/camput/files.go to upload each file individualwith its own client.Client instead.I know it does each file individually for now (see below), but I don't think each one has its own HTTP client... sure?
- schema.serverHasBlob() is eventually called from schema.writeFileChunks() forthe rolling hash blobs, interestingly this is also done serially with theuploadLastSpan() inner function, compounding request latency.Yes, this has been bugging me too. I believe there are TODOs there about this.It was so tricky to get all that code working well and bug-free that I hid from that problem for awhile, but I'm confident in our tests and FileReader / FileWriter now, so it's probably time to tackle.I would have like to help with it but I'm not sure where I could do a meaningfulcontribution taking in account the different layers involved (schema, client,camput), in theory:- client.Client.StatBlobs() should be run in a loop, accepting a mix of filelookup and chunk lookup.- That mainly requires changing the caller so that the task items are surfacedto client.Client.- camput should defer upload parallelism to client.Client.- Remote store chunks lookup should be done in parallel after the fileis detected to not be present.To some degree. If you have a 2TB local file, you don't want to buffer that all in memory. In general, the FileWriter writes a file from an io.Reader, not something seekable. (and if it's seekable: it might be changing underneath you). But yes.
- A priority queue should be used to determine which batch to stat first, thelargest files first, since they are the slowest to upload if they are a 100%remote store miss.Yes. Even without a priority queue, I'd be happy just to do batching in the case where client.go's maxParallelHTTP is exhausted and a stat has to wait.It should never be the case that 500 blob stats are waiting on reqGate, and then do 500 1-blob stats. They should be aggregated at that point. I'd fix that first.
I wrote https://camlistore.org/r/248 to enable localhost HTTP PROXY toeventually try out with a 500ms latency inducing proxy and optimizeaccordingly.You don't need a proxy. The server already supports both verbose per-HTTP request output and also latency + bandwidth control, which I added specifically for optimizing camput and camget.See the dev-server script at the root and its flags. Day-to-day we work with dev-server and dev-camput / dev-camget.
2013/1/28 Brad Fitzpatrick <br...@danga.com>On Mon, Jan 28, 2013 at 7:09 AM, Marc-Antoine Ruel <mar...@google.com> wrote:- Cache lookup seems to be totally CPU bound so it should useruntime.GOMAXPROCS(-1) instead of hardcoding to 10.I don't think -1 does what you think it means. There's no way to make it go unbounded. You probably mean runtime.NumCPU().The minimum value between runtime.GOMAXPROCS(-1) and runtime.NumCPU() would make sense? E.g. if GOMAXPROCS < NumCPU, it's not worth sending NumCPU requests. I can do a CL for that, it's ~4 lines.
- schema.serverHasBlob() is eventually called from schema.writeFileChunks() forthe rolling hash blobs, interestingly this is also done serially with theuploadLastSpan() inner function, compounding request latency.Yes, this has been bugging me too. I believe there are TODOs there about this.It was so tricky to get all that code working well and bug-free that I hid from that problem for awhile, but I'm confident in our tests and FileReader / FileWriter now, so it's probably time to tackle.
I wrote https://camlistore.org/r/248 to enable localhost HTTP PROXY to
eventually try out with a 500ms latency inducing proxy and optimizeaccordingly.You don't need a proxy. The server already supports both verbose per-HTTP request output and also latency + bandwidth control, which I added specifically for optimizing camput and camget.See the dev-server script at the root and its flags. Day-to-day we work with dev-server and dev-camput / dev-camget.Failed to find it; neither of the two following commands is informative:./dev-server -helpgopath/bin/linux_amd64/camlistored -help
I think supporting a local proxy enables more fiddling with the requests and less custom code in the project, without having to resort to use 2 workstations. For example do an experiment by HTTP 500'ing 1% of the requests. Or add extraneous 500ms to 10% of the requests. Overall, reproducing how AppEngine behaves. :) I don't mind if you don't think it's useful.