JSコード片をObjectURL化してWeb Workerで実行

今Web WorkerといえばService Workerの話題ばかりですが、これからするのは普通のWorker(Dedicated Worker)の話です。

Web Workerはバックグラウンドでスクリプトを実行できるものです。

重い処理をJSで実行するとUIが固まって何の操作も受け付けなくなりますが、Workerは別スレッドで実行するのでUI処理に影響することはありません。UIに影響しないようにDOMにアクセスできないなどの制限がありますが、ArrayBufferを渡してバイナリ処理をしたり、fetch APIを使ってデータを取得して処理したり、できることはいろいろあります。

https://developer.mozilla.org/ja/docs/Web/API/Web_Workers_API

普通は以下のように別のスクリプトファイルを指定して使いますが…

const worker = new Worker("worker.js");

// ワーカー内でpostMessageが呼ばれた時のハンドラ
worker.onmessage = function(e) {
   window.alert(e.data);
}

// ワーカーにデータを渡す
worker.postMessage("1 + 1");

URL.createObjectURLを使ってURL化したコード片も渡すことができます。

const source = "(" + function() {
    onmessage = function(e) {
        const f = new Function("js", "return eval(js)");
        postMessage(f(e.data));
    }
} + ")();"

const url = URL.createObjectURL(new Blob([source], { type: "application/javascript" }));

const worker = new Worker(url);

worker.onmessage = function(e) {
   window.alert(e.data);
}

worker.postMessage("1 + 1");

Golangの構造体比較

Goでは構造体を == で比較できるらしい。

package main

import (
    "fmt"
    "unsafe"
)

type Hoge struct {
  t bool
  d int
  s string
}

func main() {
    a := Hoge{t: true, d: 1, s:"nyan"}
    b := Hoge{t: true, d: 1, s:"nyan"}
    c := a   // 値コピー
    d := &a  // ポインタコピー
 
    fmt.Println(a == b)    // true 値の比較、同値
    fmt.Println(&a == &b)  // false ポインタの比較、同一ではない
    fmt.Println(a == c)    // true 値がコピーされたので同値
    fmt.Println(&a == &c)  // false 値がコピーされたので同一ではない
    fmt.Println(a == *d)   // true ポインタのコピーなので同値
    fmt.Println(&a == d)   // true ポインタのコピーなので同一 
}

どうせ同一性の比較しかできんのだろうと思っていたら、別のインスタンスでも、フィールドの値を比較して同値と判断してくれるようだ。

   fmt.Println(unsafe.Sizeof(a))     // 16
    fmt.Println(unsafe.Sizeof(a.t))   // 1
    fmt.Println(unsafe.Sizeof(a.d))   // 4
    fmt.Println(unsafe.Sizeof(a.s))   // 8
 
    fmt.Println(unsafe.Offsetof(a.t))  // 0
    fmt.Println(unsafe.Offsetof(a.d))  // 4
    fmt.Println(unsafe.Offsetof(a.s))  // 8

Go playgroundではbool型の後ろに3byteパディングがある(どうやら32bit環境らしいw)

どう比較しているのだろう? フィールドを一つ一つ比較するのはコストが高そうなので、構造体の開始部分から16byteのメモリの中身を丸ごと比較してるのだろうか? だとするとパディング部分も初期化時に0埋めされてないと比較できない。まあ、セキュリティ的な観点からみるとパディングも0埋め初期化するのが正解だが…、ドキュメントを見てもよくわからん。

構造体の比較は便利そうではあるが、ポインタをフィールドに含む場合を想定すると面倒だし、スライスを持っているとそもそも比較できない。そういった面倒ごとを考慮せず同値比較するならば、reflect.DeepEqualを使うといいらしい

   fmt.Println(reflect.DeepEqual(&a, &b))   // true