Skip to content

Commit

Permalink
[js] Support Float16Array polyfill
Browse files Browse the repository at this point in the history
  • Loading branch information
axinging committed Jan 29, 2024
1 parent 656ca66 commit 5a95268
Show file tree
Hide file tree
Showing 12 changed files with 105 additions and 33 deletions.
14 changes: 10 additions & 4 deletions js/common/lib/tensor-impl-type-mapping.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

import {Float16Array} from '@petamoriken/float16';

import {Tensor} from './tensor.js';

export type SupportedTypedArrayConstructors = Float32ArrayConstructor|Uint8ArrayConstructor|Int8ArrayConstructor|
Uint16ArrayConstructor|Int16ArrayConstructor|Int32ArrayConstructor|BigInt64ArrayConstructor|Uint8ArrayConstructor|
Float64ArrayConstructor|Uint32ArrayConstructor|BigUint64ArrayConstructor;
interface Float16ArrayConstructor {
new(): Float16Array;
}

export type SupportedTypedArrayConstructors = Float32ArrayConstructor|Float16ArrayConstructor|Uint8ArrayConstructor|
Int8ArrayConstructor|Uint16ArrayConstructor|Int16ArrayConstructor|Int32ArrayConstructor|BigInt64ArrayConstructor|
Uint8ArrayConstructor|Float64ArrayConstructor|Uint32ArrayConstructor|BigUint64ArrayConstructor;
export type SupportedTypedArray = InstanceType<SupportedTypedArrayConstructors>;

// a runtime map that maps type string to TypedArray constructor. Should match Tensor.DataTypeMap.
Expand All @@ -14,7 +20,7 @@ export const NUMERIC_TENSOR_TYPE_TO_TYPEDARRAY_MAP = new Map<string, SupportedTy
['uint8', Uint8Array],
['int8', Int8Array],
['uint16', Uint16Array],
['float16', Uint16Array],
['float16', Float16Array],
['int16', Int16Array],
['int32', Int32Array],
['bool', Uint8Array],
Expand Down
8 changes: 5 additions & 3 deletions js/common/lib/tensor-impl.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

import {isFloat16Array} from '@petamoriken/float16';

import {tensorToDataURL, tensorToImageData} from './tensor-conversion-impl.js';
import {TensorToDataUrlOptions, TensorToImageDataOptions} from './tensor-conversion.js';
import {tensorFromGpuBuffer, tensorFromImage, tensorFromPinnedBuffer, tensorFromTexture} from './tensor-factory-impl.js';
Expand Down Expand Up @@ -146,8 +148,8 @@ export class Tensor implements TensorInterface {
// Throw error here because when user try to use number array as data,
// e.g. new Tensor('float16', [1, 2, 3, 4], dims)), it will actually call
// Uint16Array.from(arg1) which generates wrong data.
throw new TypeError(
'Creating a float16 tensor from number array is not supported. Please use Uint16Array as data.');
// eslint-disable-next-line @typescript-eslint/no-explicit-any
data = (typedArrayConstructor as any).from(arg1);
} else if (arg0 === 'uint64' || arg0 === 'int64') {
// use 'as any' here because:
// 1. TypeScript's check on type of 'Array.isArray()' does not work with readonly arrays.
Expand All @@ -166,7 +168,7 @@ export class Tensor implements TensorInterface {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
data = (typedArrayConstructor as any).from(arg1);
}
} else if (arg1 instanceof typedArrayConstructor) {
} else if (arg1 instanceof typedArrayConstructor || isFloat16Array(arg1)) {
data = arg1;
} else {
throw new TypeError(`A ${type} tensor's data must be type of ${typedArrayConstructor}`);
Expand Down
4 changes: 3 additions & 1 deletion js/common/lib/tensor.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

import {Float16Array} from '@petamoriken/float16';

import {TensorFactory} from './tensor-factory.js';
import {Tensor as TensorImpl} from './tensor-impl.js';
import {TypedTensorUtils} from './tensor-utils.js';
Expand Down Expand Up @@ -74,7 +76,7 @@ export declare namespace Tensor {
int64: BigInt64Array;
string: string[];
bool: Uint8Array;
float16: Uint16Array; // Keep using Uint16Array until we have a concrete solution for float 16.
float16: Float16Array; // Keep using Uint16Array until we have a concrete solution for float 16.
float64: Float64Array;
uint32: Uint32Array;
uint64: BigUint64Array;
Expand Down
5 changes: 4 additions & 1 deletion js/common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,8 @@
"ONNXRuntime",
"ONNX Runtime"
],
"description": "ONNXRuntime JavaScript API library"
"description": "ONNXRuntime JavaScript API library",
"dependencies": {
"@petamoriken/float16": "^3.8.4"
}
}
3 changes: 2 additions & 1 deletion js/common/test/unit-tests/common.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

