tensorflow.js 在网络工作者中

tensorflow.js in webworkers

我想通过importScripts()在webWorker中导入2个脚本如下,导入失败。如何处理?

self.importScripts('https://cdn.jsdelivr.net/npm/@tensorflow/tfjs');
self.importScripts('https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-converter');

error figure

TensorflowJS 需要 canvas 来进行 GPU 计算,而工作人员目前没有 canvas。

OffscreenCanvas 是一项正在开发中的功能,但在 TFJS 使用它之前,它可能需要足够广泛的浏览器支持。

目前,无法在 web-worker 上使用 webgl 实现,offlineCanvas 是一项实验性功能。但是,可以使用 CPU 后端。

这是委托给网络工作者执行计算的示例

<head>
 <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@0.14.2/dist/tf.min.js"></script>
    <script>
        const worker_function = () => {

            onmessage =  () => {
                console.log('from web worker')
                    this.window = this
                    importScripts('https://cdn.jsdelivr.net/npm/setimmediate@1.0.5/setImmediate.min.js')
                    importScripts('https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@0.10.3')
                    tf.setBackend('cpu')
                    
                    const res = tf.zeros([1, 2]).add(tf.ones([1, 2]))
                    res.print()
                    
                    postMessage({res: res.dataSync(), shape: res.shape})
            };
        }
        if (window != self)
            worker_function();
    </script>
    <script>
        const worker = new Worker(URL.createObjectURL(new Blob(["(" + worker_function.toString() + ")()"], { type: 'text/javascript' })));
        worker.postMessage({});
        worker.onmessage = (message) => {
         console.log('from main thread')
         const {data} = message
         tf.tensor(data.res, data.shape).print()
        }
    </script>
</head>

有了张量,主线程和 web worker 之间共享的数据可能会很大。此数据被克隆或传输。

不同的是,如果数据被克隆,web worker 仍然会保留一份数据的副本以供进一步处理。转移时,数据的所有权也随之转移。它相对于克隆的优势是传递的牢度,实际上它可以看作是传递给引用(如果来自有指针的语言背景)

让我们讨论一下这两个片段的性能

<head>
 <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@0.14.2/dist/tf.min.js"></script>
    <script>
        const worker_function = () => {

            onmessage =  () => {
                console.log('from web worker')
                    this.window = this
                    importScripts('https://cdn.jsdelivr.net/npm/setimmediate@1.0.5/setImmediate.min.js')
                    importScripts('https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@0.10.3')
                    tf.setBackend('cpu')
                    
                    const res = tf.randomNormal([2000, 2000, 3])
                    const t0 = performance.now()
                    postMessage({res: res.dataSync().buffer, shape: res.shape}, [res.dataSync().buffer])
                    console.log(`Prediction took ${(performance.now() - t0).toFixed(1)} ms`)
            };
        }
        if (window != self)
            worker_function();
    </script>
    <script>
        const worker = new Worker(URL.createObjectURL(new Blob(["(" + worker_function.toString() + ")()"], { type: 'text/javascript' })));
        worker.postMessage({});
        worker.onmessage = (message) => {
         console.log('from main thread')
         const {data} = message
         tf.tensor(new Float32Array(message.data.res), message.data.shape)
        }
    </script>
</head>

<head>
 <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@0.14.2/dist/tf.min.js"></script>
    <script>
        const worker_function = () => {

            onmessage =  () => {
                console.log('from web worker')
                    this.window = this
                    importScripts('https://cdn.jsdelivr.net/npm/setimmediate@1.0.5/setImmediate.min.js')
                    importScripts('https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@0.10.3')
                    tf.setBackend('cpu')
                    
                    const res = tf.randomNormal([2000, 2000, 3])
                    const t0 = performance.now()
                    postMessage({res: res.dataSync(), shape: res.shape})
                    console.log(`Prediction took ${(performance.now() - t0).toFixed(1)} ms`)
            };
        }
        if (window != self)
            worker_function();
    </script>
    <script>
        const worker = new Worker(URL.createObjectURL(new Blob(["(" + worker_function.toString() + ")()"], { type: 'text/javascript' })));
        worker.postMessage({});
        worker.onmessage = (message) => {
         console.log('from main thread')
         const {data} = message
         tf.tensor(message.data.res, message.data.shape)
        }
    </script>
</head>

我们可以看到两个片段之间存在大约 10 毫秒的差异。当以性能为代价时,如果必须克隆或传输数据,则需要考虑如何共享数据。