Skip to content

Commit d9f23dd

Browse files
authored
OpenCL/CPUContext: use the Common ForkJoinPool instead of a FixedThreadPool (#463)
* OpenCL/CPUContext: use the Common ForkJoinPool instead of a FixedThreadPool * OpenClPlatform/OpenCLOptions usage improvements * OpenCLOptions: convert to fluent builder
1 parent e239345 commit d9f23dd

File tree

9 files changed

+300
-64
lines changed

9 files changed

+300
-64
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
**/target/*
55
**/.DS_Store
66
/manual/public/
7+
.llvm
78

89
# Eclipse
910
.settings

classlib/bytecoder.opencl/src/main/java/de/mirkosertic/bytecoder/api/opencl/Float2.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ public String toString() {
5353
}
5454

5555
static Float2 normalize(final Float2 aVector) {
56-
throw new IllegalArgumentException("Not implemented for CPU emulation");
56+
float length = aVector.length();
57+
return float2(aVector.s0 / length, aVector.s1 / length);
5758
}
5859

5960
float length() {

classlib/bytecoder.opencl/src/main/java/de/mirkosertic/bytecoder/api/opencl/OpenCLOptions.java

+124-3
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,136 @@
1515
*/
1616
package de.mirkosertic.bytecoder.api.opencl;
1717

18+
import java.util.Comparator;
19+
import java.util.Objects;
20+
import java.util.function.Predicate;
21+
1822
public class OpenCLOptions {
1923

20-
private final boolean preferStackifier;
24+
public static enum CodeGeneratorStyle {
25+
26+
/**
27+
* The Stackifier tries to remove all GOTO statements and
28+
* replaces them with structured control flow elements and multi level break and continues.
29+
* <p>
30+
* The Stackifier does only work for reducible control flows and also does not support
31+
* exception handling. The generated output is smaller and in some cases faster compared
32+
* to the Relooper output.
33+
*/
34+
STACKIFIER,
35+
36+
/**
37+
* The Relooper output generator tries to recover high level control flow constructs
38+
* from the intermediate representation. This step eliminates the needs of GOTO
39+
* statements and thus allows generation of more natural source code, which in turn
40+
* can be easier read and optimized by Web Browsers or other tools.
41+
* <p>
42+
* The Relooper supports all styles of control flows and also supports exception handling.
43+
*/
44+
RELOOPER
45+
}
46+
47+
private final CodeGeneratorStyle codeGeneratorStyle;
48+
public CodeGeneratorStyle getCodeGeneratorStyle() {
49+
return codeGeneratorStyle;
50+
}
51+
52+
private final Predicate<PlatformProperties> platformFilter;
53+
public Predicate<PlatformProperties> getPlatformFilter() {
54+
return platformFilter;
55+
}
56+
57+
private final Comparator<DeviceProperties> preferredDeviceComparator;
58+
public Comparator<DeviceProperties> getPreferredDeviceComparator() {
59+
return preferredDeviceComparator;
60+
}
61+
62+
/**
63+
* @apiNote not exposed, use builder to create an instance
64+
*/
65+
private OpenCLOptions(
66+
final CodeGeneratorStyle codeGeneratorStyle,
67+
final Predicate<PlatformProperties> platformFilter,
68+
final Comparator<DeviceProperties> preferredDeviceComparator) {
69+
this.codeGeneratorStyle = codeGeneratorStyle;
70+
this.platformFilter = platformFilter;
71+
this.preferredDeviceComparator = preferredDeviceComparator;
72+
}
2173

74+
/**
75+
*
76+
* @deprecated use {@link OpenCLOptions#builder()} instead.
77+
*
78+
*/
79+
@Deprecated
2280
public OpenCLOptions(final boolean preferStackifier) {
23-
this.preferStackifier = preferStackifier;
81+
this.codeGeneratorStyle = preferStackifier
82+
? CodeGeneratorStyle.STACKIFIER
83+
: CodeGeneratorStyle.RELOOPER;
84+
final OpenCLOptions defaults = defaults();
85+
this.platformFilter = defaults.getPlatformFilter();
86+
this.preferredDeviceComparator = defaults.getPreferredDeviceComparator();
2487
}
2588

2689
public boolean isPreferStackifier() {
27-
return preferStackifier;
90+
return codeGeneratorStyle == CodeGeneratorStyle.STACKIFIER;
91+
}
92+
93+
// -- DEFAULTS
94+
95+
public static OpenCLOptions defaults() {
96+
return OpenCLOptions.builder().build();
2897
}
98+
99+
// -- OPTIONS BUILDER
100+
101+
public static final class OpenCLOptionsBuilder {
102+
private CodeGeneratorStyle codeGeneratorStyle = CodeGeneratorStyle.STACKIFIER;
103+
private Predicate<PlatformProperties> platformFilter = p -> true;
104+
private Comparator<DeviceProperties> preferredDeviceComparator = (a, b) -> Integer.compare(
105+
a.getNumberOfComputeUnits(),
106+
b.getNumberOfComputeUnits());
107+
108+
/**
109+
* There are different output styles available for generated code.
110+
* @param codeGeneratorStyle
111+
*/
112+
public OpenCLOptionsBuilder codeGeneratorStyle(CodeGeneratorStyle codeGeneratorStyle) {
113+
Objects.requireNonNull(codeGeneratorStyle);
114+
this.codeGeneratorStyle = codeGeneratorStyle;
115+
return this;
116+
}
117+
118+
/**
119+
* Platforms are rejected if the platformFilter predicate returns false.
120+
* @param platformFilter
121+
*/
122+
public OpenCLOptionsBuilder platformFilter(Predicate<PlatformProperties> platformFilter) {
123+
Objects.requireNonNull(platformFilter);
124+
this.platformFilter = platformFilter;
125+
return this;
126+
}
127+
128+
/**
129+
* The device that compares highest is chosen by the {@link PlatformFactory}, unless explicitly
130+
* overridden by system property {@code OPENCL_DEVICE}.
131+
* @param preferredPlatformComparator
132+
*/
133+
public OpenCLOptionsBuilder preferredDeviceComparator(Comparator<DeviceProperties> preferredDeviceComparator) {
134+
Objects.requireNonNull(preferredDeviceComparator);
135+
this.preferredDeviceComparator = preferredDeviceComparator;
136+
return this;
137+
}
138+
139+
public OpenCLOptions build() {
140+
return new OpenCLOptions(codeGeneratorStyle, platformFilter, preferredDeviceComparator);
141+
}
142+
}
143+
144+
public static OpenCLOptionsBuilder builder() {
145+
return new OpenCLOptionsBuilder();
146+
}
147+
148+
149+
29150
}

classlib/bytecoder.opencl/src/main/java/de/mirkosertic/bytecoder/api/opencl/PlatformFactory.java

+11
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,16 @@ public static PlatformFactory resolve() {
2626
return theLoader.iterator().next();
2727
}
2828

29+
/**
30+
* Creates a {@link Platform} with default {@link OpenCLOptions}
31+
* @param aLogger
32+
* @return
33+
*/
34+
public Platform createPlatform(final Logger aLogger) {
35+
return createPlatform(aLogger, OpenCLOptions.defaults());
36+
}
37+
2938
public abstract Platform createPlatform(Logger aLogger, OpenCLOptions aOptions);
39+
40+
3041
}

core/src/main/java/de/mirkosertic/bytecoder/backend/opencl/CPUContext.java

+16-34
Original file line numberDiff line numberDiff line change
@@ -18,57 +18,39 @@
1818
import static de.mirkosertic.bytecoder.api.opencl.GlobalFunctions.set_global_id;
1919
import static de.mirkosertic.bytecoder.api.opencl.GlobalFunctions.set_global_size;
2020

21-
import java.util.concurrent.CountDownLatch;
22-
import java.util.concurrent.ExecutorService;
23-
import java.util.concurrent.Executors;
24-
import java.util.concurrent.ThreadFactory;
21+
import java.util.stream.IntStream;
2522

23+
import de.mirkosertic.bytecoder.api.Logger;
2624
import de.mirkosertic.bytecoder.api.opencl.Context;
2725
import de.mirkosertic.bytecoder.api.opencl.Kernel;
28-
import de.mirkosertic.bytecoder.api.Logger;
2926

3027
public class CPUContext implements Context {
3128

32-
private final ExecutorService executorService;
3329
private final Logger logger;
3430

3531
public CPUContext(Logger aLogger) {
3632
logger = aLogger;
37-
executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), new ThreadFactory() {
38-
39-
int counter = 0;
40-
41-
@Override
42-
public Thread newThread(Runnable aRunnable) {
43-
return new Thread(aRunnable, "OpenCL-CPU#" + (counter ++));
44-
}
45-
});
4633
}
4734

