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.LambdaOpWrapper; 35 import hat.optools.OpWrapper; 36 37 import java.lang.invoke.MethodHandles; 38 import java.lang.reflect.Method; 39 40 import jdk.incubator.code.Op; 41 import jdk.incubator.code.Quotable; 42 import jdk.incubator.code.Quoted; 43 import jdk.incubator.code.dialect.core.CoreOp; 44 import jdk.incubator.code.dialect.java.JavaOp; 45 46 import java.util.HashMap; 47 import java.util.Map; 48 import java.util.ServiceLoader; 49 import java.util.function.Consumer; 50 import java.util.function.Predicate; 51 52 import static hat.backend.Backend.FIRST; 53 54 /** 55 * This class provides the developer facing view of HAT, and wraps a <a href="backend/Backend.html">Backend</a> capable of 56 * executing <b>NDRange</b> style execution. 57 * <p/> 58 * An Accelerator is provided a <a href="java/lang/invoke/MethodHandles.Lookup.html">MethodHandles.Lookup</a> with visibility to the 59 * compute to be performed. 60 * <p/> 61 * As well we either a <a href="backend/Backend.html">Backend</a> directly 62 * <pre> 63 * Accelerator accelerator = 64 * new Accelerator(MethodHandles.lookup(), 65 * new JavaMultiThreadedBackend()); 66 * </pre> 67 * or a {@code java.util.function.Predicate<Backend>} which can be used to select the required {@code Backend} 68 * loaded via Javas ServiceLoader mechanism 69 * {@code} 70 * <pre> 71 * Accelerator accelerator = 72 * new Accelerator(MethodHandles.lookup(), 73 * be -> be.name().startsWith("OpenCL)); 74 * </pre>} 75 * 76 * @author Gary Frost 77 */ 78 public class Accelerator implements BufferAllocator, BufferTracker { 79 public MethodHandles.Lookup lookup; 80 public final Backend backend; 81 82 private final Map<Method, hat.ComputeContext> cache = new HashMap<>(); 83 84 public NDRange range(int max) { 85 NDRange ndRange = new NDRange(this); 86 ndRange.kid = new KernelContext(ndRange, max, 0); 87 return ndRange; 88 } 89 90 91 protected Accelerator(MethodHandles.Lookup lookup, ServiceLoader.Provider<Backend> provider) { 92 this(lookup, provider.get()); 93 } 94 public Accelerator(MethodHandles.Lookup lookup) { 95 this(lookup, FIRST); 96 } 97 98 /** 99 * @param lookup 100 * @param backend 101 */ 102 public Accelerator(MethodHandles.Lookup lookup, Backend backend) { 103 this.lookup = lookup; 104 this.backend = backend; 105 } 106 107 /** 108 * @param lookup 109 * @param backendPredicate 110 */ 111 public Accelerator(MethodHandles.Lookup lookup, Predicate<Backend> backendPredicate) { 112 this(lookup, Backend.getBackend(backendPredicate)); 113 } 114 115 @Override 116 public <T extends Buffer> T allocate(SegmentMapper<T> segmentMapper, BoundSchema<T> boundShema) { 117 return backend.allocate(segmentMapper, boundShema); 118 } 119 120 @Override 121 public void preMutate(Buffer b) { 122 if (backend instanceof BufferTracker) { 123 ((BufferTracker) backend).preMutate(b); 124 } 125 } 126 127 @Override 128 public void postMutate(Buffer b) { 129 if (backend instanceof BufferTracker) { 130 ((BufferTracker) backend).postMutate(b); 131 } 132 } 133 134 @Override 135 public void preAccess(Buffer b) { 136 if (backend instanceof BufferTracker) { 137 ((BufferTracker) backend).preAccess(b); 138 } 139 } 140 141 @Override 142 public void postAccess(Buffer b) { 143 if (backend instanceof BufferTracker) { 144 ((BufferTracker) backend).postAccess(b); 145 } 146 } 147 /* 148 @Override 149 public void preEscape(Buffer b) { 150 if (backend instanceof BufferTracker) { 151 ((BufferTracker) backend).preEscape(b); 152 } 153 } 154 155 @Override 156 public void postEscape(Buffer b) { 157 if (backend instanceof BufferTracker) { 158 ((BufferTracker) backend).postEscape(b); 159 } 160 } */ 161 162 /** 163 * An interface used for wrapping the compute entrypoint of work to be performed by the Accelerator. 164 * <p/> 165 * So given a ComputeClass such as... 166 * <pre> 167 * public class MyComputeClass { 168 * @ CodeReflection 169 * public static void addDeltaKernel(KernelContext kc, S32Array arrayOfInt, int delta) { 170 * arrayOfInt.array(kc.x, arrayOfInt.array(kc.x)+delta); 171 * } 172 * 173 * @ CodeReflection 174 * static public void doSomeWork(final ComputeContext cc, S32Array arrayOfInt) { 175 * } 176 * } 177 * </pre> 178 * The accelerator will be passed the doSomeWork entrypoint, wrapped in a {@code QuotableComputeContextConsumer} 179 * <pre> 180 * accelerator.compute(cc -> 181 * MyCompute.doSomeWork(cc, arrayOfInt) 182 * ); 183 * </pre> 184 */ 185 public interface QuotableComputeContextConsumer extends Quotable, Consumer<ComputeContext> { 186 } 187 188 /** 189 * This method provides the Accelerator with the {@code Compute Entrypoint} from a Compute class. 190 * <p> 191 * The entrypoint is wrapped in a <a href="QuotableComputeContextConsumer.html">QuotableComputeContextConsumer</a> lambda. 192 * 193 * <pre> 194 * accelerator.compute(cc -> 195 * MyCompute.doSomeWork(cc, intArray) 196 * ) 197 * </pre> 198 */ 199 200 public void compute(QuotableComputeContextConsumer quotableComputeContextConsumer) { 201 Quoted quoted = Op.ofQuotable(quotableComputeContextConsumer).orElseThrow(); 202 LambdaOpWrapper lambda = OpWrapper.wrap(lookup,(JavaOp.LambdaOp) quoted.op()); 203 Method method = lambda.getQuotableTargetMethod(); 204 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, (_) -> 210 new ComputeContext(this, method) 211 ); 212 // Here we get the captured values from the Quotable 213 Object[] args = lambda.getQuotableCapturedValues(quoted, method); 214 args[0] = computeContext; 215 216 // now ask the backend to execute 217 backend.dispatchCompute(computeContext, args); 218 } 219 }