TE
TechEcho
Home24h TopNewestBestAskShowJobs
GitHubTwitter
Home

TechEcho

A tech news platform built with Next.js, providing global tech news and discussions.

GitHubTwitter

Home

HomeNewestBestAskShowJobs

Resources

HackerNews APIOriginal HackerNewsNext.js

© 2025 TechEcho. All rights reserved.

Std: Clamp generates less efficient assembly than std:min(max,std:max(min,v))

174 pointsby x1f604over 1 year ago

11 comments

cmovqover 1 year ago
Depending on the order of the arguments to min max you&#x27;ll get an extra move instruction [1]:<p>std::min(max, std::max(min, v));<p><pre><code> maxsd xmm0, xmm1 minsd xmm0, xmm2 </code></pre> std::min(std::max(v, min), max);<p><pre><code> maxsd xmm1, xmm0 minsd xmm2, xmm1 movapd xmm0, xmm2 </code></pre> For min&#x2F;max on x86 if any operand is NaN the instruction copies the second operand into the first. So the compiler can&#x27;t reorder the second case to look like the first (to leave the result in xmm0 for the return value).<p>The reason for this NaN behavior is that minsd is implemented to look like `(a &lt; b) ? a : b`, where if any of a or b is NaN the condition is false, and the expression evaluates to b.<p>Possibly std::clamp has the comparisons ordered like the second case?<p>[1]: <a href="https:&#x2F;&#x2F;godbolt.org&#x2F;z&#x2F;coes8Gdhz" rel="nofollow">https:&#x2F;&#x2F;godbolt.org&#x2F;z&#x2F;coes8Gdhz</a>
评论 #39015529 未加载
评论 #39018537 未加载
评论 #39025201 未加载
评论 #39015661 未加载
camblomquistover 1 year ago
I did a double take on this because I wrote a blog post about this topic a few months ago and came to a very different conclusion, that the results are effectively identical on clang and gcc is just weird.<p>Then I realized that I was writing about compiling for ARM and this post is about x86. Which is extra weird! Why is the compiler better tuned for ARM than x86 in this case?<p>Never did figure out what gcc&#x27;s problem was.<p><a href="https:&#x2F;&#x2F;godbolt.org&#x2F;z&#x2F;Y75qnTGdr" rel="nofollow">https:&#x2F;&#x2F;godbolt.org&#x2F;z&#x2F;Y75qnTGdr</a>
评论 #39014131 未加载
celegans25over 1 year ago
On gcc 13, the difference in assembly between the min(max()) version and std::clamp is eliminated when I add the -ffast-math flag. I suspect that the two implementations handle one of the arguments being NaN a bit differently.<p><a href="https:&#x2F;&#x2F;gcc.godbolt.org&#x2F;z&#x2F;fGaP6roe9" rel="nofollow">https:&#x2F;&#x2F;gcc.godbolt.org&#x2F;z&#x2F;fGaP6roe9</a><p>I see the same behavior on clang 17 as well<p><a href="https:&#x2F;&#x2F;gcc.godbolt.org&#x2F;z&#x2F;6jvnoxWhb" rel="nofollow">https:&#x2F;&#x2F;gcc.godbolt.org&#x2F;z&#x2F;6jvnoxWhb</a>
评论 #39013277 未加载
评论 #39014940 未加载
jeffbeeover 1 year ago
Clang generates the shortest of these if you target sandybridge, or x86-64-v3, or later. The real article that&#x27;s buried in this article is that compilers target k8-generic unless you tell them otherwise, and the features and cost model of opteron are obsolete.<p>Always specify your target.
评论 #39013490 未加载
评论 #39039616 未加载
svantanaover 1 year ago
I&#x27;m a heavy std::clamp user, but I&#x27;m considering replacing it with min+max because of the uncertainty about what will happen when lo &gt; hi. On windows it triggers an assertion, while other platforms just do a min+max in one or the other order. Of course, this should never happen but can be difficult to guarantee when the limits are derived from user inputs.
评论 #39013598 未加载
评论 #39013187 未加载
评论 #39013278 未加载
评论 #39015501 未加载
tambreover 1 year ago
Both recent GCC and Clang are able to generate the most optimal version for std::clamp() if you add something like -march=znver1, even at -O1 [0]. Interesting!<p>[0] <a href="https:&#x2F;&#x2F;godbolt.org&#x2F;z&#x2F;YsMMo7Kjz" rel="nofollow">https:&#x2F;&#x2F;godbolt.org&#x2F;z&#x2F;YsMMo7Kjz</a>
评论 #39013148 未加载
评论 #39039609 未加载
planedeover 1 year ago
On a somewhat similar note, don&#x27;t use std::lerp if you don&#x27;t need its strong guarantees around rounding (monotonicity among other things).<p><a href="https:&#x2F;&#x2F;godbolt.org&#x2F;z&#x2F;hzrG3s6T4" rel="nofollow">https:&#x2F;&#x2F;godbolt.org&#x2F;z&#x2F;hzrG3s6T4</a>
CountHackulusover 1 year ago
I see that the assembly instructions are different, but what&#x27;s the performance difference? Personally, I don&#x27;t care about the number of instructions used, as long as it&#x27;s faster. With things like store forwarding and register files, a lot of those movs might be treated as noops.
superjanover 1 year ago
The only times I worry about min&#x2F;max&#x2F;clamp performance is when I need to do thousands or millions of them. And in that case, I’d suggest intrinsics. You get to choose how NaN is handled, it’s branchless, and you can do multiple in parallel.<p>It feels backwards that you need to order your comparisons so as to generate optimal assembly.
nickysielickiover 1 year ago
<a href="https:&#x2F;&#x2F;bugs.llvm.org&#x2F;show_bug.cgi?id=47271" rel="nofollow">https:&#x2F;&#x2F;bugs.llvm.org&#x2F;show_bug.cgi?id=47271</a><p>This specific test (click the godbolt links) does not reproduce the issue.
fookerover 1 year ago
If you benchmark these, you&#x27;ll likely find the version with the jump edges out the one with the conditional instruction in practice.
评论 #39013372 未加载
评论 #39013309 未加载
评论 #39013181 未加载