4835
@Override
4936
public void compute(int aNumberOfStreams, Kernel aKernel) {
50-
CountDownLatch theLatch = new CountDownLatch(aNumberOfStreams);
51-
for (int i=0;i<aNumberOfStreams;i++) {
52-
final int theWorkItemId = i;
53-
executorService.submit(() -> {
54-
try {
55-
set_global_id(0, theWorkItemId);
56-
set_global_size(0, aNumberOfStreams);
57-
aKernel.processWorkItem();
58-
} finally {
59-
theLatch.countDown();
60-
}
61-
});
62-
}
63-
try {
64-
theLatch.await();
65-
} catch (InterruptedException e) {
66-
throw new IllegalStateException("Something went wrong", e);
67-
}
37+
38+
IntStream.range(0, aNumberOfStreams)
39+
.parallel()
40+
.forEach(workItemId->{
41+
try {
42+
set_global_size(0, aNumberOfStreams);
43+
set_global_id(0, workItemId);
44+
aKernel.processWorkItem();
45+
} catch (Exception e) {
46+
throw new IllegalStateException("Kernel execution (single work item) failed.", e);
47+
}
48+
}); // blocks until all work-items are complete
49+
6850
}
6951

7052
@Override
7153
public void close() {
72-
executorService.shutdownNow();
54+
// no-op
7355
}
7456
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* Copyright 2018 Mirko Sertic
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package de.mirkosertic.bytecoder.backend.opencl;
17+
18+
import static de.mirkosertic.bytecoder.api.opencl.GlobalFunctions.set_global_id;
19+
import static de.mirkosertic.bytecoder.api.opencl.GlobalFunctions.set_global_size;
20+
21+
import java.util.concurrent.CountDownLatch;
22+
import java.util.concurrent.ExecutorService;
23+
import java.util.concurrent.Executors;
24+
import java.util.concurrent.ThreadFactory;
25+
26+
import de.mirkosertic.bytecoder.api.opencl.Context;
27+
import de.mirkosertic.bytecoder.api.opencl.Kernel;
28+
import de.mirkosertic.bytecoder.api.Logger;
29+
30+
public class CPUContextUsingFixedThreadPool implements Context {
31+
32+
private final ExecutorService executorService;
33+
private final Logger logger;
34+
35+
public CPUContextUsingFixedThreadPool(Logger aLogger) {
36+
logger = aLogger;
37+
executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), new ThreadFactory() {
38+
39+
int counter = 0;
40+
41+
@Override
42+
public Thread newThread(Runnable aRunnable) {
43+
return new Thread(aRunnable, "OpenCL-CPU#" + (counter ++));
44+
}
45+
});
46+
}
47+
48+
@Override
49+
public void compute(int aNumberOfStreams, Kernel aKernel) {
50+
CountDownLatch theLatch = new CountDownLatch(aNumberOfStreams);
51+
for (int i=0;i<aNumberOfStreams;i++) {
52+
final int theWorkItemId = i;
53+
executorService.submit(() -> {
54+
try {
55+
set_global_id(0, theWorkItemId);
56+
set_global_size(0, aNumberOfStreams);
57+
aKernel.processWorkItem();
58+
} finally {
59+
theLatch.countDown();
60+
}
61+
});
62+
}
63+
try {
64+
theLatch.await();
65+
} catch (InterruptedException e) {
66+
throw new IllegalStateException("Something went wrong", e);
67+
}
68+
}
69+
70+
@Override
71+
public void close() {
72+
executorService.shutdownNow();
73+
}
74+
}

