自定义C#平方根算法性能优化咨询:能否提速或已达极限?
Hey there! First off, it's totally expected that your custom square root algorithm is slower than Math.Sqrt()—the built-in method is heavily optimized at both the hardware and runtime level, so matching its speed is an uphill battle. But that doesn't mean we can't make your implementation way faster. Let's break down the optimizations and why the gap exists.
1. Start with a Smart Initial Guess
Most iterative square root methods (like Newton-Raphson, which your code likely uses) converge exponentially faster with a good starting point. Instead of arbitrary values like 2 or 1, use bit manipulation to get a rough guess based on the double's exponent:
// For positive doubles only long bits = BitConverter.DoubleToInt64Bits(_d); bits = (bits >> 1) + (0x3FE0000000000000L >> 1); // Adjust exponent for sqrt double x = BitConverter.Int64BitsToDouble(bits);
This guess is within a factor of 2 of the actual square root, cutting your required iterations from potentially dozens down to 3-4.
2. Trim Redundant Variables & Iterations
Your code has a lot of variables (x, y, z, w, etc.)—most are unnecessary for a tight iterative implementation. For Newton-Raphson, you only need the current guess and the input value. Each iteration boils down to:
x = 0.5 * (x + _d / x);
Also, stop iterating once the change is below a meaningful threshold (like double.Epsilon or a value you care about) instead of fixed n times. Wasting cycles on extra iterations when the value is already accurate kills performance.
3. Enable Compiler Optimizations
Always test performance in Release mode with optimizations turned on (Project Properties → Build → Check "Optimize code"). The JIT compiler will eliminate redundant variables, unroll loops, and inline small methods—things it skips in Debug mode to aid debugging.
4. Handle Edge Cases Upfront
Add checks for edge cases (negative numbers, zero, NaN, infinity) at the start of your method. This avoids running unnecessary iterations on values that have trivial results.
Why Math.Sqrt() Is So Much Faster
To set realistic expectations: Math.Sqrt() doesn't use a software loop like your implementation. It calls a hardware instruction (e.g., SQRTPS on x86 or VSQRTA on ARM) that computes the square root in 1-2 CPU cycles. Software-based algorithms can never compete with that—they're doing multiple arithmetic operations per iteration, each taking cycles.
Example Optimized Implementation
Here's a trimmed-down, fast version using the above tips:
static class MyMath { public static double Sqrt(double d) { if (d < 0) throw new ArgumentOutOfRangeException(nameof(d), "Cannot compute square root of a negative number."); if (d == 0 || double.IsNaN(d) || double.IsInfinity(d)) return d; // Smart initial guess long bits = BitConverter.DoubleToInt64Bits(d); bits = (bits >> 1) + (0x3FE0000000000000L >> 1); double x = BitConverter.Int64BitsToDouble(bits); // 3 iterations are enough for double-precision accuracy x = 0.5 * (x + d / x); x = 0.5 * (x + d / x); x = 0.5 * (x + d / x); return x; } }
This will be drastically faster than your original code, though it'll still be slower than Math.Sqrt()—but the gap will be much smaller.
Final Thought
If your goal is just to compute square roots quickly, stick with Math.Sqrt()—it's the fastest option available. But if you're doing this for learning or need a custom implementation for specific edge cases, the above optimizations will get you as close as possible with software-based methods.
内容的提问来源于stack exchange,提问作者Patrik Fröhler

