UNPKG

costreamjs

Version:

A high-performance streaming programming language for parallel architecture. This repo (js-version) is created for better using & reading & debugging.

471 lines (428 loc) 21.2 kB
import { deepCloneWithoutCircle } from "../utils" import { COStreamJS } from "./global" import { addNode, parenNode, forNode, compositeCallNode, splitjoinNode, pipelineNode, ComInOutNode, compHeadNode, compBodyNode, compositeNode, binopNode, operatorNode, splitNode, roundrobinNode, duplicateNode, joinNode, constantNode, blockNode, declareNode, operBodyNode, winStmtNode, declarator, idNode, inOutdeclNode, strdclNode, unaryNode, activationLayerNode } from "../ast/node"; import { matrix_section, matrix_slice_pair, layerNode } from "../ast/node"; import { top, setTop } from "./global" import { SymbolTable } from "./symbol"; export class UnfoldComposite { constructor() { /** @type {number} 用于对展开的 pipeline spitjoin 的 name 添加序号 */ this.num = 0 /** @type {Array<{ compName: string, content:string }>} 用于保存展开结果的记录, 避免重复展开 */ this.cached = [] } /* 给与每一个不同的splitjoin或者pipeline节点不同的名字 */ MakeCompositeName(/*string*/ name) { return name + "_" + this.num++; } } /** * 对于如下形式的 pipeline * out = pipeline(in) { * add A(1); * add B(2); * add C(3); * } * 我们要生成的 composite 的样式为{ * composite pipeline_0( input stream<int x>S0, output stream<int x>S3){ * stream<int y>S1; * S1 = A(S0)(1); * stream<double z>S2; // 注: 不同的 composite 节点的输入输出流类型确实可能不一样 * S2 = B(S1)(2); * S3 = C(S2)(3); * } * 将该新生成的 composite 加入 COStreamJS.ast 以及符号表的 S.compTable 中 * 然后我们要返回的 compositeCallNode 的样式为 * out = pipeline_0(in); */ UnfoldComposite.prototype.UnfoldPipeline = function (/** @type {pipelineNode} */ node) { let call_list = compositeCallFlow(node.body_stmts, node) let compName = this.MakeCompositeName("pipeline") const inStrType = top.streamTable[ node.inputs[0] ].strType, outStrType = top.streamTable[ node.outputs[0] ].strType const input_list = [new inOutdeclNode(null,inStrType, 'S0')] const output_list = [new inOutdeclNode(null,outStrType, 'S'+call_list.length)] const inout = new ComInOutNode(null, input_list, output_list) const head = new compHeadNode(null, compName, inout) let stmt_list = generateBodyStmts() const body = new compBodyNode(null, null, stmt_list) const pipeline = new compositeNode(null, head, body) COStreamJS.ast.push(pipeline) COStreamJS.S.compTable[compName] = { composite: pipeline }; // 构造 compositeCallNode const compositeCall = new compositeCallNode(null,compName, node.inputs) compositeCall.outputs = node.outputs return compositeCall function generateBodyStmts() { let result = [] for (let i = 0; i < call_list.length; i++) { let compCall = call_list[i] const inputNames = ['S'+i], outputNames = ['S'+(i+1)] const comp = COStreamJS.S.compTable[compCall.compName].composite // 先检查要不要生成 stream<int y>S1; 这个语句. 只要不是最后一个 add 则都要生成 if(i < call_list.length - 1){ const outStrType = comp.inout.output_list[0].strType result.push(new declareNode(null, outStrType, outputNames)) // stream<int x>S1; } // 接着生成 S1 = A(S0)(param1); 这个语句 const params = compCall.params.map(exp => exp.value) let call = new compositeCallNode(null, compCall.compName,inputNames, params) call.outputs = outputNames const binop = new binopNode(null, 'S'+(i+1), '=', call) result.push(binop) } return result } } /** * 遍历splitjoin/pipeline结构中的statement,将compositecallNode加入到compositeCall_list中 */ export function compositeCallFlow(/*list<Node *> */ stmts) { let compositeCall_list = []; // 记录了 add composite(); 的列表 if (!stmts || stmts.length == 0) throw new Error("compositeCallFlow Error") stmts.forEach(stmt => { stmt instanceof addNode ? handlerAdd(stmt) : ''; stmt instanceof forNode ? handlerFor(stmt) : ''; }) return compositeCall_list function handlerAdd(add) { if (add.content instanceof compositeCallNode) { let copy = deepCloneWithoutCircle(add.content) copy.params = copy.params.map(exp => exp.value) compositeCall_list.push(copy) }else if(add.content instanceof layerNode){ let copy = deepCloneWithoutCircle(add.content) if(!(copy instanceof activationLayerNode)) copy.arg_list = copy.arg_list.map(exp => exp.value) compositeCall_list.push(copy) }else if (add.content instanceof splitjoinNode || add.content instanceof pipelineNode) { let copy = deepCloneWithoutCircle(add.content) compositeCall_list.push(copy) } } /** * 对一个静态 for 循环做循环展开, 目前没有符号表, 所以只考虑如下简单例子 * for(j= 1;j<10;i+=2) //对该例子会将其内部语句展开5次 */ function handlerFor(/** @type {forNode}*/ for_stmt) { /*获得for循环中的init,cond和next值 目前只处理for循环中数据是整型的情况 */ let itorName = for_stmt.init.left // 获取 for 循环迭代器的 iterator 的名字/初始值 top.setVariableValue(itorName, for_stmt.init.right.value) while(for_stmt.cond.value){ const innerCall_list = compositeCallFlow(for_stmt.statement.stmt_list) compositeCall_list = compositeCall_list.concat(innerCall_list) for_stmt.next.value; // 一般是执行 i++ } } } /** * 对于如下形式的 splitjoin * out = splitjoin(in) { * split duplicate(args); // 也可以是 roundrobin; * add A(1); * add B(2); * add pipeline(); * join roundrobin(); * } * 我们要生成的 composite 的样式为{ * composite duplicate_0( input stream<int x>In, output stream<int x>Out){ * stream<int y>S0,S1,S2,J0,J1,J2; * (S0,S1,S2) = duplicate(In){ ... }; // operator 内容参见 MakeDuplicateOperator * J0 = A(S0)(1); * J1 = B(S1)(2); * J2 = pipeline(S2); * Out = join(J0,J1,J2){ ... }; // operator 内容参见 MakeJoinOperator * } * 将该新生成的 composite 加入 COStreamJS.ast 以及符号表的 S.compTable 中 * 然后我们要返回的 compositeCallNode 的样式为 * out = duplicate_0(in); * * @param {splitjoinNode} node - 待展开的 splitjoinNode * @returns {compositeCallNode} 展开完成的 */ UnfoldComposite.prototype.UnfoldSplitJoin = function (node) { setTop(new SymbolTable(top, null)) // 对生成的新 composite 构建新的符号表 let compName = this.MakeCompositeName("splitjoin"); let call_list = compositeCallFlow(node.body_stmts); const strType = top.prev.streamTable[node.inputs[0]].strType // 这里也简单默认输入输出数据流类型一致, 若有不一致的需求, 应修改此处代码 const head_input = new inOutdeclNode(null, strType, "In") const head_output = new inOutdeclNode(null, strType, "Out") let inout = new ComInOutNode(null, [head_input], [head_output]) let head = new compHeadNode(null, compName, inout) // 构建头部完成 var stmt_list = this.generateDuplicateOrRoundrobinBodyStmts(node, node.split.type, call_list); let body = new compBodyNode(null, null, stmt_list) let splitjoin = new compositeNode(null, head, body) // 已生成该新的 compositeNode // 将新生成的 compositeNode 插回到语法树和符号表中 COStreamJS.ast.push(splitjoin) COStreamJS.S.compTable[compName] = { composite: splitjoin }; // 构造 compositeCallNode const compositeCall = new compositeCallNode(null,compName, node.inputs) compositeCall.outputs = node.outputs setTop(top.prev) // 还原至上层符号表 return compositeCall } /** * 目标生成的结构: * stream<int y>S0,S1,S2,J0,J1,J2; * (S0,S1,S2) = duplicate(In){ ... }; // operator 内容参见 MakeDuplicateOperator * J0 = A(S0)(1); * J1 = B(S1)(2); * J2 = pipeline(S2); * Out = join(J0,J1,J2){ ... }; // operator 内容参见 MakeJoinOperator * @param {splitjoinNode} node * @param {Array<compositeCallNode|splitjoinNode|pipelineNode>} call_list * @returns {statement[]} */ UnfoldComposite.prototype.generateDuplicateOrRoundrobinBodyStmts = function (node, type = "duplicate", call_list) { let result = [], currentNum = this.num /** 这里要把当前的序号保存下来 达到"成对"生成oper名字的目的 * duplicate_0 * roundrobin_1 * join_1 * join_0 */ //0.先提前设置好流变量名 let splitStreams = Array.from({ length: call_list.length }).map((_, idx) => "S" + idx) let joinStreams = Array.from({ length: call_list.length }).map((_, idx) => "J" + idx) //1. 构建流变量声明节点 stream<int y>S0,S1,S2,J0,J1,J2; const strType = top.prev.streamTable[ node.inputs[0] ].strType; // 注: 这里默认过程中的数据流类型都相同, 若有不同可修改此处代码 [...splitStreams, ...joinStreams,"In","Out"].forEach(strName => top.streamTable[strName] = { strType }) // 为新声明的几个数据流名在符号表中注册类型 let declareStmt = new declareNode(null, strType, splitStreams.concat(joinStreams)) result.push(declareStmt); //2.构建 duplicateOrRoundrobin 节点 let duplicateOrRoundrobinOper = type === "duplicate" ? this.MakeDuplicateOperator(["In"], node.split.arg_list, splitStreams, currentNum) : this.MakeRoundrobinOperator(["In"], node.split.arg_list, splitStreams, currentNum) result.push(duplicateOrRoundrobinOper) //3.构建 body 中的对输入流的处理 for (let i = 0; i < call_list.length; i++) { let it = call_list[i] if (it instanceof compositeCallNode) { let call = new compositeCallNode(null, it.compName, [splitStreams[i]], it.params) call.outputs = [joinStreams[i]] let binop = new binopNode(null,splitStreams[i], '=', call) result.push(binop) } else if (it instanceof splitjoinNode || it instanceof pipelineNode) { /** 若为splitjoin或者pipeline结构,赋予其输入和输出流 * 例如之前是 add pipeline { * add A(); * add B(); * } * 将其转化为 Ji = pipeline_num(Si); // 这里额外执行一次 unfoldPipeline, 得到一个 compositeCallNode */ // 先去缓存中查找该结构是否已展开过 let hit = this.cached.find(record => record.content === it.toString()) if(hit){ var call = new compositeCallNode(null,hit.compName, [splitStreams[i]]) call.outputs = [joinStreams[i]] }else{ const needToCacheString = it.toString() it.inputs = [splitStreams[i]] it.outputs = [joinStreams[i]] var call = it instanceof splitjoinNode ? this.UnfoldSplitJoin(it) : this.UnfoldPipeline(it) this.cached.push({ compName: call.compName, content: needToCacheString }) } let binop = new binopNode(null, joinStreams[i], '=', call) result.push(binop) } } //4.构建 join 节点 result.push(this.MakeJoinOperator(joinStreams, node.split.arg_list, ["Out"],currentNum)) return result } /** * 构建出一个真实的 roundrobin 的 operatorNode, 该 operator 没有 stmt_list 和 init, 只有 work 和 window * 例如 * (S0,S1) = roundrobin(In) { * work{ * int i=0,j=0; * for(i=0;i<1;++i) S0[i]=In[j++]; * for(i=0;i<1;++i) S1[i]=In[j++]; * } * window{ * In sliding(2,2); * S0 tumbling(1); * S1 tumbling(1); * } * } * @returns {operatorNode} */ UnfoldComposite.prototype.MakeRoundrobinOperator = function (inputs, args, outputs, num) { /* duplicate 的参数被文法手册规定为全为1 * Roundrobin 的参数可不仅仅为1哦, 可以自定义哒 * 如果不指定参数, 则默认都为1 */ args = args || Array.from({ length: outputs.length }).fill(1) let work = MakeRoundrobinWork(inputs, args, outputs); let window = MakeRoundrobinWindow(inputs, args, outputs); let body = new operBodyNode(null, null, null, work, window) //没有 stmt_list 和 init,只有 work,window let oper = new operatorNode(null, `roundrobin_${num}`, inputs, body) oper.outputs = outputs let binop = new binopNode(null, new parenNode(null, outputs),'=',oper) return binop /** * 构建 Roundrobin 的 work 部分 * int i=0,j=0; * for(i=0;i<1;++i) S0[i]=In[j++]; * for(i=0;i<1;++i) S1[i]=In[j++]; */ function MakeRoundrobinWork(inputs, args, outputs) { const decl_i = new declarator(null,new idNode(null,'i'),'0') const decl_j = new declarator(null,new idNode(null,'j'),'0') const dNode = new declareNode(null, 'int',[decl_i,decl_j]) const stmts = [dNode]; // stmts = ["int i=0,j=0;"] outputs.forEach((name, idx) => { // 下面代码等价于 stmts.push(`for(i=0;i<${args[idx]};++i) ${name}[i] = ${inputs[0]}[j++];`) const init = new binopNode(null,'i','=', new constantNode(null,'0')) const cond = new binopNode(null, 'i','<',new constantNode(null,args[idx])) const next = new unaryNode(null, '++', 'i') const binop_left = new matrix_section(null, name, [new matrix_slice_pair(null,'i')]) const binop_righ = new matrix_section(null, inputs[0], [new matrix_slice_pair(null,'j++')]) const statement = new binopNode(null, binop_left, '=', binop_righ) stmts.push(new forNode(null, init, cond, next, statement)) }) let work = new blockNode(null, '{', stmts, '}') return work } /** * 构建 Roundrobin 的 window 部分 * In sliding(2,2); * S0 tumbling(1); * S1 tumbling(1); */ function MakeRoundrobinWindow(inputs, args, outputs) { //1. 构建 In sliding(2,2); let sum = args.map(arg=>parseInt(arg)).reduce((a, b) => a + b) let arg_list = [sum, sum].map(num => new constantNode(null, num)) //Roundrobin 的参数可不仅仅为1哦, 可以自定义哒 let winStmts = [new winStmtNode(null, inputs[0], { type: 'sliding', arg_list })] //2. 循环构建 Out tumbling(1); outputs.forEach((name, idx) => { let arg_list = [new constantNode(null, args[idx])] winStmts.push(new winStmtNode(null, name, { type: 'tumbling', arg_list })) }) return winStmts } } /** * 构建出一个真实的 duplicate 的 operatorNode, 该 operator 没有 stmt_list 和 init, 只有 work 和 window * 例如 * (Out1,Out2,Out3) = duplicate(In) { * work{ * int i=0; * for(i=0;i<1;++i) Out1[i]=In[i]; * for(i=0;i<1;++i) Out2[i]=In[i]; * for(i=0;i<1;++i) Out3[i]=In[i]; * } * window{ * In sliding(1,1); * Out1 tumbling(1); * Out2 tumbling(1); * Out3 tumbling(1); * } * } * @returns {operatorNode} */ UnfoldComposite.prototype.MakeDuplicateOperator = function (inputs, args, outputs, num) { args = args || Array.from({ length: outputs.length }).fill(1) //使用默认全都是1 , 实际上split duplicate()在小括号中不允许输入参数 let work = MakeDuplicateWork(inputs, args, outputs); let window = MakeDuplicateWindow(inputs, args, outputs); let body = new operBodyNode(null, null, null, work, window) //没有 stmt_list 和 init,只有 work,window let res = new operatorNode(null, `duplicate_${num}`, inputs, body) res.outputs = outputs let binop = new binopNode(null, new parenNode(null,outputs), '=', res) return binop /** * 构建 duplicate 的 work 部分 */ function MakeDuplicateWork(inputs, args, outputs) { const decl = new declarator(null,new idNode(null,'i'),'0') const dNode = new declareNode(null, 'int',[decl]) const stmts = [dNode]; // let stmts = ["int i=0;"] outputs.forEach((name, idx) => { // 下面代码等价于 stmts.push(`for(i=0;i<${args[idx]};++i) ${name}[i] = ${inputs[0]}[i];`) const init = new binopNode(null,'i','=',new constantNode(null,'0')) const cond = new binopNode(null, 'i','<',new constantNode(null,args[idx])) const next = new unaryNode(null, '++', 'i') const binop_left = new matrix_section(null, name, [new matrix_slice_pair(null,'i')]) const binop_righ = new matrix_section(null, inputs[0], [new matrix_slice_pair(null,'i')]) const statement = new binopNode(null, binop_left, '=', binop_righ) stmts.push(new forNode(null, init, cond, next, statement)) }) let work = new blockNode(null, '{', stmts, '}') return work } function MakeDuplicateWindow(inputs, args, outputs) { //1. 构建 In sliding(1,1); let arg_list = [1, 1].map(num => new constantNode(null, num)) //duplicate 的参数被文法手册规定为1 let winStmts = [new winStmtNode(null, inputs[0], { type: 'sliding', arg_list })] //2. 循环构建 Out1 tumbling(1); outputs.forEach(name => { winStmts.push(new winStmtNode(null, name, { type: 'tumbling', arg_list: arg_list.slice(1) })) }) return winStmts } } /** * 构建出一个真实的 join 的 operatorNode, 该 operator 没有 stmt_list 和 init, 只有 work 和 window * 例如 * Out = join(In1,In2) { * work{ * int i=0; * int j=0; * for(i=0;i<1;++i) Out[j++]=In0[i]; * for(i=0;i<1;++i) Out[j++]=In1[i]; * for(i=0;i<1;++i) Out[j++]=In2[i]; * } * window{ * In0 sliding(1,1); * In1 sliding(1,1); * In2 sliding(1,1); * Out tumbling(3); * } * } * @returns {binopNode} */ UnfoldComposite.prototype.MakeJoinOperator = function (inputs, args, outputs, num) { args = args || Array.from({ length: inputs.length }).fill(1) //join roundrobin()在小括号中不输入参数的话默认全都是1 let work = MakeJoinWork(inputs, args, outputs); let window = MakeJoinWindow(inputs, args, outputs); let body = new operBodyNode(null, null, null, work, window) //没有 stmt_list 和 init,只有 work,window let res = new operatorNode(null, `join_${num}`, inputs, body) res.outputs = outputs let binop = new binopNode(null, outputs[0],'=',res) return binop /** * 构建 join 的 work 部分 */ function MakeJoinWork(inputs, args, outputs) { // 下面代码等价于 let stmts = ["int i=0,j=0;"] const decl_i = new declarator(null,new idNode(null,'i'),'0') const decl_j = new declarator(null,new idNode(null,'j'),'0') const dNode = new declareNode(null, 'int',[decl_i,decl_j]) const stmts = [dNode]; // let stmts = ["int i=0,j=0;"] inputs.forEach((name, idx) => { // 下面代码等价于 stmts.push(`for(i=0;i<${args[idx]};++i) ${outputs[0]}[j++] = ${name}[i];`) const init = new binopNode(null,'i','=',new constantNode(null,'0')) const cond = new binopNode(null, 'i','<',new constantNode(null,args[idx])) const next = new unaryNode(null, '++', 'i') const binop_left = new matrix_section(null, outputs[0], [new matrix_slice_pair(null,'j++')]) const binop_righ = new matrix_section(null, name, [new matrix_slice_pair(null,'i')]) const statement = new binopNode(null, binop_left, '=', binop_righ) stmts.push(new forNode(null, init, cond, next, statement)) }) let work = new blockNode(null, '{', stmts, '}') return work } function MakeJoinWindow(inputs, args, outputs) { //每行一个形如 In sliding(1,1) 的 winStmt let winStmts = inputs.map((name, idx) => { let arg_list = [args[idx], args[idx]].map(num => new constantNode(null, num)) //一般情况下为 sliding(1,1), 也兼容其它 arg. 转为 constantNode 为后续SetFlatNodesWeights做准备 return new winStmtNode(null, name, { type: 'sliding', arg_list }) }) //加入末尾的输出, 形如 Out tumbling(3) 其中的数字是 args 的总和 let sum = args.map(arg=>parseInt(arg)).reduce((a, b) => a + b) winStmts.push(new winStmtNode( null, outputs[0], { type: 'tumbling', arg_list: [new constantNode(null, sum)] }) ) return winStmts } }