costreamjs
Version:
A high-performance streaming programming language for parallel architecture. This repo (js-version) is created for better using & reading & debugging.
520 lines (489 loc) • 20 kB
JavaScript
import { COStreamJS, setTop } from "../FrontEnd/global"
import { declareNode, function_definition, compositeNode, strdclNode, blockNode, operatorNode, fileReaderNode } from "../ast/node";
import { FlatNode } from "../FrontEnd/FlatNode"
import { StaticStreamGraph } from "../FrontEnd/StaticStreamGraph";
import { Partition } from "./Partition"
import { error } from "../utils";
import { fileWriterNode } from "../ast/node";
import "../ast/toJS"
export class WEBCodeGeneration {
constructor(nCpucore, ssg, mp) {
this.nCpucore = nCpucore
/**@type {StaticStreamGraph} */
this.ssg = ssg
/**@type {Partition} */
this.mp = mp
}
}
/** 生成stream流类型 */
WEBCodeGeneration.prototype.CGStreamData = function () {
var buf = ''
//遍历所有compositeNode的streamType,找到流中所有包含的数据类型,作为结构体streamData中的数据
//FIXME: 目前符号表未完成, 所以暂时假设所有的流都是同一种数据类型,因此只取一个 streamDcl 来填充至 streamData
var typeSet = []
for (let comp of COStreamJS.ast.filter(node => node instanceof compositeNode)) {
for (let stmt of comp.body.stmt_list) {
if (stmt instanceof declareNode && stmt.type instanceof strdclNode) {
typeSet = typeSet.concat(stmt.type.id_list)
}
}
}
//数组去重 由[{type:'int', identifier:'x'}] 先转为字符串形式的 [ 'int x' ] 来完成去重, 再给后面调用
typeSet = typeSet.map(o => o.type + ' ' + o.identifier)
typeSet = [...new Set(typeSet)]
typeSet = typeSet.map(str => ({ type: str.match(/\S+/g)[0], identifier: str.match(/\S+/g)[1] }))
//写入数据流数据类型结构体
buf += "class StreamData{\n constructor() {\n"
for (let it of typeSet) {
buf += `/* ${it.type} */ this.${it.identifier} = undefined;\n`
}
buf += "}\n}\n"
COStreamJS.files['Global.js'] = buf.beautify()
}
WEBCodeGeneration.prototype.CGGlobalvar = function () {
var buf =
`/*---------------------------*/
/* 主流程开始 */
/*---------------------------*/
`;
for (let node of COStreamJS.ast) {
if (node instanceof declareNode) {
buf += node.toJS() + ';\n'
}
}
COStreamJS.files['GlobalVar.cpp'] = buf.beautify()
}
/**
* 生成 Global.cpp 用于存储边的信息
*/
WEBCodeGeneration.prototype.CGGlobal = function () {
var buf = '/* Buffer<StreamData> */\n';
for (let flat of this.ssg.flatNodes) {
for (let out of flat.outFlatNodes) {
let stageminus = out.stageNum - flat.stageNum //发送方和接受方的软件流水阶段差
let edgePos = flat.outFlatNodes.indexOf(out) // out 在 flat 的输出边的序号
let perSteadyPushCount = flat.steadyCount * flat.outPushWeights[edgePos]//发送actor每次调用steadywork需要push的个数
let copySize = 0, copyStartPos = 0; //拷贝的数据大小,copy存放的开始位置
let inEdgeIndex = out.inFlatNodes.indexOf(flat) // out节点中 flat 对应的这条边的下标
let perWorkPeekCount = out.inPeekWeights[inEdgeIndex] //接收边actor每次peek的个数,b
let perWorkPopCount = out.inPopWeights[inEdgeIndex]; //接收边actor每次调用work需要pop的个数
let init1 = flat.initCount * flat.outPushWeights[edgePos] //发送actor调用initwork产生的数据量
let init2 = out.initCount * perWorkPopCount //接受actor的数据量
let size = init1 + perSteadyPushCount * (stageminus + 2) //缓冲区的大小
if (perWorkPeekCount === perWorkPopCount) {
if (perSteadyPushCount) {
copySize = (init1 - init2) % perSteadyPushCount
copyStartPos = init2 % perSteadyPushCount
}
} else {
//peek != pop 情况的特殊处理, 目前测试用例无此种情况, 需对这方面单独测试, 目前不保证下面5代码的正确性 FIXME
let leftnum = ((init1 - init2) % perSteadyPushCount + perSteadyPushCount - (perWorkPeekCount - perWorkPopCount) % perSteadyPushCount) % perSteadyPushCount;
copySize = leftnum + perWorkPeekCount - perWorkPopCount;
let addtime = copySize % perSteadyPushCount ? copySize / perSteadyPushCount + 1 : copySize / perSteadyPushCount;
copyStartPos = init2 % perSteadyPushCount;
size += addtime * perSteadyPushCount;
}
let edgename = flat.name + '_' + out.name
buf += `let ${edgename} = new Buffer(${size},${copySize},${copyStartPos});\n`
}
}
COStreamJS.files['Global.cpp'] = buf.beautify()
}
WEBCodeGeneration.prototype.CopyLib = function () {
const LIB = `
/** 工具函数 */
function isNumber(string) {
return string == +string;
}
function getNDArray(...args){
if(!args || !args.length) return 0;
return Array.from({ length: args[0] }).map(_=> getNDArray(...args.slice(1)))
}
/** 生产者消费者 Constructor */
class Buffer {
constructor(size,copySize,copyStartPos){
let buffer = Array.from({ length:size }).map(_ => new StreamData());
buffer.bufferSize = size;
buffer.copySize = copySize;
buffer.copyStartPos = copyStartPos;
buffer.writePos = 0;
return buffer;
}
}
class Consumer {
constructor(/* Buffer<T> & */conBuffer)
{
this.conBuffer = conBuffer
this.head = 0;
return new Proxy(this, {
get: function(target, propKey) {
if(isNumber(propKey)){
return target.conBuffer[target.head + parseInt(propKey)]
}
return target[propKey];
},
set: function(target, propKey, value) {
if(isNumber(propKey)){
target.conBuffer[target.head + parseInt(propKey)] = value
return true
}
target[propKey] = value;
return true
}
});
}
updatehead(offset){
this.head += offset;
}
resetHead(){
this.head = 0
}
}
class Producer {
constructor(/* Buffer<T> & */proBuffer)
{
this.proBuffer = proBuffer
this.tail = 0;
return new Proxy(this, {
get: function(target, propKey) {
if(isNumber(propKey)){
return target.proBuffer[target.tail + parseInt(propKey)]
}
return target[propKey];
},
set: function(target, propKey, value) {
if(isNumber(propKey)){
target.proBuffer[target.tail + parseInt(propKey)] = value
return true
}
target[propKey] = value;
return true
}
});
}
updatetail(offset){
this.tail += offset;
}
resetTail(){
this.tail = 0
}
}
`
COStreamJS.files['lib.js'] = LIB
}
/**
* 因为 WEB 是单线程, 直接生成程序主流程
*/
WEBCodeGeneration.prototype.CGMain = function CGMain() {
var buf = ''
let MaxStageNum = COStreamJS.MaxStageNum
buf = `\n/** 构建 operator Instance */\n`
this.ssg.flatNodes.forEach(flat => {
//准备构造如下格式的声明语句: const Name = new PreName(out1,out2,in1,in2);
buf += `const ${flat.name} = new ${flat.PreName}(`
let streamNames = []
flat.outFlatNodes.forEach(out => {
let edgename = flat.name + '_' + out.name
streamNames.push(edgename)
})
flat.inFlatNodes.forEach(src => {
let edgename = src.name + '_' + flat.name
streamNames.push(edgename)
})
buf += streamNames.join(',') + ','
buf += flat.steadyCount + ',' + flat.initCount;
flat.params.length > 0 ? buf += ',' + flat.params.join(',') : '';
buf += ');'
buf += '\n'
})
buf += `\n/** 数据流执行过程 */\nconst MAX_ITER = 1;\n`
const constant_array = [1].concat(Array(MaxStageNum-1).fill(0)) // 得到这样的数组: [1,0,0,...,0] 长度为阶段数
buf += `const stage = [${constant_array.join()}];\n`
//生成初态的 initWork 对应的 for 循环
let initFor = `
for(let _stageNum = 0; _stageNum < ${MaxStageNum}; _stageNum++){
#SLOT
}
`
var forBody = ''
for (let stage = 0 ; stage < MaxStageNum; stage++) {
let ifStr = `if(${stage} == _stageNum){`
//获取在这个 stage 上的 actor 集合
let flatVec = this.ssg.flatNodes.filter(flat => flat.stageNum == stage)
ifStr += flatVec.map(flat => flat.name + '.runInitScheduleWork();\n').join('') + '}\n'
forBody += ifStr
}
buf += initFor.replace('#SLOT', forBody)
//初态的 initWork 对应的 for 循环生成完毕
//生成稳态的 steadyWork 对应的 for 循环
let steadyFor = `
for(let _stageNum = ${MaxStageNum}; _stageNum < 2*${MaxStageNum}+MAX_ITER-1; _stageNum++){
#SLOT
}
`
var forBody = ''
for (let stage = 0 ; stage < MaxStageNum; stage++) {
let ifStr = `if(stage[${stage}]){`
//获取既在在这个 stage 上的 actor 集合
let flatVec = this.ssg.flatNodes.filter(flat => flat.stageNum == stage)
ifStr += flatVec.map(flat => flat.name + '.runSteadyScheduleWork();\n').join('') + '}\n'
forBody += ifStr
}
forBody +=
`for(let index=${MaxStageNum-1}; index>=1; --index){
stage[index] = stage[index-1];
}
if(_stageNum == MAX_ITER - 1 + ${MaxStageNum}){
stage[0] = 0;
}
`
buf += steadyFor.replace('#SLOT', forBody)
//稳态的 steadyWork 对应的 for 循环生成完毕
COStreamJS.files[`main.js`] = buf.beautify()
}
WEBCodeGeneration.prototype.Pack = function Pack(){
COStreamJS.files['main.cpp'] = Object.values(COStreamJS.files).join('\n').beautify();
}
/**
* 生成各个计算节点, 例如 class Source {}; class Sink {};
*/
WEBCodeGeneration.prototype.CGactors = function () {
var hasGenerated = new Set() //存放已经生成过的 FlatNode 的 PreName , 用来做去重操作
this.ssg.flatNodes.forEach(flat => {
// 先对 FileReader 进行特殊处理
if(flat.contents instanceof fileReaderNode || flat.contents instanceof fileWriterNode){
COStreamJS.files[`${flat.PreName}.h`] = this.cgFileActor(flat.contents);
return;
}
// 再处理普通 oper
if (hasGenerated.has(flat.PreName)) return
hasGenerated.add(flat.PreName)
var buf = ''
//开始构建 class
buf += `class ${flat.PreName}{\n`
/*写入类成员函数*/
let oper = flat.contents
let inEdgeNames = oper.inputs
let outEdgeNames = oper.outputs
buf += this.CGactorsConstructor(oper,inEdgeNames, outEdgeNames);
buf += this.CGactorsRunInitScheduleWork(inEdgeNames, outEdgeNames);
buf += this.CGactorsRunSteadyScheduleWork(inEdgeNames, outEdgeNames);
//写入init部分前的statement定义,调用tostring()函数,解析成规范的类变量定义格式
buf += this.CGactorsPopToken(oper);
buf += this.CGactorsPushToken(oper);
//init部分前的statement赋值
buf += this.CGactorsinitVarAndState(oper.operBody.stmt_list, oper);
buf += this.CGactorsInit(oper.operBody.init, flat);
buf += this.CGactorsWork(oper.operBody.work, flat);
/* 类体结束*/
buf += "}\n";
COStreamJS.files[`${flat.PreName}.h`] = buf.beautify()
})
}
WEBCodeGeneration.prototype.cgFileActor = function(fileNode){
if(fileNode instanceof fileReaderNode && /canvas/.test(fileNode.fileName)){
return `
class FileReader{
constructor(/* Buffer<StreamData>& */RAW,steadyC,initC){
this.steadyScheduleCount = steadyC;
this.initScheduleCount = initC;
this.RAW = new Producer(RAW);
}
runInitScheduleWork() {}
runSteadyScheduleWork() {
for (let i = 0; i < this.steadyScheduleCount; i++) {
this.work();
}
this.RAW.resetTail();
}
popToken(){ }
pushToken(){
this.RAW.updatetail(784);
}
work(){
let i=0;
window.getCanvasData();
for(i=0;i<784;i++)
this.RAW[i].x= window.img28[i];
this.pushToken();
this.popToken();
}
}`
}else if(fileNode instanceof fileWriterNode && /chart/.test(fileNode.fileName)){
return `
class FileWriter{
constructor(/* Buffer<StreamData>& */Out,steadyC,initC){
this.steadyScheduleCount = steadyC;
this.initScheduleCount = initC;
this.Out = new Consumer(Out);
}
runInitScheduleWork() {}
runSteadyScheduleWork() {
for (let i = 0; i < this.steadyScheduleCount; i++) {
this.work();
}
this.Out.resetHead();
}
popToken(){
this.Out.updatehead(10);
}
pushToken(){}
work(){
let i=0, arr= [];
for(i=0;i<10;i++)
arr.push(this.Out[i].x)
window.update_data(arr) //调用index.html中提供的更新图表的接口
this.pushToken();
this.popToken();
}
}`
}
throw new Error('WEB端暂不支持该类型的FileReader or FileWriter')
}
/**
* 生成actors constructor
* @example
* constructor(Source_0_B_1, steadyC,initC, param1 ) {
* this.steadyScheduleCount = steadyC;
* this.initScheduleCount = initC;
* this.Source_0_B_1 = new Producer(Source_0_B_1)
* this.paramName1 = param1;
* this.i = 0
* }
**/
WEBCodeGeneration.prototype.CGactorsConstructor = function(/** @type {operatorNode} */oper,inEdgeNames, outEdgeNames) {
let paramNames = oper._symbol_table.prev.paramNames
var OutAndInEdges = (outEdgeNames || []).concat(inEdgeNames) // 把 out 放前面, in 放后面
var buf = 'constructor(/* Buffer<StreamData>& */'
buf += OutAndInEdges.join(',')
buf += ',steadyC,initC';
paramNames.length ? buf += ',' + paramNames.join(',') : '';
buf += '){'
buf += `
this.steadyScheduleCount = steadyC;
this.initScheduleCount = initC;
${inEdgeNames.map(src => `this.${src} = new Consumer(${src});`).join('\n')}
${outEdgeNames.map(out => `this.${out} = new Producer(${out});`).join('\n')}
${paramNames.map(param => `this.${param} = ${param};`).join('\n')}
`
if(oper._symbol_table){
for(let name of Object.keys(oper._symbol_table.memberTable)){
let variable = oper._symbol_table.memberTable[name];
// 若该成员变量被声明为数组类型
if(variable.shape && variable.shape.join('') !== '11'){
var initializer = `getNDArray(${variable.shape.join(',')})`
}
// 非数组类型
else{
var initializer = variable.value;
}
buf += `this.${name} = ${initializer};\n`
}
}
return buf+'}'
}
WEBCodeGeneration.prototype.CGactorsRunInitScheduleWork = function (inEdgeNames, outEdgeNames) {
var buf = `
runInitScheduleWork() {
this.initVarAndState();
this.init();
for (let i = 0; i < this.initScheduleCount; i++) {
this.work();
}
`;
(outEdgeNames || []).forEach(out => buf += 'this.' + out + '.resetTail();\n');
(inEdgeNames || []).forEach(src => buf += 'this.' + src + '.resetHead();\n');
return buf + '}\n'
}
WEBCodeGeneration.prototype.CGactorsRunSteadyScheduleWork = function(inEdgeNames, outEdgeNames) {
var buf = `
runSteadyScheduleWork() {
for (let i = 0; i < this.steadyScheduleCount; i++) {
this.work();
}
`;
(outEdgeNames || []).forEach(out => buf += 'this.' + out + '.resetTail();\n');
(inEdgeNames || []).forEach(src => buf += 'this.' + src + '.resetHead();\n');
return buf + '}\n'
}
/**
* 生成 class 的 popToken 函数, 例如
* popToken() {
* this.Rstream0_0.updatehead(1);
* this.Rstream0_1.updatehead(1);
* }
* @param {operatorNode} oper
*/
WEBCodeGeneration.prototype.CGactorsPopToken = function (oper) {
const stmts = [];
(oper.operBody.win||[]).forEach(winStmt =>{
if(winStmt.type == 'sliding'){
let pop = winStmt.arg_list[0].toString()
oper._symbol_table.prev.paramNames.forEach(name =>{
const reg = new RegExp(`\\b(?<!\\.)${name}\\b`, 'g')
pop = pop.replace(reg, 'this.'+name)
})
stmts.push(`this.${winStmt.winName}.updatehead(${pop});`)
}
})
return `\n popToken(){ ${stmts.join('\n')} }\n`
}
/**
* 生成 class 的 pushToken 函数, 例如
* pushToken() {
* this.Dstream0_1.updatetail(2);
* }
* @param {FlatNode} flat
*/
WEBCodeGeneration.prototype.CGactorsPushToken = function (oper) {
const stmts = [];
(oper.operBody.win||[]).forEach(winStmt =>{
if(winStmt.type == 'tumbling'){
let push = winStmt.arg_list[0].toString()
oper._symbol_table.prev.paramNames.forEach(name =>{
const reg = new RegExp(`\\b(?<!\\.)${name}\\b`, 'g')
push = push.replace(reg, 'this.'+name)
})
stmts.push(`this.${winStmt.winName}.updatetail(${push});`)
}
})
return `\n pushToken(){ ${stmts.join('\n')} }\n`
}
/**
* 将 stmt_list 中的 let i=0部分转换为 this.i=0;
* @param {declareNode[]} stmt_list
*/
WEBCodeGeneration.prototype.CGactorsinitVarAndState = function (stmt_list, oper){
// 基于符号表来把 变量名 转化为 string
setTop(oper._symbol_table)
var result = 'initVarAndState() {'
stmt_list.forEach( declare =>{
declare.init_declarator_list.forEach(item =>{
if(item.initializer){
result += item.identifier.toJS() + '=' + item.initializer.toJS() +';\n'
}
})
})
return result+'}';
}
WEBCodeGeneration.prototype.CGactorsInit = function(init, flat){
// 基于符号表来把 init 转化为 string
if(init) setTop(init._symbol_table)
let buf = (init||'{ }').toJS();
return `init() ${buf} \n`
}
/**
* @param {blockNode} work
* @param {operatorNode} oper
*/
WEBCodeGeneration.prototype.CGactorsWork = function (work, flat){
// 基于符号表来把 work 转化为 string
// 将 work 的 toString 的头尾两个花括号去掉}, 例如 { cout << P[0].x << endl; } 变成 cout << P[0].x << endl;
setTop(work._symbol_table)
let innerWork = work.toJS().replace(/^\s*{/, '').replace(/}\s*$/, '')
return `work(){
${innerWork}
this.pushToken();
this.popToken();
}\n`
}