Skip to content

Commit 881afd0

Browse files
committed
Init simple ort-web benchmark tool
1 parent 24a16ef commit 881afd0

File tree

91 files changed

+186808
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

91 files changed

+186808
-0
lines changed

benchmark-webgpu.html

Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,291 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+
<title>ONNXRuntime Web Benchmark Tool</title>
8+
</head>
9+
<style>
10+
body {
11+
font-family: sans-serif;
12+
padding: 20px;
13+
}
14+
15+
h1 {
16+
color: #425066;
17+
font-size: 31px;
18+
margin-top: 0;
19+
}
20+
21+
.loading-stats {
22+
color: #aaa;
23+
font-size: 12px;
24+
margin-top: -12px;
25+
}
26+
27+
.hide {
28+
display: none;
29+
}
30+
31+
.content {
32+
margin-top: 30px;
33+
}
34+
35+
div {
36+
margin-top: 20px;
37+
}
38+
</style>
39+
<body>
40+
<h1>ONNXRuntime Web Benchmark Tool</h1>
41+
42+
<!-- Loading status -->
43+
<div class="loading-stats">Choose options then click 'Run'...</div>
44+
<div>
45+
Number of runs:
46+
<input type="number" id="numRuns" value="101" min="101" defaultValue="101" />
47+
</div>
48+
<div>
49+
Model:
50+
<select id="model">
51+
<option value="mobilenetv2-10">MobileNetv2</option>
52+
<option value="squeezenet1.1-7">SqueezeNet</option>
53+
<option value="emotion-ferplus-8">FER_emotion_recognition</option>
54+
<option value="tinyyolov2-8">Yolo</option>
55+
<option value="efficientnet-lite4-11">efficientnet-lite4-11</option>
56+
<option value="candy-8">onnxzoo_fns-candy-8</option>
57+
<option value="densenet-9">densenet-9</option>
58+
<option value="resnet50-v1-12">ressnet50_v1</option>
59+
<option value="resnet50-v2-7">resnet50-v2-7</option>
60+
<option value="inception-v1-12">inception_v1</option>
61+
<option value="yolo">ORT Web Demo Yolo</option>
62+
<option value="mobilenetv2-7">ORT Web Demo MobileNetv2</option>
63+
</select>
64+
</div>
65+
<div>
66+
Backend:
67+
<select id="backend">
68+
<!-- <option value="webnn">webnn</option>
69+
<option value="webgl">webgl</option>
70+
<option value="wasm">wasm</option> -->
71+
<option value="webgpu">webgpu</option>
72+
</select>
73+
</div>
74+
<div>
75+
<input type="button" value="Run" id="run" />
76+
</div>
77+
<div id="status" style="font: 1em sans-serif"></div>
78+
<script src="./onnxruntime-web/dist-jiajia/ort.webgpu.min.js"></script>
79+
80+
<script>
81+
function log(i) {
82+
console.log(i);
83+
document.getElementById("status").innerText +=
84+
`\n[${performance.now().toFixed(3)}] ` + i;
85+
}
86+
87+
function generateTensor(dataType, shape, val) {
88+
let size = 1;
89+
shape.forEach((element) => {
90+
size *= element;
91+
});
92+
switch (dataType) {
93+
case "uint16":
94+
return new ort.Tensor(
95+
dataType,
96+
Uint16Array.from({ length: size }, () => val),
97+
shape
98+
);
99+
case "float16":
100+
return new ort.Tensor(
101+
dataType,
102+
Uint16Array.from({ length: size }, () => val),
103+
shape
104+
);
105+
case "float32":
106+
return new ort.Tensor(
107+
dataType,
108+
Float32Array.from({ length: size }, () => val),
109+
shape
110+
);
111+
case "int32":
112+
return new ort.Tensor(
113+
dataType,
114+
Int32Array.from({ length: size }, () => val),
115+
shape
116+
);
117+
case "int64":
118+
return new ort.Tensor(
119+
dataType,
120+
BigInt64Array.from({ length: size }, () => val),
121+
shape
122+
);
123+
}
124+
throw new Error(`Input tensor type ${dataType} is unknown`);
125+
}
126+
127+
const type_to_func = {
128+
float32: Float32Array,
129+
uint16: Uint16Array,
130+
float16: Uint16Array,
131+
int32: Int32Array,
132+
BigInt64Array: BigInt64Array,
133+
};
134+
135+
function clone(x) {
136+
let feed = {};
137+
for (const [key, value] of Object.entries(x)) {
138+
let func = type_to_func[value.type];
139+
let arrayType = func.from(value.data);
140+
feed[key] = new ort.Tensor(
141+
value.type,
142+
arrayType.slice(0),
143+
value.dims
144+
);
145+
}
146+
return feed;
147+
}
148+
149+
// ort.env.wasm.numThreads = 1;
150+
// ort.env.wasm.simd = false;
151+
// ort.env.wasm.proxy = true;
152+
// ort.env.logLevel = "verbose"; //"error";
153+
// ort.env.debug = false;
154+
155+
async function run() {
156+
let feed = {};
157+
const provider = document.getElementById("backend").value;
158+
const modelName = document.getElementById("model").value;
159+
let modelPath = `models/feng/${modelName}.onnx`;
160+
log("entering run ...");
161+
162+
try {
163+
if (modelName == "mobilenetv2-10") {
164+
feed["input"] = generateTensor("float32", [1, 3, 224, 224], 0.5);
165+
} else if (modelName == "mobilenetv2-7") {
166+
feed["input"] = generateTensor("float32", [1, 3, 224, 224], 0.5);
167+
modelPath = `models/${modelName}.onnx`;
168+
} else if (modelName == "squeezenet1.1-7") {
169+
feed["data"] = generateTensor("float32", [1, 3, 224, 224], 0.5);
170+
} else if (modelName == "emotion-ferplus-8") {
171+
feed["Input3"] = generateTensor("float32", [1, 1, 64, 64], 0.5);
172+
} else if (modelName == "tinyyolov2-8") {
173+
feed["image"] = generateTensor("float32", [1, 3, 416, 416], 0.5);
174+
} else if (modelName == "yolo") {
175+
modelPath = `models/${modelName}.onnx`;
176+
feed["image"] = generateTensor("float32", [1, 3, 416, 416], 0.5);
177+
} else if (modelName == "efficientnet-lite4-11") {
178+
feed["images:0"] = generateTensor("float32", [1, 224, 224, 3], 0.5);
179+
} else if (modelName == "candy-8") {
180+
feed["input1"] = generateTensor("float32", [1, 3, 224, 224], 0.5);
181+
} else if (modelName == "densenet-9") {
182+
feed["data_0"] = generateTensor("float32", [1, 3, 224, 224], 0.5);
183+
} else if (modelName == "resnet50-v1-12" || modelName == "resnet50-v2-7") {
184+
feed["data"] = generateTensor("float32", [1, 3, 224, 224], 0.5);
185+
} else if (modelName == "inception-v1-12") {
186+
feed["data_0"] = generateTensor("float32", [1, 3, 224, 224], 0.5);
187+
} else if (modelName == "sam-h") {
188+
feed["image_embeddings"] = generateTensor(
189+
"float32",
190+
[1, 256, 64, 64],
191+
0.5
192+
);
193+
feed["point_coords"] = new ort.Tensor(
194+
new Float32Array([327.1111, 426.875, 241.77777, 341.5]),
195+
[1, 2, 2]
196+
);
197+
feed["point_labels"] = new ort.Tensor(
198+
new Float32Array([0, 1]),
199+
[1, 2]
200+
);
201+
feed["mask_input"] = generateTensor("float32", [1, 1, 256, 256], 0);
202+
feed["has_mask_input"] = generateTensor("float32", [1], 1);
203+
// orig_im_size is optimized out for this model:
204+
//feed["orig_im_size"] = new ort.Tensor(new Float32Array([1200., 1800.]), [2]);
205+
} else if (modelName == "sam-h-16") {
206+
// The float16 version.
207+
// TODO: Convert to actual float16 values. We're just estimating perf with this one, not correctness.
208+
feed["image_embeddings"] = generateTensor(
209+
"float16",
210+
[1, 256, 64, 64],
211+
0.5
212+
);
213+
feed["point_coords"] = new ort.Tensor(
214+
"float16",
215+
new Uint16Array([327, 426, 241, 341]),
216+
[1, 2, 2]
217+
);
218+
feed["point_labels"] = new ort.Tensor(
219+
"float16",
220+
new Uint16Array([0, 2]),
221+
[1, 2]
222+
);
223+
feed["mask_input"] = generateTensor("float16", [1, 1, 256, 256], 0);
224+
feed["has_mask_input"] = generateTensor("float16", [1], 1);
225+
// orig_im_size is optimized out for this model:
226+
//feed["orig_im_size"] = new ort.Tensor(new Float32Array([1200., 1800.]), [2]);
227+
} else if (modelName == "onnx-add") {
228+
feed["A"] = generateTensor("float32", [5], 1);
229+
feed["B"] = generateTensor("float32", [5], 1);
230+
} else {
231+
throw new Error(`Model ${modelName} is unknown`);
232+
}
233+
234+
// let options = {
235+
// executionProviders: [
236+
// {
237+
// name: provider,
238+
// deviceType: "gpu",
239+
// powerPreference: "default",
240+
// },
241+
// ],
242+
// //executionProviders: [{name: "webnn", deviceType: 'gpu', powerPreference: 'high-performance' }],
243+
// };
244+
// options.logSeverityLevel = 0;
245+
// options.logVerbosityLevel = 3;
246+
const options = {executionProviders: ["webgpu"]};
247+
log("creating session ...");
248+
const sess = await ort.InferenceSession.create(modelPath, options);
249+
log("warmup ...");
250+
await sess.run(clone(feed));
251+
252+
log("running ...");
253+
let N = document.getElementById("numRuns").value;
254+
N = N === null ? 10 : parseInt(N);
255+
if (N < 1) {
256+
throw new Error("Run Number should be greater than 0!");
257+
}
258+
let inferTimes = [];
259+
260+
for (var i = 0; i < N; i++) {
261+
const input = clone(feed);
262+
const start = performance.now();
263+
const outputs = await sess.run(input);
264+
//const outputs = await sess.run(feed); // Without clone(), you get DOMException: Failed to execute 'postMessage' on 'Worker': ArrayBuffer at index 0 is already detached.
265+
inferTimes.push(performance.now() - start);
266+
}
267+
268+
let intermediateTimings = "";
269+
for (var i = 0; i < N; i++) {
270+
intermediateTimings += `${inferTimes[i].toFixed(2)}, `;
271+
}
272+
log(intermediateTimings);
273+
const totalTime = inferTimes.reduce(
274+
(partialSum, a) => partialSum + a,
275+
0
276+
);
277+
const result = `${modelName}/${provider}, ${(totalTime / N).toFixed(
278+
2
279+
)} ms / iter`;
280+
log(result);
281+
} catch (e) {
282+
log(e);
283+
}
284+
}
285+
const runBtn = document.getElementById("run");
286+
runBtn.onclick = async () => {
287+
await run();
288+
};
289+
</script>
290+
</body>
291+
</html>

0 commit comments

Comments
 (0)