ラベル JavaScript の投稿を表示しています。 すべての投稿を表示
ラベル JavaScript の投稿を表示しています。 すべての投稿を表示

2013年1月17日木曜日

what's transferable objects?

元ネタはこちら
workers with transferable objectとは何か?を調べて見つけたページ。
2011年の投稿なので結構前からある技術の様だ。

transferable object以前


chrome 13でweb workerとmain process間でデータのやり取りを行うのにstructured cloningと呼ばれる(HTML5の仕様に記載されているらしい)方法が使われていた。これはJavaScriptの複雑なオブジェクトをシリアライズする方法でJSONよりも使える範囲は広くImageObjectやRegExpとかでもOK。だが、オブジェクトは新規にクローンを作って伝達する仕組みになっているので、メモリコピーが発生している分遅く、H/Wスペックは不明だが、32MBのArrayBufferをweb workerに転送するのに数百ミリ秒かかるらしい。

でtransferable objectとは?

そんなstructured cloningの課題を解決する方法として開発されchromeに採用されたのがtransferable object。メモリコピーがボトルネックだった訳なので、当然の様にzero-copyであり、C/C++の世界の参照渡しを想像してもらえばよい。しかし、完全に同じと言う訳ではなく、オブジェクトを送信してしまうと、そのオブジェクトは送信元のコンテキストから送信先のコンテキストに移動してしまい、送信元のコンテキストからクリアされ、利用する事が出来なくなる。うーん、使い方を気をつけないと行けない制約だ。

で、transferable objectを使う為のAPIは専用になっており、以下のAPIがchrome/v8には用意されている。

worker.webkitPostMessage(arrayBuffer, [arrayBuffer]);
window.webkitPostMessage(arrayBuffer, targetOrigin, [arrayBuffer]);

どの程度早くなるのか?

で最終的にどの程度早くなったのかを計測した結果が書かれている。それによると、32MBのArrayBufferを転送するのが302msecから6.6msecまで短縮されており、効果としては絶大なものがある。しかし、32MBものデータを転送するのか?とか考える人もいるだろうと思うのだが、WebGLのテクスチャー転送などで使えると書いてある(ちょっとした自動車のモデルを高精度に描画しようとすると、数MBは楽勝なので、確かに考えられるレベルではあると思う)。






2012年9月23日日曜日

Skia for NaCl

最近、色々と事情があり調べなければ行けない事がコロコロと移り変わるので結構しんどい。で、今はSkiaについて。

SkiaのSource Codeを調べていると、どうもnacl関係のディレクトリが作られている。naclと言えばNative Clientの事だろう。つまりはnacl環境で使えるSkiaが開発されていると言う事か?とかなり期待。

この中のindex.htmlを見てみると、どうやらIntel系CPU向けのnaclでskiaを動かそうという試みらしい。
ChangeLogから色々情報を探してみると、どうも2週間程度前から開始されたプロジェクトの様で、まだ動作するには色々やる事がある様子。今の所、サンプルHTMLとサンプルサーバまでの様で、naclport内でskiaをビルドするのはまだ出来ていない。今後に期待ですね。

2012年3月5日月曜日

Reading platform.h in v8 repository

v8のメモリまわりを知りたいので、virtual memory機能を調べるとplatform.hにVirtualMemoryなるクラスを発見。

class VirtualMemory

予約されたメモリ領域を表現し制御するクラス。
予約されたメモリの制御は代入とcopy-contructing?(コピーコンストラクタの誤字と思われる)によって他のVirtualMemoryオブジェクトにアサイン可能。予約されたメモリはお事理なるオブジェクトからは削除される。

メソッドは以下のものがある。日本語はheader fileに記載のコメント。

VirtualMemory();
空のVirtualMemoryオブジェクトを生成。予約されたメモリを持たない。

exlicit VirtualMemory(size_t size);
サイズが引数sizeのvirtual memoryを予約する。

VirtualMemory(size_t size, size_t alignment);
引数alignment毎にアライメントされた、サイズが引数sizeの領域を持つvirtual memoryを予約する。この処理ではaddress()によって戻された位置ではないかもしれない。

~VirtualMemory();
このVirtualMemoryオブジェクトによって制御されていた予約したメモリを開放する。

bool IsReserved();
予約されたメモリを持っているかどうかを返す。

void Reset();
組み込まれたVirtualMemoryオブジェクトの初期化、もしくはリセットを実行する。

