Uh oh!
There was an error while loading. Please reload this page.
Serialize incr comp structures to file via fixed-size buffer - #80463
Conversation
rust-highfive
commented
Dec 29, 2020
r? @oli-obk (rust-highfive has picked a reviewer for you, use r? to override) |
tgnottingham
commented
Dec 29, 2020
@rustbot label T-compiler A-incr-comp I-compiletime I-compilemem |
tgnottingham
commented
Dec 29, 2020
Btw, if #80115 merges and this is rebased on and adapted for it, it should also reduce the impact on instruction count. |
rust-log-analyzer
commented
Dec 29, 2020
The job Click to see the possible cause of the failure (guessed by this bot) |
1dd1955 to
d2eb5f5Comparejyn514
commented
Dec 29, 2020
I would be curious to see how much this helps with #79103. |
lqd
commented
Dec 29, 2020
@bors try @rust-timer queue |
rust-timer
commented
Dec 29, 2020
Awaiting bors try build completion. |
bors
commented
Dec 29, 2020
⌛ Trying commit d2eb5f51983c8d7e73d8a14a8e25bc3e540268b9 with merge 9afe98c1ed87fbe0ee26f93e23a265c09372baf6... |
bors
commented
Dec 29, 2020
☀️ Try build successful - checks-actions |
rust-timer
commented
Dec 29, 2020
Queued 9afe98c1ed87fbe0ee26f93e23a265c09372baf6 with parent d75f48e, future comparison URL. @rustbot label: +S-waiting-on-perf |
rust-timer
commented
Dec 29, 2020
Finished benchmarking try commit (9afe98c1ed87fbe0ee26f93e23a265c09372baf6): comparison url. Benchmarking this pull request likely means that it is perf-sensitive, so we're automatically marking it as not fit for rolling up. Please note that if the perf results are neutral, you should likely undo the rollup=never given below by specifying Importantly, though, if the results of this run are non-neutral do not roll this PR up -- it will mask other regressions or improvements in the roll up. @bors rollup=never |
The results are more extreme than what I see in local benchmarks. E.g. the 20% reduction I mentioned in the summary turned into a 30% reduction, but the instruction counts are worse. I'll investigate. |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
d2eb5f5 to
27dadafComparetgnottingham
commented
Dec 30, 2020
I was hoping to reproduce the above perf results locally, but it's too much of a chore to figure out what's causing the difference. It's not that important anyway, provided it's not actually a correctness issue. I got some assurance that it isn't UB that's somehow causing the perf difference, by running some tests locally under Miri. I pushed couple of changes: a fix to an existing bug in the signed LEB128 decoding (it had no perf impact, and may not have been encountered in practice), an increase to the Regarding performance, I'd like to see how #80115 changes things before trying anything else. Though if anyone can see something that will make buffered file writing more competitive with the previous implemention, I'd gladly take a hint. |
jyn514
commented
Dec 30, 2020
Have you considered using mmap directly? That would need some unsafe code, but it shouldn't require an intermediate copy at all. |
tgnottingham
commented
Dec 30, 2020
I hadn't. I have some reservations, but it's worth experimenting with. |
I implemented an The numbers weren't very encouraging -- up to 7% worse in instruction count. The increase is probably due to some complexity in handling data that ends up straddling one I'm sure it could result in an improvement if optimized, but I just don't want to go down that path, personally. |
7de6bd2 to
44be53bComparetgnottingham
commented
Jan 10, 2021
@bors try @rust-timer queue |
rust-timer
commented
Jan 10, 2021
Awaiting bors try build completion. |
bors
commented
Jan 10, 2021
⌛ Trying commit cce99b3bdd4c695026fda94fea711e5f0d7c7ee5 with merge 007744ee92eae311ea94065ead37aa138ea30e30... |
bors
commented
Jan 10, 2021
☀️ Try build successful - checks-actions |
rust-timer
commented
Jan 10, 2021
Queued 007744ee92eae311ea94065ead37aa138ea30e30 with parent 7cf2056, future comparison URL. @rustbot label: +S-waiting-on-perf |
rust-timer
commented
Jan 10, 2021
Finished benchmarking try commit (007744ee92eae311ea94065ead37aa138ea30e30): comparison url. Benchmarking this pull request likely means that it is perf-sensitive, so we're automatically marking it as not fit for rolling up. Please note that if the perf results are neutral, you should likely undo the rollup=never given below by specifying Importantly, though, if the results of this run are non-neutral do not roll this PR up -- it will mask other regressions or improvements in the roll up. @bors rollup=never |
tgnottingham
commented
Jan 10, 2021
Perf result confirms that larger buffer doesn't help. |
tgnottingham
commented
Jan 11, 2021
I'm looking into the overhead of error handling. From the assembly, it seems like there's unnecessary overhead in On first glance, it looks like creation of the Here is the assembly for push %rbppush %rbxpush %raxmov %esi,%ebpmov0x8(%rdi),%rbxmov0x10(%rbx),%raxlea0x5(%rax),%rcxcmp0x8(%rbx),%rcx /--------ja/--|-------> mov (%rbx),%rcx| | add %rax,%rcx| | mov$0x1,%edx| | cmp$0x80,%ebp| | /-----jb| | | mov %ebp,%esi| | | /-> mov %esi,%edi| | | | or$0x80,%bpl| | | | mov %bpl,(%rcx)| | | | shr$0x7,%esi| | | | add$0x1,%rcx| | | | add$0x1,%rdx| | | | mov %esi,%ebp| | | | cmp$0x3fff,%edi| | | \--ja| | | mov %esi,%ebp| | \----> mov %bpl,(%rcx)| | add %rax,%rdx| | mov %rdx,0x10(%rbx)| | mov$0x3,%al // Start of Result creation in Ok path| | /----> shld$0x8,%rcx,%rdx // Continuation of Result creation in Ok and Err paths| | | shl$0x8,%rcx| | | movzbl %al,%eax| | | or %rcx,%rax| | | add$0x8,%rsp| | | pop %rbx| | | pop %rbp| | | retq | \--|----> mov %rbx,%rdi| | callq *0x168146d(%rip) # <<rustc_serialize::opaque::FileEncoder>::flush@@Base+0xe7b158>| | cmp$0x3,%al| | /--jne| | | xor %eax,%eax\-----|--|--jmp | \-> mov %rax,%rcx // Start of Result creation in Err path (when flush() fails) | shrd$0x8,%rdx,%rcx | shr$0x8,%rdx \-----jmpThe result is a The formation of the | | mov$0x3,%al // Set Ok variant, I assume.| | /----> shld$0x8,%rcx,%rdx // Seems irrelevant to Ok Result.| | | shl$0x8,%rcx // Seems irrelevant to Ok Result.| | | movzbl %al,%eax // eax = Ok variant, zero-extended.| | | or %rcx,%rax // rax = Ok variant plus seemingly irrelevant stuff inrcx.I think the above could be replaced with something like The formation of the // ...just called flush,and found thatresult inrax was Err... | \-> mov %rax,%rcx | shrd$0x8,%rdx,%rcx | shr$0x8,%rdx \-----jmp// ...jumps to the shld from previously quoted block...| | /----> shld$0x8,%rcx,%rdx| | | shl$0x8,%rcx| | | movzbl %al,%eax| | | or %rcx,%raxIIUC, this sequence doesn't change If all the above is correct, fixing this would remove 4 instructions from the hot path, and 7 from the cold. That alone would help, since the hot code is really hot, but it might also unlock other optimizations. Anyway, I'm hoping my reasoning is right, and I'll continue looking into this. |
cjgillot
commented
Jan 11, 2021
Should we merge this PR for the memory improvement, and let the perf side for another PR? |
oli-obk
commented
Jan 11, 2021
I'm fine with that, but if @tgnottingham still has motivation to munch away at the perf part, I'm not going to stop them 😆 |
Reduce a large memory spike that happens during serialization by writing the incr comp structures to file by way of a fixed-size buffer, rather than an unbounded vector. Effort was made to keep the instruction count close to that of the previous implementation. However, buffered writing to a file inherently has more overhead than writing to a vector, because each write may result in a handleable error. To reduce this overhead, arrangements are made so that each LEB128-encoded integer can be written to the buffer with only one capacity and error check. Higher-level optimizations in which entire composite structures can be written with one capacity and error check are possible, but would require much more work. The performance is mostly on par with the previous implementation, with small to moderate instruction count regressions. The memory reduction is significant, however, so it seems like a worth-while trade-off.
The signed LEB128 decoding function used a hardcoded constant of 64 instead of the number of bits in the type of integer being decoded, which resulted in incorrect results for some inputs. Fix this, make the decoding more consistent with the unsigned version, and increase the LEB128 encoding and decoding test coverage.
cce99b3 to
f15fae8Comparetgnottingham
commented
Jan 11, 2021
Rebased and returned to using default buffer size.
I'd love that. :) I'll create an issue for the |
jyn514
commented
Jan 12, 2021
@bors r=oli-obk rollup=never |
bors
commented
Jan 12, 2021
📌 Commit f15fae8 has been approved by |
bors
commented
Jan 12, 2021
bors
commented
Jan 12, 2021
☀️ Test successful - checks-actions |
tgnottingham
commented
Jan 18, 2021
Created #81146 for the |
Reduce a large memory spike that happens during serialization by writing
the incr comp structures to file by way of a fixed-size buffer, rather
than an unbounded vector.
Effort was made to keep the instruction count close to that of the
previous implementation. However, buffered writing to a file inherently
has more overhead than writing to a vector, because each write may
result in a handleable error. To reduce this overhead, arrangements are
made so that each LEB128-encoded integer can be written to the buffer
with only one capacity and error check. Higher-level optimizations in
which entire composite structures can be written with one capacity and
error check are possible, but would require much more work.
The performance is mostly on par with the previous implementation, with
small to moderate instruction count regressions. The memory reduction is
significant, however, so it seems like a worth-while trade-off.