core/src/main/java/de/mirkosertic/bytecoder/backend/opencl/CPUPlatform.java

+15-2
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,31 @@
1515
*/
1616
package de.mirkosertic.bytecoder.backend.opencl;
1717

18+
import java.util.Optional;
19+
import java.util.function.Function;
20+
21+
import de.mirkosertic.bytecoder.api.Logger;
1822
import de.mirkosertic.bytecoder.api.opencl.Context;
1923
import de.mirkosertic.bytecoder.api.opencl.DeviceProperties;
2024
import de.mirkosertic.bytecoder.api.opencl.Platform;
2125
import de.mirkosertic.bytecoder.api.opencl.PlatformProperties;
22-
import de.mirkosertic.bytecoder.api.Logger;
2326

2427
public class CPUPlatform implements Platform {
2528

2629
private final PlatformProperties platformProperties;
2730
private final DeviceProperties deviceProperties;
2831
private final Logger logger;
32+
private final Function<Logger, Context> cpuContextFactory;
2933

3034
public CPUPlatform(Logger aLogger) {
35+
this(aLogger, CPUContext::new);
36+
}
37+
38+
/**
39+
* @param aLogger
40+
* @param cpuContextFactory - custom cpuContextFactory eg. {@link CPUContextUsingFixedThreadPool::new}
41+
*/
42+
public CPUPlatform(Logger aLogger, Function<Logger, Context> cpuContextFactory) {
3143
logger = aLogger;
3244
platformProperties = () -> "JVM Emulation";
3345
deviceProperties = new DeviceProperties() {
@@ -56,11 +68,12 @@ public long getClockFrequency() {
5668
return 1;
5769
}
5870
};
71+
this.cpuContextFactory = Optional.ofNullable(cpuContextFactory).orElse(CPUContext::new);
5972
}
6073

6174
@Override
6275
public Context createContext() {
63-
return new CPUContext(logger);
76+
return cpuContextFactory.apply(logger);
6477
}
6578

6679
@Override

0 commit comments

Comments
 (0)