void* address();
予約されたメモリの先頭アドレスを返す。
もしアライメントを持って予約されたメモリならば、このアドレスは必ずしもアライメントされているとは限らない。アライメントブロックの先頭を取得するためにはアライメントサイズの倍数だけ端数を切り捨てる必要があるかもしれない。

size_t size();
予約されたメモリのサイズを返す。戻り値が意味あるのはIsReserved()がtrueの場合のみである。
もしアライメントを持って予約されたメモリならば、このサイズは要求されたサイズよりも大きくなるかもしれない。

bool Commit(void* address, size_t size, bool is_executable);
実際のメモリをコミットする。コミット操作が成功したかどうかを戻り値で返す。

bool Uncommit(void* address, size_t size);
実際のメモリをアンコミットする。アンコミット操作が成功したかどうかを戻り値で返す。

void Release();
[コメントなし]

void TakeControl(VirtualMemory* from);
予約された領域の制御を異なるVirtualMemoryオブジェクトにアサインする。
古いオブジェクトはこれ以上動作しない(IsReserved()はfalseを返す)。

static void* ReserveRegion(size_t size);
[コメントなし]

static bool CommitRegion(void* base, size_t size, bool is_executable);
[コメントなし]

static bool UncommitRegion(void* base, size_t size);
[コメントなし]

static bool ReleaseRegion(void* base, size_t size);
ReserveRegion()によって返されるベースポインタと予約時のサイズと同サイズの値を引数に実行する必要がある。

基本的なメモリ管理クラスの様だ。ccファイルの方はplatform別にplatform-<プラットフォーム名>.ccと言うファイルになっている。linuxの場合にはplatform-linux.ccといった具合。
次回はccファイルの中身を見てみる予定。

2012年3月1日木曜日

V8 JavaScript Engine libraries.cc Part 2

昨日の続き。
libraries.ccで定義されている関数がどこで使われているかを調査。
(JavaScriptコードをv8エンジンではどう使っているのか)一番気になるGetRawScriptSource()がターゲット。
■GetRawScriptSource()
1.void Deserializer::ReadChunk() @ serializer.cc
data = source_->Get();の結果がkNativesStringResourceの場合に実行される。
その戻り値を利用してNativesExternalStringResourceクラスのインスタンス生成。
2.Handle<String> Bootstrapper::NativesSourceLookup() @ bootstrapper.cc
heap->natives_source_cache()->get(index)->IsUndefined()がtrueならば実行される(ただし、Natives::GetRawScriptSource())。
※ indexはint型引数。
※ NativeSourceLookup()はbool Genesis::CompileBuiltin() @ bootstrapper.ccから実行され、戻り値がCompileNative()に渡される。
3.bool Genesis::CompileExperimentalBuiltin() @ bootstrapper.cc
ExperimentalNatives::GetRawScriptSource()限定だが実行され、その結果がfactory->NewStringFromAscii()に渡され、その結果がCompileNative()に渡される。
4.void Shell::InstallUtilityScript() @ d8.cc
GetRawScriptSource()の引数として与えられる値は以下の通り。
i::NativesCollection<i::D8>::GetIndex("d8");
"d8"と言う時点でbuiltinではなく、d8固有の機能をインストールしているはず。

ちなみにNativesとExperimentalNativesは以下の通りnatives.hで定義されている。NativeCollectionクラスの定義も同じファイルで定義されている。

typedef NativesCollection<CORE> Natives;
typedef NativesCollection<EXPERIMENTAL> ExperimentalNatives;

以上の事から、以下の2点が今後の調査ポイントになりそう。

  • Deserializer::ReadChunk()でNativesExternalStringResourceオブジェクトを作る事の意味
  • Bootstrapper::NativesSourceLookup()でCompileNative()にJavaScriptソースコードが渡された後の処理
あと、libraries.ccに生成された整数値の羅列を解析(と言う程のものではないが、char型データとしてファイル出力)して、目視確認すると以下の事をやっている事は分かった。
  • コメント行は全て改行のみに変換
  • ローカル変数名を1文字のアルファベットに変換

つまりは、データ化した時のサイズを小さくする事が目的の変換が施されている様子。
依然として関数名の先頭に%が記述されている変則的な記法は残っている。。。

とりあえず今日はここまで。

2012年2月29日水曜日

V8 JavaScript Engine libraries.cc

