From 60258daa0610c7bac5e9b73eb1be890a58289178 Mon Sep 17 00:00:00 2001 From: Du Li Date: Mon, 26 Apr 2021 17:48:20 -0700 Subject: [PATCH 1/7] bug fixing --- lib/backends/webgl/ops/conv-pack.ts | 13 ++++++------- lib/backends/webgl/ops/conv.ts | 17 +++++++++-------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/lib/backends/webgl/ops/conv-pack.ts b/lib/backends/webgl/ops/conv-pack.ts index b08104c7..414bf64d 100644 --- a/lib/backends/webgl/ops/conv-pack.ts +++ b/lib/backends/webgl/ops/conv-pack.ts @@ -38,8 +38,8 @@ export class WebGLConvPacked extends Conv { const matmul = new WebGLMatMulPacked(); const reshape = new WebGLReshapePacked(); // shape for kernel reshape - const shape = new Tensor( - [2], 'int32', undefined, undefined, new Float32Array([kshape[0], kshape[1] * kshape[2] * kshape[3]])); + const shape = + new Tensor([2], 'int32', undefined, undefined, new Int32Array([kshape[0], kshape[1] * kshape[2] * kshape[3]])); if (!this.artifacts) { this.artifacts = []; this.programInfo = []; @@ -78,12 +78,11 @@ export class WebGLConvPacked extends Conv { const matmulOutput = runDataMatmul.outputTextureData.tensor; // reshape output - const outputShapeTensor = new Tensor([outputShape.length], 'int32'); + const outputShapeTensor = new Tensor( + [outputShape.length], 'int32', undefined, undefined, + new Int32Array([outputShape[0], outputShape[1], outputShape[2], outputShape[3]])); - for (let i = 0; i < outputShape.length; i++) { - outputShapeTensor.data[i] = outputShape[i]; - } - assert(this.artifacts.length > 1, () => 'expect at least 3 artifacts created'); + assert(this.artifacts.length > 2, () => 'expect at least 3 artifacts created'); if (this.artifacts.length === 3) { this.programInfo[3] = reshape.createProgramInfo(inferenceHandler, [matmulOutput, outputShapeTensor]); this.artifacts[3] = programManager.build(this.programInfo[3]); diff --git a/lib/backends/webgl/ops/conv.ts b/lib/backends/webgl/ops/conv.ts index 62ec3509..0a99a62d 100644 --- a/lib/backends/webgl/ops/conv.ts +++ b/lib/backends/webgl/ops/conv.ts @@ -146,19 +146,19 @@ export class WebGLUnpackedConv extends Conv { const programManager = inferenceHandler.session.programManager; if (!this.artifacts) { this.artifacts = []; - const programInfos = this.createProgramInfo(inferenceHandler, inputs); + const programInfos = this.createProgramInfoArray(inferenceHandler, inputs); for (let i = 0; i < programInfos.length; ++i) { const artifact = inferenceHandler.session.programManager.build(programInfos[i]); this.artifacts.push(artifact); } } - const runDatas = this.createRunData(inferenceHandler, this.artifacts.map(a => a.programInfo), inputs); - inferenceHandler.checkAndUpdateTextureForm(this.artifacts[0], runDatas[0]); - programManager.run(this.artifacts[0], runDatas[0]); - programManager.run(this.artifacts[1], runDatas[1]); - return [runDatas[1].outputTextureData.tensor]; + const runDataArray = this.createRunDataArray(inferenceHandler, this.artifacts.map(a => a.programInfo), inputs); + inferenceHandler.checkAndUpdateTextureForm(this.artifacts[0], runDataArray[0]); + programManager.run(this.artifacts[0], runDataArray[0]); + programManager.run(this.artifacts[1], runDataArray[1]); + return [runDataArray[1].outputTextureData.tensor]; } - createProgramInfo(inferenceHandler: WebGLInferenceHandler, inputs: Tensor[]): ProgramInfo[] { + createProgramInfoArray(inferenceHandler: WebGLInferenceHandler, inputs: Tensor[]): ProgramInfo[] { const xshape = inputs[0].dims.slice(); const kshape = inputs[1].dims.slice(); // if kernelShape is not specified in the attributes of this op, infer it from the weight tensor dims @@ -180,7 +180,8 @@ export class WebGLUnpackedConv extends Conv { this.createDotProductProgramInfo(inferenceHandler, im2colProgramInfo.outputLayout, inputs, outputShape); return [im2colProgramInfo, dotProductProgramInfo]; } - createRunData(inferenceHandler: WebGLInferenceHandler, programInfos: ProgramInfo[], inputs: Tensor[]): RunData[] { + createRunDataArray(inferenceHandler: WebGLInferenceHandler, programInfos: ProgramInfo[], inputs: Tensor[]): + RunData[] { const k = inputs[1]; const b = inputs.length >= 3 ? inputs[2] : undefined; let kTD = inferenceHandler.getTextureData(k.dataId); From 785d37bcbfbfc18eb5244ce4d41b118c18c64223 Mon Sep 17 00:00:00 2001 From: Du Li Date: Mon, 3 May 2021 21:43:56 -0700 Subject: [PATCH 2/7] replace mod with imod to avoid strange numeric errors. --- lib/backends/webgl/ops/im2col-pack.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/backends/webgl/ops/im2col-pack.ts b/lib/backends/webgl/ops/im2col-pack.ts index 0681a1fd..e6b944d5 100644 --- a/lib/backends/webgl/ops/im2col-pack.ts +++ b/lib/backends/webgl/ops/im2col-pack.ts @@ -48,12 +48,11 @@ export class WebGLIm2ColPacked implements WebGLOperator { if(blockIndex < ${im2colShape[1]} && pos < ${im2colShape[0]}) { offsetY = int(blockIndex / (${this.convOutputShape[rank - 1]})) * ${this.strides[0]} - ${this.pads[1]}; - d0 = offsetY + ${this.dilations[0]} * (int(mod(float(pos), ${kernelSize}.)) / ${wshape[2]} ); + d0 = offsetY + ${this.dilations[0]} * (imod(pos, ${kernelSize}) / ${wshape[2]}); if(d0 < ${xshape[rowDim]} && d0 >= 0) { - offsetX = int(mod(float(blockIndex), ${this.convOutputShape[rank - 1]}.) * ${this.strides[1]}. - ${ - this.pads[0]}.); - d1 = offsetX + ${this.dilations[1]} * (int(mod(mod(float(pos), ${kernelSize}.), ${wshape[2]}.))); + offsetX = imod(blockIndex, ${this.convOutputShape[rank - 1]}) * ${this.strides[1]} - ${this.pads[0]}; + d1 = offsetX + ${this.dilations[1]} * imod(imod(pos, ${kernelSize}), ${wshape[2]}); if(d1 < ${xshape[colDim]} && d1 >= 0) { From f941136d22337624246c43523e734af81b534746 Mon Sep 17 00:00:00 2001 From: Du Li Date: Mon, 3 May 2021 23:39:06 -0700 Subject: [PATCH 3/7] repro reshape pack error --- .../backends/webgl/test_reshape_packed.ts | 141 +++++++++--------- 1 file changed, 73 insertions(+), 68 deletions(-) diff --git a/test/unittests/backends/webgl/test_reshape_packed.ts b/test/unittests/backends/webgl/test_reshape_packed.ts index 1f7bf3d8..f2139950 100644 --- a/test/unittests/backends/webgl/test_reshape_packed.ts +++ b/test/unittests/backends/webgl/test_reshape_packed.ts @@ -86,75 +86,80 @@ interface TestData { function getTestData(): TestData[] { return [ // test 2D tensor + /* { + elementCount: 16, + inputShape: [4, 4], + outputShape: [2, 8], + }, + { + elementCount: 16, + inputShape: [4, 4], + outputShape: [1, 16], + }, + { + elementCount: 8, + inputShape: [2, 4], + outputShape: [4, 2], + }, + { + elementCount: 8, + inputShape: [2, 4], + outputShape: [1, 8], + }, + { + elementCount: 6, + inputShape: [2, 3], + outputShape: [1, 6], + }, + { + elementCount: 6, + inputShape: [2, 3], + outputShape: [3, 2], + }, + + // test 3d tensor + { + elementCount: 16, + inputShape: [2, 2, 4], + outputShape: [4, 2, 2], + }, + { + elementCount: 16, + inputShape: [2, 2, 4], + outputShape: [2, 4, 2], + }, + { + elementCount: 16, + inputShape: [2, 2, 4], + outputShape: [1, 1, 2, 8], + }, + + // test 4d tensor + { + elementCount: 32, + inputShape: [2, 2, 2, 4], + outputShape: [4, 2, 2, 2], + }, + { + elementCount: 32, + inputShape: [2, 2, 2, 4], + outputShape: [2, 4, 2, 2], + }, + + { + elementCount: 32, + inputShape: [2, 2, 2, 4], + outputShape: [2, 2, 4, 2], + }, + { + elementCount: 32, + inputShape: [2, 2, 2, 4], + outputShape: [2, 1, 4, 4], + },*/ { - elementCount: 16, - inputShape: [4, 4], - outputShape: [2, 8], - }, - { - elementCount: 16, - inputShape: [4, 4], - outputShape: [1, 16], - }, - { - elementCount: 8, - inputShape: [2, 4], - outputShape: [4, 2], - }, - { - elementCount: 8, - inputShape: [2, 4], - outputShape: [1, 8], - }, - { - elementCount: 6, - inputShape: [2, 3], - outputShape: [1, 6], - }, - { - elementCount: 6, - inputShape: [2, 3], - outputShape: [3, 2], - }, - - // test 3d tensor - { - elementCount: 16, - inputShape: [2, 2, 4], - outputShape: [4, 2, 2], - }, - { - elementCount: 16, - inputShape: [2, 2, 4], - outputShape: [2, 4, 2], - }, - { - elementCount: 16, - inputShape: [2, 2, 4], - outputShape: [1, 1, 2, 8], - }, - - // test 4d tensor - { - elementCount: 32, - inputShape: [2, 2, 2, 4], - outputShape: [4, 2, 2, 2], - }, - { - elementCount: 32, - inputShape: [2, 2, 2, 4], - outputShape: [2, 4, 2, 2], - }, - - { - elementCount: 32, - inputShape: [2, 2, 2, 4], - outputShape: [2, 2, 4, 2], - }, - { - elementCount: 32, - inputShape: [2, 2, 2, 4], - outputShape: [2, 1, 4, 4], + elementCount: 18432, + inputShape: [512, 36, 1, 1], + outputShape: [512, 36], }, ]; } From 7fba657de4f5a233907ba9c34380d0e108bff995 Mon Sep 17 00:00:00 2001 From: Du Li Date: Mon, 3 May 2021 23:39:06 -0700 Subject: [PATCH 4/7] adding a reshape pack test. --- test/unittests/backends/webgl/test_reshape_packed.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/unittests/backends/webgl/test_reshape_packed.ts b/test/unittests/backends/webgl/test_reshape_packed.ts index 1f7bf3d8..5f30cf60 100644 --- a/test/unittests/backends/webgl/test_reshape_packed.ts +++ b/test/unittests/backends/webgl/test_reshape_packed.ts @@ -156,5 +156,10 @@ function getTestData(): TestData[] { inputShape: [2, 2, 2, 4], outputShape: [2, 1, 4, 4], }, + { + elementCount: 18432, + inputShape: [512, 36, 1, 1], + outputShape: [512, 36], + }, ]; } From 9431802ade3b45942db2a59d9b95100fac6d36d4 Mon Sep 17 00:00:00 2001 From: Du Li Date: Thu, 6 May 2021 23:40:22 -0700 Subject: [PATCH 5/7] fixing a reshape_pack bug --- lib/backends/webgl/ops/reshape-packed.ts | 33 ++++++++++++++++++------ 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/lib/backends/webgl/ops/reshape-packed.ts b/lib/backends/webgl/ops/reshape-packed.ts index 9cdc6346..53b23caf 100644 --- a/lib/backends/webgl/ops/reshape-packed.ts +++ b/lib/backends/webgl/ops/reshape-packed.ts @@ -6,7 +6,7 @@ import {Tensor} from '../../../tensor'; import {ShapeUtil} from '../../../util'; import {getGlsl} from '../glsl-source'; import {WebGLInferenceHandler} from '../inference-handler'; -import {ProgramInfo, RunData, WebGLOperator} from '../types'; +import {ProgramInfo, RunData, TextureData, WebGLOperator} from '../types'; import {TextureLayout} from '../types'; import {unpackFromChannel} from './packing_utils'; @@ -32,14 +32,14 @@ export class WebGLReshapePacked extends Reshape implements WebGLOperator { // the same between input shape and output shape, the packed reshape can be // treated as no-op. const originInputShape = inputs[0].dims; - const inputShape3D = processDims3D(inputs[0].dims); + this.inputShape3D = processDims3D(inputs[0].dims); let inputLayout: TextureLayout; if (originInputShape.length === 3) { inputLayout = handler.getOrCreateTextureLayout(inputs[0], 4, true, originInputShape, true); } else { // if originShape is not a 3D shape, create texture layout from the processed shape. - inputLayout = - handler.createTextureLayoutFromShape(inputShape3D, 4, inputShape3D, {isPacked: true, reverseWH: true}); + inputLayout = handler.createTextureLayoutFromShape( + this.inputShape3D, 4, this.inputShape3D, {isPacked: true, reverseWH: true}); } this.outputShape = ShapeUtil.calculateReshapedDims(originInputShape, inputs[1].integerData); @@ -86,9 +86,10 @@ export class WebGLReshapePacked extends Reshape implements WebGLOperator { const glsl = getGlsl(handler.session.backend.glContext.version); const shaderSource = ` - ${getReshapedInputCoords(inputShape3D)} + ${getReshapedInputCoords(this.inputShape3D)} ${getFlattenedIndexFrom3D(squeezedOutputShape)} ${unpackFromChannel()} + void main() { ivec3 rc = getOutputCoords(); @@ -99,7 +100,6 @@ export class WebGLReshapePacked extends Reshape implements WebGLOperator { int cols = ${squeezedOutputShape[1]}; ${mainLoop} - ${glsl.output} = result; } `; @@ -115,8 +115,24 @@ export class WebGLReshapePacked extends Reshape implements WebGLOperator { }; } createRunData(handler: WebGLInferenceHandler, programInfo: ProgramInfo, inputs: Tensor[]): RunData { - const inputTDs = - [handler.getOrCreateTextureData(inputs[0], handler.getOrCreateTextureLayout(inputs[0], 1, false, [], false))]; + let inputTDs: [TextureData]; + const originalInputLayout = handler.getOrCreateTextureLayout(inputs[0], 1, false, [], false); + const originalInputTD = handler.getOrCreateTextureData(inputs[0], originalInputLayout, false); + if (inputs[0].dims.length !== 3) { + const squeezedInputLayout: TextureLayout = { + channels: 1, + height: originalInputTD.height, + width: originalInputTD.width, + shape: this.inputShape3D, + strides: ShapeUtil.computeStrides(this.inputShape3D), + unpackedShape: this.inputShape3D, + }; + const squeezedInputTD = + handler.createSharedTextureData(squeezedInputLayout, inputs[0].type, originalInputTD.texture); + inputTDs = [squeezedInputTD]; + } else { + inputTDs = [originalInputTD]; + } let outputLayout = this.originalOutputLayout; if (outputLayout === undefined) { const originInputShape = inputs[0].dims; @@ -133,6 +149,7 @@ export class WebGLReshapePacked extends Reshape implements WebGLOperator { } protected outputShape: ReadonlyArray; private originalOutputLayout: TextureLayout; + private inputShape3D: [number, number, number]; } function processDims3D(shape: readonly number[]|ReadonlyArray|Tensor.IntegerType): [number, number, number] { From 758499231801f4c8e3b2dda60175b54fbe2e28d5 Mon Sep 17 00:00:00 2001 From: Du Li Date: Tue, 11 May 2021 21:39:01 -0700 Subject: [PATCH 6/7] Further refine the fix for reshape_pack kernel --- lib/backends/webgl/ops/reshape-packed.ts | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/lib/backends/webgl/ops/reshape-packed.ts b/lib/backends/webgl/ops/reshape-packed.ts index 53b23caf..a2cc99da 100644 --- a/lib/backends/webgl/ops/reshape-packed.ts +++ b/lib/backends/webgl/ops/reshape-packed.ts @@ -34,12 +34,16 @@ export class WebGLReshapePacked extends Reshape implements WebGLOperator { const originInputShape = inputs[0].dims; this.inputShape3D = processDims3D(inputs[0].dims); let inputLayout: TextureLayout; - if (originInputShape.length === 3) { - inputLayout = handler.getOrCreateTextureLayout(inputs[0], 4, true, originInputShape, true); - } else { + inputLayout = handler.getOrCreateTextureLayout(inputs[0], 4, true, originInputShape, true); + if (originInputShape.length !== 3) { + const originalInputLayout = inputLayout; // if originShape is not a 3D shape, create texture layout from the processed shape. inputLayout = handler.createTextureLayoutFromShape( this.inputShape3D, 4, this.inputShape3D, {isPacked: true, reverseWH: true}); + // if the processed input shape produces texture layout differnt from the original + // one, the run data has to use the processed (3D) input shape later. + this.needSqueezeInputData = + (inputLayout.height !== originalInputLayout.height) || (inputLayout.width !== originalInputLayout.width); } this.outputShape = ShapeUtil.calculateReshapedDims(originInputShape, inputs[1].integerData); @@ -118,11 +122,12 @@ export class WebGLReshapePacked extends Reshape implements WebGLOperator { let inputTDs: [TextureData]; const originalInputLayout = handler.getOrCreateTextureLayout(inputs[0], 1, false, [], false); const originalInputTD = handler.getOrCreateTextureData(inputs[0], originalInputLayout, false); - if (inputs[0].dims.length !== 3) { + + if (this.needSqueezeInputData) { const squeezedInputLayout: TextureLayout = { channels: 1, - height: originalInputTD.height, - width: originalInputTD.width, + height: originalInputLayout.height, + width: originalInputLayout.width, shape: this.inputShape3D, strides: ShapeUtil.computeStrides(this.inputShape3D), unpackedShape: this.inputShape3D, @@ -130,6 +135,7 @@ export class WebGLReshapePacked extends Reshape implements WebGLOperator { const squeezedInputTD = handler.createSharedTextureData(squeezedInputLayout, inputs[0].type, originalInputTD.texture); inputTDs = [squeezedInputTD]; + } else { inputTDs = [originalInputTD]; } @@ -150,6 +156,7 @@ export class WebGLReshapePacked extends Reshape implements WebGLOperator { protected outputShape: ReadonlyArray; private originalOutputLayout: TextureLayout; private inputShape3D: [number, number, number]; + private needSqueezeInputData = false; } function processDims3D(shape: readonly number[]|ReadonlyArray|Tensor.IntegerType): [number, number, number] { From 5af1276f031e82ca4d1c79cb7e39c4c031382cb2 Mon Sep 17 00:00:00 2001 From: Du Li Date: Thu, 13 May 2021 00:01:46 -0700 Subject: [PATCH 7/7] address PR comments --- lib/backends/webgl/ops/conv-pack.ts | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/lib/backends/webgl/ops/conv-pack.ts b/lib/backends/webgl/ops/conv-pack.ts index 414bf64d..6d00df15 100644 --- a/lib/backends/webgl/ops/conv-pack.ts +++ b/lib/backends/webgl/ops/conv-pack.ts @@ -15,6 +15,10 @@ import {WebGLReshapePacked} from './reshape-packed'; export class WebGLConvPacked extends Conv { protected artifacts: Artifact[]; protected programInfo: ProgramInfo[]; + private kernelReshape = new WebGLReshapePacked(); + private im2col: WebGLIm2ColPacked; + private matmul = new WebGLMatMulPacked(); + private outputReshape = new WebGLReshapePacked(); run(inferenceHandler: WebGLInferenceHandler, inputs: Tensor[]): Tensor[] { const programManager = inferenceHandler.session.programManager; @@ -34,30 +38,31 @@ export class WebGLConvPacked extends Conv { this.kernelShape}, pads:${this.pads}, strides:${this.strides}`); const outputShape = WebGLConv.calcOutputShape(xshape, kshape, this.dilations, this.pads, this.strides); - const im2col = new WebGLIm2ColPacked(outputShape, kshape, this.dilations, this.pads, this.strides); - const matmul = new WebGLMatMulPacked(); - const reshape = new WebGLReshapePacked(); + if (this.im2col === undefined) { + this.im2col = new WebGLIm2ColPacked(outputShape, kshape, this.dilations, this.pads, this.strides); + } // shape for kernel reshape const shape = new Tensor([2], 'int32', undefined, undefined, new Int32Array([kshape[0], kshape[1] * kshape[2] * kshape[3]])); if (!this.artifacts) { this.artifacts = []; this.programInfo = []; - this.programInfo[0] = im2col.createProgramInfo(inferenceHandler, [inputs[0], inputs[1]]); + this.programInfo[0] = this.im2col.createProgramInfo(inferenceHandler, [inputs[0], inputs[1]]); this.artifacts[0] = programManager.build(this.programInfo[0]); - this.programInfo[1] = reshape.createProgramInfo(inferenceHandler, [inputs[1], shape]); + this.programInfo[1] = this.kernelReshape.createProgramInfo(inferenceHandler, [inputs[1], shape]); this.artifacts[1] = programManager.build(this.programInfo[1]); } // run im2col - const runDataIm2col = im2col.createRunData(inferenceHandler, this.programInfo[0], [inputs[0], inputs[1]]); + const runDataIm2col = this.im2col.createRunData(inferenceHandler, this.programInfo[0], [inputs[0], inputs[1]]); inferenceHandler.checkAndUpdateTextureForm(this.artifacts[0], runDataIm2col); programManager.run(this.artifacts[0], runDataIm2col); const im2colOutput = runDataIm2col.outputTextureData.tensor; // reshape kernel - const runDataKernelReshape = reshape.createRunData(inferenceHandler, this.programInfo[1], [inputs[1], shape]); + const runDataKernelReshape = + this.kernelReshape.createRunData(inferenceHandler, this.programInfo[1], [inputs[1], shape]); inferenceHandler.checkAndUpdateTextureForm(this.artifacts[1], runDataKernelReshape); programManager.run(this.artifacts[1], runDataKernelReshape); const kernelReshaped = runDataKernelReshape.outputTextureData.tensor; @@ -66,11 +71,11 @@ export class WebGLConvPacked extends Conv { const hasBias = (inputs.length === 3); assert(this.artifacts.length > 1, () => 'expect at least 2 artifacts created'); if (this.artifacts.length === 2) { - this.programInfo[2] = matmul.createProgramInfo( + this.programInfo[2] = this.matmul.createProgramInfo( inferenceHandler, hasBias ? [kernelReshaped, im2colOutput, inputs[2]] : [kernelReshaped, im2colOutput]); this.artifacts[2] = programManager.build(this.programInfo[2]); } - const runDataMatmul = matmul.createRunData( + const runDataMatmul = this.matmul.createRunData( inferenceHandler, this.programInfo[2], hasBias ? [kernelReshaped, im2colOutput, inputs[2]] : [kernelReshaped, im2colOutput]); inferenceHandler.checkAndUpdateTextureForm(this.artifacts[2], runDataMatmul); @@ -84,11 +89,11 @@ export class WebGLConvPacked extends Conv { assert(this.artifacts.length > 2, () => 'expect at least 3 artifacts created'); if (this.artifacts.length === 3) { - this.programInfo[3] = reshape.createProgramInfo(inferenceHandler, [matmulOutput, outputShapeTensor]); + this.programInfo[3] = this.outputReshape.createProgramInfo(inferenceHandler, [matmulOutput, outputShapeTensor]); this.artifacts[3] = programManager.build(this.programInfo[3]); } const runDataOutputReshape = - reshape.createRunData(inferenceHandler, this.programInfo[3], [matmulOutput, outputShapeTensor]); + this.outputReshape.createRunData(inferenceHandler, this.programInfo[3], [matmulOutput, outputShapeTensor]); inferenceHandler.checkAndUpdateTextureForm(this.artifacts[3], runDataOutputReshape); programManager.run(this.artifacts[3], runDataOutputReshape); return [runDataOutputReshape.outputTextureData.tensor];