<div dir="ltr"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">David was specifically talking about the case where SomeOtherFn and<br>
SomeFn "don't depend on any data flow between each other".<br><br>
eg: you are performing lookups on two containers - compiler doesn't<br>
know these lookups have no side effects, so it can't collapse the two,<br><div>
but you as the author can see that readily.</div></blockquote>
<div>I see, thanks for the clarification.</div><div><br></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">David Blaikie <<a href="mailto:dblaikie@gmail.com">dblaikie@gmail.com</a>> 于2020年8月17日周一 下午3:14写道:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">On Mon, Aug 17, 2020 at 3:04 PM Haoran Xu via llvm-dev<br>
<<a href="mailto:llvm-dev@lists.llvm.org" target="_blank">llvm-dev@lists.llvm.org</a>> wrote:<br>
>><br>
>> I did some experiments about a year ago with different hand-written<br>
>> assembly implementations of error handling control flow patterns and it<br>
>> was really hard to beat the checked return on recent Intel hardware.<br>
>> This was a microbenchmark, so didn't see effects from increased cache usage.<br>
><br>
> That's a very interesting result. Thanks for the insights!<br>
> Just out of curiosity, did you remember how much slowdown you get between checked return and no check at all (simply ignore the error)?<br>
><br>
>> There's also the fact that explicit error checking lets the programmer<br>
>> simplify the CFG explicitly. If you're calling two functions that don't<br>
>> depend on any data flow between them, you can do something like:<br>
>><br>
>> auto a = someFn();<br>
>> auto b = someOtherFn();<br>
>> if (!a || !b)<br>
>> doSomeErrorHandlingStuffs();<br>
>><br>
>> Transforming exception-driven code into this structure would be an<br>
>> invalid transform unless the compiler could prove that someOtherFn() has<br>
>> no observable side effects.<br>
><br>
> I didn't fully understand this part. If someOtherFn() has obversable side effects, you cannot convert<br>
> auto a = someFn(); if (!a) { ... }<br>
> auto b = someOtherFn() if (!b) { ... }<br>
> to your code snippet either..<br>
<br>
David was specifically talking about the case where SomeOtherFn and<br>
SomeFn "don't depend on any data flow between each other".<br>
<br>
eg: you are performing lookups on two containers - compiler doesn't<br>
know these lookups have no side effects, so it can't collapse the two,<br>
but you as the author can see that readily.<br>
<br>
><br>
><br>
><br>
><br>
><br>
><br>
><br>
><br>
> David Chisnall via llvm-dev <<a href="mailto:llvm-dev@lists.llvm.org" target="_blank">llvm-dev@lists.llvm.org</a>> 于2020年8月14日周五 上午7:55写道:<br>
>><br>
>> On 14/08/2020 03:39, David Blaikie via llvm-dev wrote:<br>
>> > Once you get past the nothrow default problems, then you probably have<br>
>> > to deal with the performance tradeoffs between the current strategy<br>
>> > for exception implementations (table based, etc) compared to the<br>
>> > tradeoffs for explicit error handling. You'd probably find that using<br>
>> > exceptions for/every/ error return would not be the right perf<br>
>> > tradeoff for many use cases where errors are at least somewhat common.<br>
>> > So you'd probably end up with a hybrid solution - some things using<br>
>> > exceptions where the failure is rare/going to be costly anyway<br>
>> > (stopping the process, showing an error to a user/asking for user<br>
>> > input about retrying, etc) and then still using explicit error<br>
>> > handling for other things.<br>
>><br>
>> There's a second-order effect here too. Everyone knows exceptions are<br>
>> slow, so everyone writes fast-path code with explicit error returns. As<br>
>> a result, modern branch predictors are *really* good at predicting the<br>
>> no-error case. A lot of the arguments about checked return values being<br>
>> slow date back to in-order processors where adding any instructions on<br>
>> the fast path was always a slowdown, even if they were not-taken branches.<br>
>><br>
>> I did some experiments about a year ago with different hand-written<br>
>> assembly implementations of error handling control flow patterns and it<br>
>> was really hard to beat the checked return on recent Intel hardware.<br>
>> This was a microbenchmark, so didn't see effects from increased cache usage.<br>
>><br>
>> There's also the fact that explicit error checking lets the programmer<br>
>> simplify the CFG explicitly. If you're calling two functions that don't<br>
>> depend on any data flow between them, you can do something like:<br>
>><br>
>> auto a = someFn();<br>
>> auto b = someOtherFn();<br>
>> if (!a || !b)<br>
>> doSomeErrorHandlingStuffs();<br>
>><br>
>> Transforming exception-driven code into this structure would be an<br>
>> invalid transform unless the compiler could prove that someOtherFn() has<br>
>> no observable side effects.<br>
>><br>
>> David<br>
>><br>
>> _______________________________________________<br>
>> LLVM Developers mailing list<br>
>> <a href="mailto:llvm-dev@lists.llvm.org" target="_blank">llvm-dev@lists.llvm.org</a><br>
>> <a href="https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev" rel="noreferrer" target="_blank">https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev</a><br>
><br>
> _______________________________________________<br>
> LLVM Developers mailing list<br>
> <a href="mailto:llvm-dev@lists.llvm.org" target="_blank">llvm-dev@lists.llvm.org</a><br>
> <a href="https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev" rel="noreferrer" target="_blank">https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev</a><br>
</blockquote></div>