Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[js/web] Float16 polyfill #19302

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
[js] Support Float16Array polyfill
  • Loading branch information
axinging committed Jan 29, 2024
commit 5a95268ee4a06e0443467d23e1e3740966e06d80
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