V8と言って思い浮かべるのがエンジンなのか、JavaScriptなのか、と言うと私はJavaScript。と言う訳で今回はV8 JavaScript Engineについて調査。

 V8をビルドするとobj/releaseもしくはobj/debug以下にlibraries.ccexperimental-libraries.ccと言うソースコードとそのビルド結果となるlibraries.oexperimenta-libraries.oいう合計4個のファイルが生成される。これは一体どうやって作られて、どんな目的のモノなのであろうか。

 まずはビルド時のログ出力をlibrariesをキーに検索すると、以下のような出力がある事が分かる(ちなみに、以下の出力はscons d8 mode=debugでビルドした時のもの。他の場合でも同じだとは思う)。
JS2C(["obj/debug/libraries.cc"], ["src/runtime.js", "src/v8natives.js", "src/array.js", "src/string.js", "src/uri.js", "src/math.js", "src/messages.js", "src/apinatives.js", "src/date.js", "src/regexp.js", "src/json.js", "src/liveedit-debugger.js", "src/mirror-debugger.js", "src/debug-debugger.js", "src/macros.py"])
g++ -o obj/debug/libraries.o -c -fno-rtti -fno-exceptions -fvisibility=hidden -Wall -Werror -W -Wno-unused-parameter -Woverloaded-virtual -Wnon-virtual-dtor -pedantic -m32 -g -O0 -ansi -DV8_TARGET_ARCH_IA32 -DENABLE_DISASSEMBLER -DDEBUG -DENABLE_DEBUGGER_SUPPORT -DV8_ENABLE_CHECKS -DOBJECT_PRINT -Iobj/debug -Isrc -Isrc obj/debug/libraries.cc
JS2C(["obj/debug/experimental-libraries.cc"], ["src/proxy.js", "src/collection.js", "src/macros.py"])
g++ -o obj/debug/experimental-libraries.o -c -fno-rtti -fno-exceptions -fvisibility=hidden -Wall -Werror -W -Wno-unused-parameter -Woverloaded-virtual -Wnon-virtual-dtor -pedantic -m32 -g -O0 -ansi -DV8_TARGET_ARCH_IA32 -DENABLE_DISASSEMBLER -DDEBUG -DENABLE_DEBUGGER_SUPPORT -DV8_ENABLE_CHECKS -DOBJECT_PRINT -Iobj/debug -Isrc -Isrc obj/debug/experimental-libraries.cc
 どうもJS2Cでccファイルを生成して、その後にg++でビルドしている様子。JS2Cと言う関数名からするとJavaScriptをCに変換しているのであろう。そのJS2Cはtools/js2c.pyファイルに定義されてて、そのファイルの先頭には以下の様なコメントがある。
# This is a utility for converting JavaScript source code into C-style
# char arrays. It is used for embedded JavaScript code in the V8
# library.
 つまり、JavaSciptコードをC形式の文字列配列に変換する、と言う事でJavaScript関数をCの関数に変換している訳ではないようなので、ちょっとがっかり。

 とりあえずJS2Cの第二引数に与えられているjsもpyもsrc以下と言う事なのでsrcフォルダを見てみると、jsもpyも全てそこに存在している。とりあえずv8natives.jsの中身を見てみると、どうやらJavaScriptコードの様子。。。なのだが微妙に違う記法が混じっていて、関数名の先頭に%記号が付いている。。。。こんなのありだっけか?と思ったが、恐らくpyの方で何かしらの変換をするのかも知れない。

// Helper function used to install functions on objects.
function InstallFunctions(object, attributes, functions) {
  if (functions.length >= 8) {
    %OptimizeObjectForAddingMultipleProperties(object, functions.length >> 1);
  }
  for (var i = 0; i < functions.length; i += 2) {
    var key = functions[i];
    var f = functions[i + 1];
    %FunctionSetName(f, key);
    %FunctionRemovePrototype(f);
    %SetProperty(object, key, f, attributes);
    %SetNativeFlag(f);
  }
  %ToFastProperties(object);
}
とりあえずsrcフォルダ以下でOptimizeObjectForAddingMultiplePropertiesを検索キーとしてgrepをかけてみると以下のファイルが見つかった(subversion関連は除外)。
./src/v8natives.js
./src/runtime.cc
./src/runtime.h
./src/math.js
どうもOptimizeObjectForAddingMultiplePropertiesはruntime.ccで定義されているcの関数である事が分かった。と言う事は%が先頭についた関数はcで定義された関数を直接記述する特別な記法なのかも知れない。

