-
Notifications
You must be signed in to change notification settings - Fork 6.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Speed up SinCosDegrees() by manual range reduction.
SinCosDegrees() is fortunate enough to take in its arguments in degrees (360 degrees = 1 turn) instead of radians (2pi radians = 1 turn). Accurate range reduction in degrees is much simpler than in radians; 45 is a whole number and exactly representable as a double, unlike pi/2. This means that we can get greater accuracy _and_ higher speed than the current code of fmod() + internal sincos() range reduction, by doing it ourselves, mostly branch-free. (In particular, fmod() appears to be very slow on macOS.) This gives a small boost in MotionMark for the Leaves and/or Multiply subtests, which both make heavy use of rotate() in CSS. MotionMark (mac-m1_mini_2020-perf, 95% CI, only significant results): motionmark_ramp_leaves [ +0.7%, +1.0%] Same with win-10-perf: motionmark_ramp_multiply [ +0.6%, +1.9%] Change-Id: Iedf1822d2995408531a623d0768373703941e3e2 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4537360 Reviewed-by: danakj <danakj@chromium.org> Commit-Queue: Steinar H Gunderson <sesse@chromium.org> Cr-Commit-Position: refs/heads/main@{#1150624}
- Loading branch information
Steinar H. Gunderson
authored and
Chromium LUCI CQ
committed
May 30, 2023
1 parent
230315b
commit 109a4bf
Showing
10 changed files
with
178 additions
and
25 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file modified
BIN
+20 Bytes
(100%)
...rm/linux/fast/forms/datalist/input-appearance-range-with-transform-expected.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified
BIN
-6 Bytes
(100%)
...form/mac/fast/forms/datalist/input-appearance-range-with-transform-expected.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified
BIN
+6 Bytes
(100%)
third_party/blink/web_tests/transforms/matrix-02-expected.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
// Copyright 2023 The Chromium Authors | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
#ifndef UI_GFX_GEOMETRY_SIN_COS_DEGREES_H_ | ||
#define UI_GFX_GEOMETRY_SIN_COS_DEGREES_H_ | ||
|
||
#include "angle_conversions.h" | ||
#include "base/numerics/math_constants.h" | ||
|
||
#include <algorithm> | ||
#include <cmath> | ||
|
||
namespace gfx { | ||
|
||
struct SinCos { | ||
double sin; | ||
double cos; | ||
bool IsZeroAngle() const { return sin == 0 && cos == 1; } | ||
}; | ||
|
||
inline SinCos SinCosDegrees(double degrees) { | ||
// Some math libraries have poor accuracy with large arguments, | ||
// so range-reduce explicitly before we call sin() or cos(). However, unless | ||
// we're _really_ large (out of range of an int), we can do that faster than | ||
// fmod(), since we have an integer divisor (and as an extra bonus, we've | ||
// already got it precomputed). We pick a pretty arbitrary limit that should | ||
// be safe. | ||
// | ||
// We range-reduce to [0..45]. This should hit the fast path of sincos() | ||
// on most platforms (since no further reduction is needed; reducing | ||
// accurately modulo a trancendental can we slow), using only branches that | ||
// should be possible to do using conditional operations; using a switch | ||
// instead would be possible, but benchmarked much slower on M1. | ||
// For platforms that don't use sincos() (e.g., it seems Clang doesn't | ||
// manage the rewrite on Linux), we also save on having the range reduction | ||
// done only once. | ||
if (degrees > -90000000.0 && degrees < 90000000.0) { | ||
// Make sure 0, 90, 180 and 270 degrees get exact results. (We also have | ||
// precomputed values for 45, 135, etc., but only as a side effect of using | ||
// 45 instead of 90, for the benefit of the range reduction algorithm below. | ||
// The error for e.g. sin(45 degrees) is typically only 1 ulp.) | ||
double n45degrees = degrees / 45.0; | ||
int octant = static_cast<int>(n45degrees); | ||
if (octant == n45degrees) { | ||
constexpr SinCos kSinCosN45[] = { | ||
{0, 1}, {base::kSqrtHalfDouble, base::kSqrtHalfDouble}, | ||
{1, 0}, {base::kSqrtHalfDouble, -base::kSqrtHalfDouble}, | ||
{0, -1}, {-base::kSqrtHalfDouble, -base::kSqrtHalfDouble}, | ||
{-1, 0}, {-base::kSqrtHalfDouble, base::kSqrtHalfDouble}}; | ||
return kSinCosN45[octant & 7]; | ||
} | ||
|
||
if (degrees < 0) { | ||
// This will cause the range-reduction below to move us | ||
// into [0..45], as desired, instead of [-45..0]. | ||
--octant; | ||
} | ||
degrees -= octant * 45.0; // Range-reduce to [0..45]. | ||
|
||
// Deal with 45..90 the same as 45..0. This also covers | ||
// 135..180, 225..270 and 315..360, i.e. the odd octants. | ||
// The relevant trigonometric identities is that | ||
// sin(90 - a) = cos(a) and vice versa; we do the sin/cos | ||
// flip below. | ||
if (octant & 1) { | ||
degrees = 45.0 - degrees; | ||
} | ||
|
||
double rad = DegToRad(degrees); | ||
double s = std::sin(rad); | ||
double c = std::cos(rad); | ||
|
||
// 45..135 and -135..-45 can be moved into the opposite areas | ||
// simply by flipping the x and y axis (in conjunction with | ||
// the conversion from CW to CCW done above). | ||
using std::swap; | ||
if ((octant + 1) & 2) { | ||
swap(s, c); | ||
} | ||
|
||
// For sine, 180..360 (lower half) is the same as 0..180, | ||
// except negative. | ||
if (octant & 4) { | ||
s = -s; | ||
} | ||
|
||
// For cosine, 90..270 (right half) is the same as -90..90, | ||
// except negative. | ||
if ((octant + 2) & 4) { | ||
c = -c; | ||
} | ||
|
||
return SinCos{s, c}; | ||
} | ||
|
||
// Slow path for extreme cases. | ||
degrees = std::fmod(degrees, 360.0); | ||
double rad = DegToRad(degrees); | ||
return SinCos{std::sin(rad), std::cos(rad)}; | ||
} | ||
|
||
} // namespace gfx | ||
|
||
#endif // UI_GFX_GEOMETRY_SIN_COS_DEGREES_H_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
// Copyright 2023 The Chromium Authors | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
#define _USE_MATH_DEFINES // To get M_PI on Windows. | ||
|
||
#include "ui/gfx/geometry/sin_cos_degrees.h" | ||
|
||
#include <math.h> | ||
|
||
#include "testing/gtest/include/gtest/gtest.h" | ||
|
||
namespace gfx { | ||
namespace { | ||
|
||
TEST(SinCosDegreesTest, ExactValues) { | ||
for (int turn = -5 * 360; turn <= 5 * 360; turn += 360) { | ||
EXPECT_EQ(0.0, SinCosDegrees(turn + 0).sin); | ||
EXPECT_EQ(1.0, SinCosDegrees(turn + 0).cos); | ||
|
||
EXPECT_EQ(1.0, SinCosDegrees(turn + 90).sin); | ||
EXPECT_EQ(0.0, SinCosDegrees(turn + 90).cos); | ||
|
||
EXPECT_EQ(0.0, SinCosDegrees(turn + 180).sin); | ||
EXPECT_EQ(-1.0, SinCosDegrees(turn + 180).cos); | ||
|
||
EXPECT_EQ(-1.0, SinCosDegrees(turn + 270).sin); | ||
EXPECT_EQ(0.0, SinCosDegrees(turn + 270).cos); | ||
} | ||
} | ||
|
||
TEST(SinCosDegreesTest, CloseToLibc) { | ||
for (int d = -3600; d <= 3600; ++d) { | ||
double degrees = (d * 0.1); | ||
EXPECT_NEAR(sin(degrees * M_PI / 180.0), SinCosDegrees(degrees).sin, 1e-6); | ||
EXPECT_NEAR(cos(degrees * M_PI / 180.0), SinCosDegrees(degrees).cos, 1e-6); | ||
} | ||
} | ||
|
||
TEST(SinCosDegreesTest, AccurateRangeReduction) { | ||
EXPECT_EQ(SinCosDegrees(90000123).sin, SinCosDegrees(90000123).sin); | ||
EXPECT_EQ(SinCosDegrees(90000123).cos, SinCosDegrees(90000123).cos); | ||
|
||
EXPECT_EQ(SinCosDegrees(90e5).sin, 0.0); | ||
EXPECT_EQ(SinCosDegrees(90e5).cos, 1.0); | ||
} | ||
|
||
TEST(SinCosDegreesTest, HugeValues) { | ||
EXPECT_NEAR(SinCosDegrees(360e10 + 20).sin, sin(20 * (M_PI / 180.0)), 1e-6); | ||
EXPECT_NEAR(SinCosDegrees(360e10 + 20).cos, cos(20 * (M_PI / 180.0)), 1e-6); | ||
} | ||
|
||
} // namespace | ||
} // namespace gfx |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters