Interop parallelism requires needs awaiting on results. Blocking awaits are bad for performance. TFRT supports lightweight resumption on threads, and coroutines are an abstraction than can be used to lower the kernels onto TFRT threads.
Details
Diff Detail
- Repository
- rG LLVM Github Monorepo
Unit Tests
Event Timeline
mlir/include/mlir/Dialect/Async/Passes.td | ||
---|---|---|
47 | Why is this an option and not just the default? | |
mlir/lib/Dialect/Async/Transforms/AsyncToAsyncRuntime.cpp | ||
562 | This is repeating the function name, can you expand on how it is achieving this instead? | |
566 | Nit: you don't need to specify the size in SmallVector anymore. | |
572 | setResultAttrs and getResultAttrs aren't really efficient API. setAllResultAttrs and getAllResultAttrs should be able to do it better. But also, it seems that what you really want to do here is to say "add a token as the first return for the function", and there is an API for this on FuncOp I believe: /// Insert a single result of type `resultType` at `resultIndex`. void insertResult(unsigned resultIndex, Type resultType, DictionaryAttr resultAttrs) { | |
576 | Nit early-form reduces indentation: for (Block &block : func.getBlocks()) { if (&block != coro.suspend) continue; Operation *terminator = block.getTerminator(); ... | |
584 | Where is this invariant verified/enforced before getting into this point? | |
591 | ||
595 | Can you make this function (and all the others) static please | |
608 | Please favor for-range loop and avoid indexing when possible: SmallVector<Value> unwrappedResults; unwrappedResults.reserve(newCall->getResults().size() - 1); for (Value result : newCall.getResults().drop_front()) unwrappedResults.push_back(callBuilder.create<AwaitOp>(loc, result)); | |
610 | I don't understand this comment, it may miss some context: dangling pointer where? Is this referring to the comment in front of this function The invocation of this function is safe only when call ops are traversed in reverse order of how they appear in a single block. See funcsToCoroutines.? If so, it seems a bit out-of-place here (we're not careful in these two lines, only in the contract with this function and its caller which is documented on the API itself). | |
644 | walk() is recursive and would traverse all the ops inside the functions. | |
651–652 | (you'll see that LLVM/MLIR uses SmallVector for worklists in general, and because we don't support exceptions we have pop_back_val() :) ) | |
658 | You can save double-queries to maps when you know you want the entry inserted regardless: auto newMapEntry = outlinedFunctions.insert({func, CoroMachinery{}); if (!insertion.second) { // This function has already been processed because this is either // the corecursive case, or a caller with multiple calls to a newly // created corouting. Either way, skip updating the call sites. continue; } insertion.first->second = rewriteFuncAsCoroutine(func); | |
664 | ||
677 | I suspect this can fire on valid IR: can you make this a proper error handling (or if you want to keep an assert here, document the invariant and make it a runtime check and signalPassFailure() at the beginning of runOnOperation. |
mlir/include/mlir/Dialect/Async/Passes.td | ||
---|---|---|
47 | I should be the default once it will be tested, I'm +1 to have it as an option for now, so it can be quickly disabled without a need to push complex fixes. |
mlir/include/mlir/Dialect/Async/Passes.td | ||
---|---|---|
47 | OK, then please document it if the option is temporary for bringup :) |
mlir/lib/Dialect/Async/Transforms/AsyncToAsyncRuntime.cpp | ||
---|---|---|
570 | SmallVector<Type> resultTypes = {TokenType::get(ctx)} to skip func.insertResult below? |
Why is this an option and not just the default?