次にlibraries.ccの中を見ると以下の通りで、正確にはsources[]の中身を見る必要があるが、やはりスクリプトをC形式の文字列データに置き換えて、V8からアクセス出来る様にしている様子。
// Copyright 2011 Google Inc. All Rights Reserved. 
// This file was generated from .js source files by SCons.  If you
// want to make changes to this file you should either change the
// javascript source files or the SConstruct script.
#include "v8.h"
#include "natives.h"
#include "utils.h"
namespace v8 {
namespace internal {
static const byte sources[] = { .....(数値の羅列).....};
static const char* raw_sources = reinterpret_cast<const char*>(sources);  // const byteをconst charにキャスト
  template <>
  int NativesCollection<CORE>::GetBuiltinsCount() { // ビルトイン関数の個数
    return 14;
  }
  template <>
  int NativesCollection<CORE>::GetDebuggerCount() {  // デバッガー関数の個数
    return 3;
  }
  template <>
  int NativesCollection<CORE>::GetIndex(const char* name) { // オブジェクト名からインデックスを導出
    if (strcmp(name, "liveedit") == 0) return 0;
 。。。
    if (strcmp(name, "json") == 0) return 13;
    return -1;
  }
  template <>
  int NativesCollection<CORE>::GetRawScriptsSize() { // 生スクリプトサイズ
    return 238136;
  }
  template <>
  Vector<const char> NativesCollection<CORE>::GetRawScriptSource(int index) { // 生スクリプトソースをインデックスをキーに取得
    if (index == 0) return Vector<const char>(raw_sources + 144015, 15179);
 。。。
    if (index == 13) return Vector<const char>(raw_sources + 138135, 5880);
    return Vector<const char>("", 0);
  }
  template <>
  Vector<const char> NativesCollection<CORE>::GetScriptName(int index) { // スクリプト名をインデックスをキーに取得
    if (index == 0) return Vector<const char>("native liveedit.js", 18);
 。。。
    if (index == 13) return Vector<const char>("native json.js", 14);
    return Vector<const char>("", 0);
  }
  template <>
  Vector<const byte> NativesCollection<CORE>::GetScriptsSource() { // スクリプトソースを取得
    return Vector<const byte>(sources, 238136);
  }
  template <>
  void NativesCollection<CORE>::SetRawScriptsSource(Vector<const char> raw_source) { // 生スクリプトの設定
    ASSERT(238136 == raw_source.length());
    raw_sources = raw_source.start();
  }
}  // internal
}  // v8
 しかし、スクリプトコードをV8から見る事がどんな状況で必要なのだろうか?ここのスクリプトコードをJITコンパイルしてからbuilt-inオブジェクトのメソッドは実行されている?少なくともbuilt-in関数のソースコードはJavaScriptでは見える必要はないので、JITが有力である気がする。となると独自のbuilt-in関数を組み込む場合にはjsファイルを作成して、SConsのスクリプトを書き換えれば、追加が出来る様になるとか?

うーん、もう少し調べたいが、遅いので今日はこれまで。

2012年2月22日水曜日

How to invoke v8 engine on chromium

少しWebKitから外れて、chromiumの調査をしてみる。

気になっていたのはchromiumのWebKitとv8の関係。WebKitはJavaScriptCoreを持っているのだが、一部を流用してv8が動いているのか、それともv8単体でJavaScriptを実現しているのか。そして、WebKitとv8の繋ぎはどうなっているのか。
とりあえずWebKitとv8の繋ぎをコードを追っかけてみた結果が下のシーケンス図。


WebCoreのScriptElementからScriptControllerのevaluate()を実行しているが、このScriptControllerはv8用とjs用の2種類が用意されていて、chromiumではv8用のコードがビルド時に使われ、そのままv8に流れて行く事になる。
次にV8Proxyでv8のライブラリを呼び出してv8の機能でプリコンパイル、コンパイル、実行、と処理を行っている。最終的な実行はi:Executtionで行っている様子。この構成を見る限り、v8は単独でJavaScriptを実現している様だ。まあ、v8のスタンドアローン版もある訳だし、敢えてブラウザの場合だけJavaScriptCoreと連動するってのも変な構成だし、至極当たり前な流れだ。

次はv8はJITコンパイラな訳だから、ネイティブコンパイルする条件を調べたい。

(2/22追記)
chromiumのコードにprintf()を追加してビルドしてみて分かったのだが、どうもV8Proxyから左側はchromiumのビルド時にターゲットから外れている様子。調べ直しだな。。。

(2/23追記)
v8はJITコンパイルするけれど、Firefoxのとは違って全て初回実行時にネイティブビルドすると言う事が分かった。全て初回ビルドか。。。