<div dir="ltr"><br><div class="gmail_extra"><br><div class="gmail_quote">On Thu, Aug 3, 2017 at 7:17 PM, Karl Wagner <span dir="ltr">&lt;<a href="mailto:razielim@gmail.com" target="_blank">razielim@gmail.com</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div style="word-wrap:break-word"><br><div><blockquote type="cite"><div><div class="h5"><div>On 3. Aug 2017, at 20:52, Taylor Swift via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>&gt; wrote:</div><br class="m_-3360087742745277501Apple-interchange-newline"></div></div><div><div><div class="h5"><div dir="ltr"><div><div><div>In an effort to get this thread back on track, I tried implementing cos(_:) in pure generic Swift code, with the BinaryFloatingPoint protocol. It deviates from the _cos(_:) intrinsic by no more than 5.26362703423544e-11. Adding more terms to the approximation only has a small penalty to the performance for some reason.<br><br></div>To make the benchmarks fair, and explore the idea of distributing a Math module without killing people on the cross-module optimization boundary, I enabled some of the unsafe compiler attributes. All of these benchmarks are cross-module calls, as if the math module were downloaded as a dependency in the SPM.<br><br><span style="font-family:monospace,monospace">== Relative execution time (lower is better) ==<br><br></span><span style="font-family:monospace,monospace"><span style="background-color:rgb(217,210,233)">llvm intrinsic                   :  3.133</span><br></span></div><div><span style="font-family:monospace,monospace"><span style="background-color:rgb(234,209,220)">glibc cos()                      :  3.124</span><br></span></div><div><span style="font-family:monospace,monospace"><br>no attributes                    : 43.675<br>with specialization              :  4.162<br>with inlining                    :  3.108<br>with inlining and specialization :  3.264</span><br><br></div>As you can see, the pure Swift generic implementation actually beats the compiler intrinsic (and the glibc cos() but I guess they’re the same thing) when inlining is used, but for some reason generic specialization and inlining don’t get along very well.<br><br></div>Here’s the source implementation. It uses a taylor series (!) which probably isn’t optimal but it does prove that cos() and sin() can be implemented as generics in pure Swift, be distributed as a module outside the stdlib, and still achieve competitive performance with the llvm intrinsics.<br><br><span style="font-family:monospace,monospace">@_inlineable<br>//@_specialize(where F == Float)<br>//@_specialize(where F == Double)<br>public<br>func cos&lt;F&gt;(_ x:F) -&gt; F where F:BinaryFloatingPoint<br>{<br>    let x:F = abs(x.remainder(dividingBy: 2 * F.pi)),<br>        quadrant:Int = Int(x * (2 / F.pi))<br><br>    switch quadrant<br>    {<br>    case 0:<br>        return  cos(on_first_quadrant:        x)<br>    case 1:<br>        return -cos(on_first_quadrant: F.pi - x)<br>    case 2:<br>        return -cos(on_first_quadrant: x - F.pi)<br>    case 3:<br>        return -cos(on_first_quadrant: 2 * F.pi - x)<br>    default:<br>        fatalError(&quot;unreachable&quot;)<br>    }<br>}<br><br>@_versioned<br>@_inlineable<br>//@_specialize(where F == Float)<br>//@_specialize(where F == Double)<br>func cos&lt;F&gt;(on_first_quadrant x:F) -&gt; F where F:BinaryFloatingPoint<br>{<br>    let x2:F = x * x<br>    var y:F  = -0.<wbr>000000000011470745126775543239<wbr>4<br>    for c:F in [0.<wbr>000000002087675698165412591559<wbr>,<br>               -0.<wbr>000000275573192239332256421489<wbr>,<br>                0.<wbr>00002480158730158702330045157,<br>               -0.<wbr>00138888888888888880310186415,<br>                0.<wbr>04166666666666666665319411988,<br>               -0.<wbr>4999999999999999999991637437,<br>                0.9999999999999999999999914771<br>                ]<br>    {<br>        y = x2 * y + c<br>    }<br>    return y<br>}</span><br></div></div></div><span class=""><div class="gmail_extra"><br><div class="gmail_quote">On Thu, Aug 3, 2017 at 7:04 AM, Stephen Canon via swift-evolution <span dir="ltr">&lt;<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div style="word-wrap:break-word;line-break:after-white-space"><span><blockquote type="cite">On Aug 2, 2017, at 7:03 PM, Karl Wagner via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>&gt; wrote:<br></blockquote><div><blockquote type="cite"><div><br class="m_-3360087742745277501m_3712668855384799923Apple-interchange-newline"><span style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;float:none;display:inline!important">It’s important to remember that computers are mathematical machines, and some functions which are implemented in hardware on essentially every platform (like sin/cos/etc) are definitely best implemented as compiler intrinsics.</span></div></blockquote><br></div></span><div>sin/cos/etc are implemented in software, not hardware. x86 does have the FSIN/FCOS instructions, but (almost) no one actually uses them to implement the sin( ) and cos( ) functions; they are a legacy curiosity, both too slow and too inaccurate for serious use today. There are no analogous instructions on ARM or PPC.</div><div><br></div><div>– Steve</div></div><br>______________________________<wbr>_________________<br>
swift-evolution mailing list<br>
<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a><br>
<a href="https://lists.swift.org/mailman/listinfo/swift-evolution" rel="noreferrer" target="_blank">https://lists.swift.org/mailma<wbr>n/listinfo/swift-evolution</a><br>
<br></blockquote></div><br></div>
______________________________<wbr>_________________<br>swift-evolution mailing list<br><a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a><br><a href="https://lists.swift.org/mailman/listinfo/swift-evolution" target="_blank">https://lists.swift.org/<wbr>mailman/listinfo/swift-<wbr>evolution</a><br></span></div></blockquote></div><br><div><br></div><div>Just a guess, but I’d expect inlining implies specialisation. It would be weird if the compiler inlined a chunk of unoptimised generic code in to your function.</div><div><br></div><div>Pretty cool figures, though.</div><span class="HOEnZb"><font color="#888888"><div><br></div><div>- Karl</div><div><br></div></font></span></div></blockquote></div><br></div><div class="gmail_extra">The weird part is that generic specialization actually <i>hurt</i> performance. I understand why inlining can be harmful sometimes, but I always assumed specialization was always helpful. Weird. Can a core team member weigh in here?<br></div></div>