Skip to content

Commit c8fa059

Browse files
authored
Gpu remove redundant copies (#113)
* [GPU] Avoid redundant data transfers depending on the type of access required in a kernel (read/write/read-write) * [GPU] Added simple heuristic to detect cases where data are already in the device (because of a previous operation). Currently it's quite conservative as it does not track memref aliases and will just copy if an alias is detected.
1 parent 89aaa1d commit c8fa059

3 files changed

Lines changed: 148 additions & 11 deletions

File tree

lib/Conversion/BlockedGpuToTriton/BlockedGpuToTriton.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,8 @@ class ConvertGpuFuncToTritonFunc : public OpConversionPattern<mlir::gpu::GPUFunc
103103
rewriter.create<func::ReturnOp>(gpuFunc.getLoc());
104104
newFunc->setAttr(gpu::GPUDialect::getKernelFuncAttrName(),
105105
rewriter.getUnitAttr());
106-
106+
newFunc.setArgAttrsAttr(gpuFunc.getArgAttrsAttr());
107+
107108
return success();
108109
}
109110

lib/Conversion/GpuToBlockedGpu/GpuToBlockedGpu.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,40 @@ class ConvertGpuToBlockedGpu: public CometGpuToBlockedGpuBase<ConvertGpuToBlocke
197197
mlir::gpu::GPUFuncOp funcOp = getOperation();
198198
mlir::OpBuilder builder(funcOp);
199199

200+
for(auto memrefArg : funcOp.getArguments())
201+
{
202+
if(mlir::isa<mlir::MemRefType>(memrefArg.getType()))
203+
{
204+
std::vector<Operation*> toExamine;
205+
toExamine.insert(toExamine.end(), memrefArg.getUsers().begin(), memrefArg.getUsers().end());
206+
for(size_t i = 0; i < toExamine.size(); i++)
207+
{
208+
auto user = toExamine[i];
209+
if(mlir::isa<mlir::memref::LoadOp>(user))
210+
{
211+
funcOp.setArgAttr(memrefArg.getArgNumber(), "gpu.read", builder.getUnitAttr());
212+
}
213+
else if(mlir::isa<mlir::memref::StoreOp>(user))
214+
{
215+
funcOp.setArgAttr(memrefArg.getArgNumber(), "gpu.write", builder.getUnitAttr());
216+
}
217+
218+
if(user->getNumResults() > 0)
219+
{
220+
for(auto res: user->getResults())
221+
{
222+
if(isa<MemRefType>(res.getType()))
223+
{
224+
// If the result is a memref, we need to check its users as well
225+
// to see if it is used in a store or load operation
226+
toExamine.insert(toExamine.end(), res.getUsers().begin(), res.getUsers().end());
227+
}
228+
}
229+
}
230+
}
231+
}
232+
}
233+
200234
for(auto arg: funcOp.getArguments())
201235
{
202236
if(mlir::isa<mlir::MemRefType>(arg.getType()))

lib/Conversion/PrepareGpuHost/PrepareGpuHost.cpp

Lines changed: 112 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
#include "comet/Conversion/PrepareGpuHost/PrepareGpuHostPass.h"
2+
#include "mlir/Dialect/Func/IR/FuncOps.h"
3+
#include "mlir/IR/Attributes.h"
4+
#include "mlir/IR/Builders.h"
5+
#include "mlir/IR/BuiltinAttributes.h"
6+
#include "mlir/IR/Operation.h"
27
#include "mlir/Pass/Pass.h"
38
#include "mlir/Dialect/GPU/IR/GPUDialect.h"
49
#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
@@ -26,24 +31,28 @@ class PrepareGpuHost
2631

2732
std::map<std::string, Value> funcs;
2833
std::map<std::string, std::string> gpu_to_triton_kernel;
34+
std::map<std::string, func::FuncOp> gpu_name_to_funcOp;
2935
std::map<std::string, triton::FuncOp> triton_name_to_triton_func_op;
3036
auto gpuModules = modOp.getOps<gpu::GPUModuleOp>();
3137
for(auto gpuModuleOp: gpuModules)
3238
{
3339
auto funcOps = gpuModuleOp.getOps<mlir::func::FuncOp>();
3440
for(func::FuncOp funcOp: llvm::make_early_inc_range(funcOps))
3541
{
42+
std::map<size_t, std::vector<Attribute>> argsToSet;
3643
if(!funcOp->hasAttr(gpu::GPUDialect::getKernelFuncAttrName()))
3744
{
3845
continue;
3946
}
4047
builder.setInsertionPoint(funcOp);
4148
SmallVector<Type, 4> newTypes;
42-
for(auto argType: funcOp.getArgumentTypes())
49+
for(auto arg: funcOp.getArguments())
4350
{
51+
auto argType = arg.getType();
4452
newTypes.push_back(argType);
4553
if(MemRefType rankedType = dyn_cast<mlir::MemRefType>(argType))
4654
{
55+
argsToSet[newTypes.size() - 1] = {funcOp.getArgAttr(arg.getArgNumber(), "gpu.read"), funcOp.getArgAttr(arg.getArgNumber(), "gpu.write")};
4756
if(rankedType.hasRank())
4857
{
4958
newTypes.push_back(builder.getIndexType());
@@ -71,6 +80,18 @@ class PrepareGpuHost
7180
builder.create<func::ReturnOp>(funcOp.getLoc());
7281
newFunc->setAttr(gpu::GPUDialect::getKernelFuncAttrName(),
7382
builder.getUnitAttr());
83+
for(auto [argNumber, attrs]: argsToSet)
84+
{
85+
if(attrs[0])
86+
{
87+
newFunc.setArgAttr(argNumber, "gpu.read", attrs[0]);
88+
}
89+
if(attrs[1])
90+
{
91+
newFunc.setArgAttr(argNumber, "gpu.write", attrs[1]);
92+
}
93+
}
94+
gpu_name_to_funcOp[funcOp.getName().str()] = newFunc;
7495
funcOp->erase();
7596
}
7697
}
@@ -185,14 +206,24 @@ class PrepareGpuHost
185206
if (mlir::gpu::LaunchFuncOp launchOp =
186207
dyn_cast<mlir::gpu::LaunchFuncOp>(use.getOwner())) {
187208
builder.setInsertionPoint(launchOp);
188-
builder.create<mlir::gpu::MemcpyOp>(launchOp->getLoc(), TypeRange(),
189-
ValueRange(),
190-
gpuAlloc.getMemref(), alloc);
209+
int offset = launchOp->getNumOperands() - launchOp.getNumKernelOperands();
210+
int operNum = use.getOperandNumber() - offset;
211+
if(gpu_name_to_funcOp[launchOp.getKernelName().str()].getArgAttr(operNum, "gpu.read"))
212+
{
213+
auto gpuMemCpy = builder.create<mlir::gpu::MemcpyOp>(launchOp->getLoc(), TypeRange(),
214+
ValueRange(),
215+
gpuAlloc.getMemref(), alloc);
216+
gpuMemCpy->setAttr("gpu.read", builder.getUnitAttr());
217+
}
191218
use.set(gpuAlloc.getMemref());
192219
builder.setInsertionPointAfter(launchOp);
193-
builder.create<mlir::gpu::MemcpyOp>(launchOp->getLoc(), TypeRange(),
194-
ValueRange(), alloc,
195-
gpuAlloc.getMemref());
220+
if(gpu_name_to_funcOp[launchOp.getKernelName().str()].getArgAttr(operNum, "gpu.write"))
221+
{
222+
auto gpuMemCpy = builder.create<mlir::gpu::MemcpyOp>(launchOp->getLoc(), TypeRange(),
223+
ValueRange(), alloc,
224+
gpuAlloc.getMemref());
225+
gpuMemCpy->setAttr("gpu.write", builder.getUnitAttr());
226+
}
196227
}
197228
}
198229
}
@@ -214,11 +245,82 @@ class PrepareGpuHost
214245
gpuAllocs.push_back(gpuAllocOp);
215246
});
216247

