Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/jdk17u
Path: blob/master/test/jdk/java/util/DoubleStreamSums/CompensatedSums.java
66644 views
1
/*
2
* Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation.
8
*
9
* This code is distributed in the hope that it will be useful, but WITHOUT
10
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12
* version 2 for more details (a copy is included in the LICENSE file that
13
* accompanied this code).
14
*
15
* You should have received a copy of the GNU General Public License version
16
* 2 along with this work; if not, write to the Free Software Foundation,
17
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18
*
19
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20
* or visit www.oracle.com if you need additional information or have any
21
* questions.
22
*/
23
24
/*
25
* @test
26
* @bug 8214761
27
* @key randomness
28
* @library /test/lib
29
* @build jdk.test.lib.RandomFactory
30
* @run testng CompensatedSums
31
* @summary
32
*/
33
34
import java.util.Random;
35
import java.util.function.BiConsumer;
36
import java.util.function.ObjDoubleConsumer;
37
import java.util.function.Supplier;
38
import java.util.stream.DoubleStream;
39
40
import jdk.test.lib.RandomFactory;
41
import org.testng.Assert;
42
import org.testng.annotations.Test;
43
44
public class CompensatedSums {
45
46
@Test
47
public void testCompensatedSums() {
48
Random r = RandomFactory.getRandom();
49
50
double naive = 0;
51
double jdkSequentialStreamError = 0;
52
double goodSequentialStreamError = 0;
53
double jdkParallelStreamError = 0;
54
double goodParallelStreamError = 0;
55
double badParallelStreamError = 0;
56
57
for (int loop = 0; loop < 100; loop++) {
58
// sequence of random numbers of varying magnitudes, both positive and negative
59
double[] rand = r.doubles(1_000_000)
60
.map(Math::log)
61
.map(x -> (Double.doubleToLongBits(x) % 2 == 0) ? x : -x)
62
.toArray();
63
64
// base case: standard Kahan summation
65
double[] sum = new double[2];
66
for (int i=0; i < rand.length; i++) {
67
sumWithCompensation(sum, rand[i]);
68
}
69
70
// All error is the squared difference of the standard Kahan Sum vs JDK Stream sum implementation
71
// Older less accurate implementations included here as the baseline.
72
73
// squared error of naive sum by reduction - should be large
74
naive += square(DoubleStream.of(rand).reduce((x, y) -> x+y).getAsDouble() - sum[0]);
75
76
// squared error of sequential sum - should be 0
77
jdkSequentialStreamError += square(DoubleStream.of(rand).sum() - sum[0]);
78
79
goodSequentialStreamError += square(computeFinalSum(DoubleStream.of(rand).collect(doubleSupplier,objDoubleConsumer,goodCollectorConsumer)) - sum[0]);
80
81
// squared error of parallel sum from the JDK
82
jdkParallelStreamError += square(DoubleStream.of(rand).parallel().sum() - sum[0]);
83
84
// squared error of parallel sum
85
goodParallelStreamError += square(computeFinalSum(DoubleStream.of(rand).parallel().collect(doubleSupplier,objDoubleConsumer,goodCollectorConsumer)) - sum[0]);
86
87
// the bad parallel stream
88
badParallelStreamError += square(computeFinalSum(DoubleStream.of(rand).parallel().collect(doubleSupplier,objDoubleConsumer,badCollectorConsumer)) - sum[0]);
89
90
91
}
92
93
Assert.assertTrue(jdkParallelStreamError <= goodParallelStreamError);
94
Assert.assertTrue(badParallelStreamError >= jdkParallelStreamError);
95
96
Assert.assertTrue(goodSequentialStreamError >= jdkSequentialStreamError);
97
Assert.assertTrue(naive > jdkSequentialStreamError);
98
Assert.assertTrue(naive > jdkParallelStreamError);
99
100
}
101
102
private static double square(double arg) {
103
return arg * arg;
104
}
105
106
// from OpenJDK 18 Collectors, unmodified
107
static double[] sumWithCompensation(double[] intermediateSum, double value) {
108
double tmp = value - intermediateSum[1];
109
double sum = intermediateSum[0];
110
double velvel = sum + tmp; // Little wolf of rounding error
111
intermediateSum[1] = (velvel - sum) - tmp;
112
intermediateSum[0] = velvel;
113
return intermediateSum;
114
}
115
116
// from OpenJDK 18 Collectors, unmodified
117
static double computeFinalSum(double[] summands) {
118
// Final sum with better error bounds subtract second summand as it is negated
119
double tmp = summands[0] - summands[1];
120
double simpleSum = summands[summands.length - 1];
121
if (Double.isNaN(tmp) && Double.isInfinite(simpleSum))
122
return simpleSum;
123
else
124
return tmp;
125
}
126
127
//Suppliers and consumers for Double Stream summation collection.
128
static Supplier<double[]> doubleSupplier = () -> new double[3];
129
static ObjDoubleConsumer<double[]> objDoubleConsumer = (double[] ll, double d) -> {
130
sumWithCompensation(ll, d);
131
ll[2] += d;
132
};
133
static BiConsumer<double[], double[]> badCollectorConsumer =
134
(ll, rr) -> {
135
sumWithCompensation(ll, rr[0]);
136
sumWithCompensation(ll, rr[1]);
137
ll[2] += rr[2];
138
};
139
140
static BiConsumer<double[], double[]> goodCollectorConsumer =
141
(ll, rr) -> {
142
sumWithCompensation(ll, rr[0]);
143
sumWithCompensation(ll, -rr[1]);
144
ll[2] += rr[2];
145
};
146
147
}
148