1 /* 2 * Copyright (c) 2024, 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package hat; 26 27 28 import hat.backend.Backend; 29 import hat.buffer.Buffer; 30 import hat.buffer.BufferAllocator; 31 import hat.buffer.BufferTracker; 32 import hat.ifacemapper.BoundSchema; 33 import hat.ifacemapper.SegmentMapper; 34 import hat.optools.OpTk; 35 36 import java.lang.invoke.MethodHandles; 37 import java.lang.reflect.Method; 38 39 import jdk.incubator.code.Op; 40 import jdk.incubator.code.Quotable; 41 import jdk.incubator.code.Quoted; 42 import jdk.incubator.code.dialect.java.JavaOp; 43 44 import java.util.HashMap; 45 import java.util.Map; 46 import java.util.ServiceLoader; 47 import java.util.function.Consumer; 48 import java.util.function.Predicate; 49 50 import static hat.backend.Backend.FIRST; 51 52 53 /** 54 * This class provides the developer facing view of HAT, and wraps a <a href="backend/Backend.html">Backend</a> capable of 55 * executing <b>NDRange</b> style execution. 56 * <p/> 57 * An Accelerator is provided a <a href="java/lang/invoke/MethodHandles.Lookup.html">MethodHandles.Lookup</a> with visibility to the 58 * compute to be performed. 59 * <p/> 60 * As well we either a <a href="backend/Backend.html">Backend</a> directly 61 * <pre> 62 * Accelerator accelerator = 63 * new Accelerator(MethodHandles.lookup(), 64 * new JavaMultiThreadedBackend()); 65 * </pre> 66 * or a {@code java.util.function.Predicate<Backend>} which can be used to select the required {@code Backend} 67 * loaded via Javas ServiceLoader mechanism 68 * {@code} 69 * <pre> 70 * Accelerator accelerator = 71 * new Accelerator(MethodHandles.lookup(), 72 * be -> be.name().startsWith("OpenCL)); 73 * </pre>} 74 * 75 * @author Gary Frost 76 */ 77 public class Accelerator implements BufferAllocator, BufferTracker { 78 public MethodHandles.Lookup lookup; 79 public final Backend backend; 80 81 private final Map<Method, hat.ComputeContext> cache = new HashMap<>(); 82 83 public NDRange range(int max) { 84 NDRange ndRange = new NDRange(this); 85 ndRange.kid = new KernelContext(ndRange, max); 86 return ndRange; 87 } 88 89 public NDRange range(int maxX, int maxY) { 90 NDRange ndRange = new NDRange(this); 91 ndRange.kid = new KernelContext(ndRange, maxX, maxY); 92 return ndRange; 93 } 94 95 public NDRange range(int maxX, int maxY, int maxZ) { 96 NDRange ndRange = new NDRange(this); 97 ndRange.kid = new KernelContext(ndRange, maxX, maxY, maxZ); 98 return ndRange; 99 } 100 101 public NDRange range(ComputeRange computeRange) { 102 NDRange ndRange = new NDRange(this); 103 ndRange.kid = new KernelContext(ndRange, computeRange); 104 return ndRange; 105 } 106 107 protected Accelerator(MethodHandles.Lookup lookup, ServiceLoader.Provider<Backend> provider) { 108 this(lookup, provider.get()); 109 } 110 public Accelerator(MethodHandles.Lookup lookup) { 111 this(lookup, FIRST); 112 } 113 114 /** 115 * @param lookup 116 * @param backend 117 */ 118 public Accelerator(MethodHandles.Lookup lookup, Backend backend) { 119 this.lookup = lookup; 120 this.backend = backend; 121 } 122 123 /** 124 * @param lookup 125 * @param backendPredicate 126 */ 127 public Accelerator(MethodHandles.Lookup lookup, Predicate<Backend> backendPredicate) { 128 this(lookup, Backend.getBackend(backendPredicate)); 129 } 130 131 @Override 132 public <T extends Buffer> T allocate(SegmentMapper<T> segmentMapper, BoundSchema<T> boundShema) { 133 return backend.allocate(segmentMapper, boundShema); 134 } 135 136 @Override 137 public void preMutate(Buffer b) { 138 if (backend instanceof BufferTracker) { 139 ((BufferTracker) backend).preMutate(b); 140 } 141 } 142 143 @Override 144 public void postMutate(Buffer b) { 145 if (backend instanceof BufferTracker) { 146 ((BufferTracker) backend).postMutate(b); 147 } 148 } 149 150 @Override 151 public void preAccess(Buffer b) { 152 if (backend instanceof BufferTracker) { 153 ((BufferTracker) backend).preAccess(b); 154 } 155 } 156 157 @Override 158 public void postAccess(Buffer b) { 159 if (backend instanceof BufferTracker) { 160 ((BufferTracker) backend).postAccess(b); 161 } 162 } 163 164 /** 165 * An interface used for wrapping the compute entrypoint of work to be performed by the Accelerator. 166 * <p/> 167 * So given a ComputeClass such as... 168 * <pre> 169 * public class MyComputeClass { 170 * @ CodeReflection 171 * public static void addDeltaKernel(KernelContext kc, S32Array arrayOfInt, int delta) { 172 * arrayOfInt.array(kc.x, arrayOfInt.array(kc.x)+delta); 173 * } 174 * 175 * @ CodeReflection 176 * static public void doSomeWork(final ComputeContext cc, S32Array arrayOfInt) { 177 * } 178 * } 179 * </pre> 180 * The accelerator will be passed the doSomeWork entrypoint, wrapped in a {@code QuotableComputeContextConsumer} 181 * <pre> 182 * accelerator.compute(cc -> 183 * MyCompute.doSomeWork(cc, arrayOfInt) 184 * ); 185 * </pre> 186 */ 187 public interface QuotableComputeContextConsumer extends Quotable, Consumer<ComputeContext> { 188 } 189 190 /** 191 * This method provides the Accelerator with the {@code Compute Entrypoint} from a Compute class. 192 * <p> 193 * The entrypoint is wrapped in a <a href="QuotableComputeContextConsumer.html">QuotableComputeContextConsumer</a> lambda. 194 * 195 * <pre> 196 * accelerator.compute(cc -> 197 * MyCompute.doSomeWork(cc, intArray) 198 * ) 199 * </pre> 200 */ 201 public void compute(QuotableComputeContextConsumer quotableComputeContextConsumer) { 202 Quoted quoted = Op.ofQuotable(quotableComputeContextConsumer).orElseThrow(); 203 JavaOp.LambdaOp lambda = (JavaOp.LambdaOp) quoted.op(); 204 Method method = OpTk.methodOrThrow(lookup,OpTk.getQuotableTargetInvokeOpWrapper(lambda)); 205 // Create (or get cached) a compute context which closes over compute entryppint and reachable kernels. 206 // The models of all compute and kernel methods are passed to the backend during creation 207 // The backend may well mutate the models. 208 // It will also use this opportunity to generate ISA specific code for the kernels. 209 ComputeContext computeContext = cache.computeIfAbsent(method, (_) -> new ComputeContext(this, method)); 210 // Here we get the captured values from the Quotable 211 Object[] args = OpTk.getQuotableCapturedValues(lambda,quoted, method); 212 args[0] = computeContext; 213 // now ask the backend to execute 214 backend.dispatchCompute(computeContext, args); 215 } 216 }