217-
std::vector<mlir::gpu::MemcpyOp> gpuCopies;
218-
modOp->walk([&gpuCopies](mlir::gpu::MemcpyOp gpuCopy) {
219-
gpuCopies.push_back(gpuCopy);
248+
249+
std::map<void*, std::vector<Operation*>> memEffects;
250+
modOp->walk([&uniqueGpuAllocs, &memEffects](Operation* memEffect) {
251+
for(auto op: memEffect->getOperands())
252+
{
253+
if(uniqueGpuAllocs.find(op) != uniqueGpuAllocs.end())
254+
{
255+
memEffects[op.getAsOpaquePointer()].push_back(memEffect);
256+
}
257+
}
220258
});
221259

260+
for(auto& [memref, effects]: memEffects)
261+
{
262+
bool copyIn = true;
263+
std::vector<Operation*> copyDelete;
264+
for(size_t i = 0; i < effects.size(); i++)
265+
{
266+
if(mlir::gpu::MemcpyOp gpuCopy = mlir::dyn_cast<mlir::gpu::MemcpyOp>(effects[i]))
267+
{
268+
if(!copyIn & gpuCopy->hasAttr("gpu.read"))
269+
{
270+
gpuCopy->erase();
271+
}
272+
else if(gpuCopy->hasAttr("gpu.read"))
273+
{
274+
copyIn = false;
275+
}
276+
else if(gpuCopy->hasAttr("gpu.write"))
277+
{
278+
copyIn = false;
279+
copyDelete.push_back(gpuCopy);
280+
}
281+
}
282+
else if(mlir::memref::StoreOp store = mlir::dyn_cast<mlir::memref::StoreOp>(effects[i]))
283+
{
284+
copyIn = true;
285+
if(!copyDelete.empty())
286+
{
287+
copyDelete.pop_back();
288+
}
289+
}
290+
else if(mlir::memref::CopyOp copy = mlir::dyn_cast<mlir::memref::CopyOp>(effects[i]))
291+
{
292+
if(copy.getTarget().getAsOpaquePointer() == memref)
293+
{
294+
copyIn = true;
295+
}
296+
}
297+
else if(mlir::memref::LoadOp load = mlir::dyn_cast<mlir::memref::LoadOp>(effects[i]))
298+
{
299+
if(!copyDelete.empty())
300+
{
301+
copyDelete.pop_back();
302+
}
303+
}
304+
else if(isa<memref::ExtractStridedMetadataOp, memref::DimOp, memref::RankOp>(effects[i]))
305+
{
306+
continue;
307+
}
308+
else // Unknown operation, be conservative
309+
{
310+
copyIn = true;
311+
if(!copyDelete.empty())
312+
{
313+
copyDelete.pop_back();
314+
}
315+
}
316+
}
317+
318+
for(auto toDelete: copyDelete)
319+
{
320+
toDelete->erase();
321+
}
322+
}
323+
222324
}
223325
};
224326

0 commit comments

Comments
 (0)