import {Float16Array} from '@petamoriken/float16';
import assert from 'assert/strict';
import {Tensor} from 'onnxruntime-common';

Expand Down Expand Up @@ -34,7 +35,7 @@ export const BIGINT_TYPES = [
/**
* float16 type, data represented by Uint16Array
*/
export const FLOAT16_TYPE = ['float16', Uint16Array, false] as const;
export const FLOAT16_TYPE = ['float16', Float16Array, false] as const;

/**
* A list of all numerical types.
Expand Down
10 changes: 8 additions & 2 deletions js/web/lib/onnxjs/tensor.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

import {Float16Array, isFloat16Array} from '@petamoriken/float16';
import {Guid} from 'guid-typescript';
import Long from 'long';

Expand All @@ -13,6 +14,7 @@ import ortFbs = onnxruntime.experimental.fbs;
export declare namespace Tensor {
export interface DataTypeMap {
bool: Uint8Array;
float16: Float16Array;
float32: Float32Array;
float64: Float64Array;
string: string[];
Expand All @@ -31,7 +33,7 @@ export declare namespace Tensor {
export type BooleanType = Tensor.DataTypeMap['bool'];
export type IntegerType = Tensor.DataTypeMap['int8']|Tensor.DataTypeMap['uint8']|Tensor.DataTypeMap['int16']|
Tensor.DataTypeMap['uint16']|Tensor.DataTypeMap['int32']|Tensor.DataTypeMap['uint32'];
export type FloatType = Tensor.DataTypeMap['float32']|Tensor.DataTypeMap['float64'];
export type FloatType = Tensor.DataTypeMap['float16']|Tensor.DataTypeMap['float32']|Tensor.DataTypeMap['float64'];
export type NumberType = BooleanType|IntegerType|FloatType;

export type Id = Guid;
Expand Down Expand Up @@ -93,6 +95,7 @@ export class Tensor {
*/
get floatData() {
switch (this.type) {
case 'float16':
case 'float32':
case 'float64':
return this.data as Tensor.FloatType;
Expand Down Expand Up @@ -188,7 +191,7 @@ export class Tensor {
} else {
if (cache !== undefined) {
const constructor = dataviewConstructor(type);
if (!(cache instanceof constructor)) {
if (!(cache instanceof constructor) && !isFloat16Array(cache)) {
throw new TypeError(`cache should be type ${constructor.name}`);
}
}
Expand Down Expand Up @@ -357,6 +360,7 @@ function sizeof(type: Tensor.DataType): number {
return 1;
case 'int16':
case 'uint16':
case 'float16':
return 2;
case 'int32':
case 'uint32':
Expand Down Expand Up @@ -412,6 +416,8 @@ function dataviewConstructor(type: Tensor.DataType) {
return Uint32Array;
case 'int64':
return BigInt64Array;
case 'float16':
return Float16Array;
case 'float32':
return Float32Array;
case 'float64':
Expand Down
3 changes: 2 additions & 1 deletion js/web/lib/wasm/jsep/tensor-view.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

import {Float16Array} from '@petamoriken/float16';
import {Tensor} from 'onnxruntime-common';

import {tensorTypeToTypedArrayConstructor} from '../wasm-common';

export const createView = (dataBuffer: ArrayBuffer, type: Tensor.Type): Int32Array|Uint32Array|BigInt64Array|
BigUint64Array|Uint8Array|Float32Array|Float64Array|Int8Array|Int16Array|Uint16Array =>
BigUint64Array|Uint8Array|Float16Array|Float32Array|Float64Array|Int8Array|Int16Array|Uint16Array =>
new (tensorTypeToTypedArrayConstructor(type))(dataBuffer);

/**
Expand Down
14 changes: 10 additions & 4 deletions js/web/lib/wasm/wasm-common.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

import {Float16Array} from '@petamoriken/float16';
import {Tensor} from 'onnxruntime-common';

interface Float16ArrayConstructor {
new(): Float16Array;
}

// This file includes common definitions. They do NOT have dependency on the WebAssembly instance.

/**
Expand Down Expand Up @@ -112,12 +117,13 @@ export const getTensorElementSize = (dateType: number): number|
/**
* get typed array constructor by the given tensor type
*/
export const tensorTypeToTypedArrayConstructor = (type: Tensor.Type): Float32ArrayConstructor|Uint8ArrayConstructor|
Int8ArrayConstructor|Uint16ArrayConstructor|Int16ArrayConstructor|Int32ArrayConstructor|BigInt64ArrayConstructor|
Uint8ArrayConstructor|Float64ArrayConstructor|Uint32ArrayConstructor|BigUint64ArrayConstructor => {
export const tensorTypeToTypedArrayConstructor =
(type: Tensor.Type): Float16ArrayConstructor|Float32ArrayConstructor|Uint8ArrayConstructor|Int8ArrayConstructor|
Uint16ArrayConstructor|Int16ArrayConstructor|Int32ArrayConstructor|BigInt64ArrayConstructor|Uint8ArrayConstructor|
Float64ArrayConstructor|Uint32ArrayConstructor|BigUint64ArrayConstructor => {
switch (type) {
case 'float16':
return Uint16Array;
return Float16Array;
case 'float32':
return Float32Array;
case 'uint8':
Expand Down
1 change: 1 addition & 0 deletions js/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"version": "1.18.0",
"jsdelivr": "dist/ort.min.js",
"dependencies": {
"@petamoriken/float16": "^3.8.4",
"flatbuffers": "^1.12.0",
"guid-typescript": "^1.0.9",
"long": "^5.2.3",
Expand Down
35 changes: 35 additions & 0 deletions js/web/test/data/ops/padf16.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
[
{
"name": "constant 2D",
"operator": "Pad",
"opset": { "domain": "", "version": 10 },
"attributes": [
{ "name": "mode", "data": "constant", "type": "string" },
{ "name": "value", "data": 1.2, "type": "float" },
{ "name": "pads", "data": [3, 2, 2, 3], "type": "ints" }
],
"cases": [
{
"name": "[2,2]->[7,7]",
"inputs": [
{
"data": [1.0, 2.0, 3.0, 4.0],
"dims": [2, 2],
"type": "float16"
}
],
"outputs": [
{
"data": [
1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2,
1.2, 1.2, 1.0, 2.0, 1.2, 1.2, 1.2, 1.2, 1.2, 3.0, 4.0, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2,
1.2, 1.2, 1.2, 1.2, 1.2, 1.2, 1.2
],
"dims": [7, 7],
"type": "float16"
}
]
}
]
}
]
11 changes: 8 additions & 3 deletions js/web/test/test-runner.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

import {Float16Array} from '@petamoriken/float16';
import {expect} from 'chai';
import * as ort from 'onnxruntime-common';
import {extname} from 'path';
Expand Down Expand Up @@ -393,11 +394,12 @@ export class TensorResultValidator {
case 'string':
return this.strictEqual(actual.stringData, expected.stringData);

case 'float16':
case 'float32':
case 'float64':
return this.floatEqual(
actual.numberData as number[] | Float32Array | Float64Array,
expected.numberData as number[] | Float32Array | Float64Array);
actual.numberData as number[] | Float16Array | Float32Array | Float64Array,
expected.numberData as number[] | Float16Array | Float32Array | Float64Array);

case 'uint8':
case 'int8':
Expand Down Expand Up @@ -425,7 +427,10 @@ export class TensorResultValidator {
return false;
}
}
floatEqual(actual: number[]|Float32Array|Float64Array, expected: number[]|Float32Array|Float64Array): boolean {

floatEqual(
actual: number[]|Float16Array|Float32Array|Float64Array,
expected: number[]|Float16Array|Float32Array|Float64Array): boolean {
if (actual.length !== expected.length) {
return false;
}
Expand Down
30 changes: 17 additions & 13 deletions js/web/test/unittests/backends/webgl/test-conv-utils.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

import {Float16Array} from '@petamoriken/float16';

import {Tensor} from '../../../../lib/onnxjs/tensor';

/* eslint-disable no-bitwise */

type FloatTypedArray = Float16Array|Float32Array|Float64Array;

// eslint-disable-next-line no-underscore-dangle
function matMul2d_(
A: Float32Array|Float64Array, B: Float32Array|Float64Array, C: Float32Array|Float64Array, alpha: number,
beta: number, M: number, N: number, K: number) {
A: FloatTypedArray, B: FloatTypedArray, C: FloatTypedArray, alpha: number, beta: number, M: number, N: number,
K: number) {
let offsetA = 0, offsetB = 0, offsetC = 0;
for (let mm = 0; mm < M; mm++) {
for (let nn = 0; nn < N; nn++) {
Expand All @@ -30,8 +34,8 @@ function matMul2d_(
}

function matMul2d_tA(
A: Float32Array|Float64Array, B: Float32Array|Float64Array, C: Float32Array|Float64Array, alpha: number,
beta: number, M: number, N: number, K: number) {
A: FloatTypedArray, B: FloatTypedArray, C: FloatTypedArray, alpha: number, beta: number, M: number, N: number,
K: number) {
let offsetA = 0, offsetB = 0, offsetC = 0;
for (let mm = 0; mm < M; mm++) {
for (let nn = 0; nn < N; nn++) {
Expand All @@ -53,8 +57,8 @@ function matMul2d_tA(
}

function matMul2d_tB(
A: Float32Array|Float64Array, B: Float32Array|Float64Array, C: Float32Array|Float64Array, alpha: number,
beta: number, M: number, N: number, K: number) {
A: FloatTypedArray, B: FloatTypedArray, C: FloatTypedArray, alpha: number, beta: number, M: number, N: number,
K: number) {
let offsetA = 0, offsetB = 0, offsetC = 0;
for (let mm = 0; mm < M; mm++) {
for (let nn = 0; nn < N; nn++) {
Expand All @@ -76,8 +80,8 @@ function matMul2d_tB(
}

function matMul2d_tAtB(
A: Float32Array|Float64Array, B: Float32Array|Float64Array, C: Float32Array|Float64Array, alpha: number,
beta: number, M: number, N: number, K: number) {
A: FloatTypedArray, B: FloatTypedArray, C: FloatTypedArray, alpha: number, beta: number, M: number, N: number,
K: number) {
let offsetA = 0, offsetB = 0, offsetC = 0;
for (let mm = 0; mm < M; mm++) {
for (let nn = 0; nn < N; nn++) {
Expand Down Expand Up @@ -105,8 +109,8 @@ function matMul2d_tAtB(
* @param C data of tensor C, whose shape is [M,N]
*/
export function matMul2d(
A: Float32Array|Float64Array, B: Float32Array|Float64Array, C: Float32Array|Float64Array, transA: boolean,
transB: boolean, alpha: number, beta: number, M: number, N: number, K: number): void {
A: FloatTypedArray, B: FloatTypedArray, C: FloatTypedArray, transA: boolean, transB: boolean, alpha: number,
beta: number, M: number, N: number, K: number): void {
if (transA && transB) {
matMul2d_tAtB(A, B, C, alpha, beta, M, N, K);
} else if (transA) {
Expand All @@ -119,9 +123,9 @@ export function matMul2d(
}

function im2col(
data_im: Float32Array|Float64Array, data_col: Float32Array|Float64Array, channels: number, height: number,
width: number, kernel_h: number, kernel_w: number, dilation_h: number, dilation_w: number, pad_t: number,
pad_l: number, pad_b: number, pad_r: number, stride_h: number, stride_w: number) {
data_im: FloatTypedArray, data_col: FloatTypedArray, channels: number, height: number, width: number,
kernel_h: number, kernel_w: number, dilation_h: number, dilation_w: number, pad_t: number, pad_l: number,
pad_b: number, pad_r: number, stride_h: number, stride_w: number) {
const output_h = ~~((height + pad_b + pad_t - (dilation_h * (kernel_h - 1) + 1)) / stride_h) + 1;
const output_w = ~~((width + pad_l + pad_r - (dilation_w * (kernel_w - 1) + 1)) / stride_w) + 1;

Expand Down

0 comments on commit 5a95268

Please sign in to comment.