Hatena::ブログ(Diary)

Ko-Taのバ・ー・ルのようなもの

2016-10-22

4x4matrixのちょっとした最適化

luaに移植していたらさっぱり???なmatrix計算に出くわして思い出すのに30分かかりました。

もう6〜8年前のコードだしそりゃ忘れるって話ですよ…。

尚、今回はSIMDを使うとかそういう話では無いです。

行列の結合

mulとかcompositeとか色々言われる、行列同士のかけ算です。

function matrix4composite(m1,m2)
	local t = {
		m1[1+0]*m2[1+0] + m1[2+0]*m2[1+4] + m1[3+0]*m2[1+8] + m1[4+0]*m2[1+12],
		m1[1+0]*m2[2+0] + m1[2+0]*m2[2+4] + m1[3+0]*m2[2+8] + m1[4+0]*m2[2+12],
		m1[1+0]*m2[3+0] + m1[2+0]*m2[3+4] + m1[3+0]*m2[3+8] + m1[4+0]*m2[3+12],
		m1[1+0]*m2[4+0] + m1[2+0]*m2[4+4] + m1[3+0]*m2[4+8] + m1[4+0]*m2[4+12],

		m1[1+4]*m2[1+0] + m1[2+4]*m2[1+4] + m1[3+4]*m2[1+8] + m1[4+4]*m2[1+12],
		m1[1+4]*m2[2+0] + m1[2+4]*m2[2+4] + m1[3+4]*m2[2+8] + m1[4+4]*m2[2+12],
		m1[1+4]*m2[3+0] + m1[2+4]*m2[3+4] + m1[3+4]*m2[3+8] + m1[4+4]*m2[3+12],
		m1[1+4]*m2[4+0] + m1[2+4]*m2[4+4] + m1[3+4]*m2[4+8] + m1[4+4]*m2[4+12],

		m1[1+8]*m2[1+0] + m1[2+8]*m2[1+4] + m1[3+8]*m2[1+8] + m1[4+8]*m2[1+12],
		m1[1+8]*m2[2+0] + m1[2+8]*m2[2+4] + m1[3+8]*m2[2+8] + m1[4+8]*m2[2+12],
		m1[1+8]*m2[3+0] + m1[2+8]*m2[3+4] + m1[3+8]*m2[3+8] + m1[4+8]*m2[3+12],
		m1[1+8]*m2[4+0] + m1[2+8]*m2[4+4] + m1[3+8]*m2[4+8] + m1[4+8]*m2[4+12],

		m1[1+12]*m2[1+0] + m1[2+12]*m2[1+4] + m1[3+12]*m2[1+8] + m1[4+12]*m2[1+12],
		m1[1+12]*m2[2+0] + m1[2+12]*m2[2+4] + m1[3+12]*m2[2+8] + m1[4+12]*m2[2+12],
		m1[1+12]*m2[3+0] + m1[2+12]*m2[3+4] + m1[3+12]*m2[3+8] + m1[4+12]*m2[3+12],
		m1[1+12]*m2[4+0] + m1[2+12]*m2[4+4] + m1[3+12]*m2[4+8] + m1[4+12]*m2[4+12]
	}
	return t
end

うげぇ…すごい計算密度に頭がくらくらします。forで回そうが焼け石に水です。(cだと_m11とか名前使うところですが…)

これだと新たな行列が生成されて毎回メモリを食っちゃうので、以下のように一個目に反映させてecoしたりします。

function matrix4pushcomposite(m1,m2)
	local h1,h2,h3,h4

	h1 = m1[1+0]
	h2 = m1[2+0]
	h3 = m1[3+0]
	h4 = m1[4+0]
	m1[1+0] = h1*m2[1+0] + h2*m2[1+4] + h3*m2[1+8] + h4*m2[1+12]
	m1[2+0] = h1*m2[2+0] + h2*m2[2+4] + h3*m2[2+8] + h4*m2[2+12]
	m1[3+0] = h1*m2[3+0] + h2*m2[3+4] + h3*m2[3+8] + h4*m2[3+12]
	m1[4+0] = h1*m2[4+0] + h2*m2[4+4] + h3*m2[4+8] + h4*m2[4+12]

	h1 = m1[1+4]
	h2 = m1[2+4]
	h3 = m1[3+4]
	h4 = m1[4+4]
	m1[1+4] = h1*m2[1+0] + h2*m2[1+4] + h3*m2[1+8] + h4*m2[1+12]
	m1[2+4] = h1*m2[2+0] + h2*m2[2+4] + h3*m2[2+8] + h4*m2[2+12]
	m1[3+4] = h1*m2[3+0] + h2*m2[3+4] + h3*m2[3+8] + h4*m2[3+12]
	m1[4+4] = h1*m2[4+0] + h2*m2[4+4] + h3*m2[4+8] + h4*m2[4+12]
...省略
end

そろそろhatenaさんに怒られちゃう文字数なので省略します。

あんまり変わりませんが、実際の速度はこれで結構マシになったりします。以上でお膳立ては完了です。

回転行列と結合したい

本題。回転を行列に加えたいなって場合は以下のような感じで組むと思います。

local matrix1 = matrix4indentity() --単位行列
matrix4pushcomposite(matrix1 , matrix4rotatex(rotx)) --X軸回転を反映

毎回行う行列の結合が重いのなんのって。

なんですがX軸回転の行列なんて

function matrix4rotatex(r)
  local s = math.sin(r)
  local c = math.cos(r)
  local m2 = {}
  m2[1+0] = 1.0   m2[2+0] = 0.0   m2[3+0] = 0.0   m2[4+0] = 0.0
  m2[1+4] = 0.0   m2[2+4] = c     m2[3+4] = s     m2[4+4] = 0.0
  m2[1+8] = 0.0   m2[2+8] = -s    m2[3+8] = c     m2[4+8] = 0.0
  m2[1+12] = 0.0  m2[2+12] = 0.0  m2[3+12] = 0.0  m2[4+12] = 1.0
  return m2
end

と、縦に見て、2行目と3行目ぐらいしか使ってませんし、1行目と4行目は単位行列です。

最適化しましょう。

まず、回転と結合を1つの関数にまとめます。

function matrix4pushrotatex(m1,x)
  local s = math.sin(r)
  local c = math.cos(r)
  local m2 = {}
  m2[1+0] = 1.0   m2[2+0] = 0.0   m2[3+0] = 0.0   m2[4+0] = 0.0
  m2[1+4] = 0.0   m2[2+4] = c     m2[3+4] = s     m2[4+4] = 0.0
  m2[1+8] = 0.0   m2[2+8] = -s    m2[3+8] = c     m2[4+8] = 0.0
  m2[1+12] = 0.0  m2[2+12] = 0.0  m2[3+12] = 0.0  m2[4+12] = 1.0
  return m2
  
  local h1,h2,h3,h4
  h1 = m1[1+0]
  h2 = m1[2+0]
  h3 = m1[3+0]
  h4 = m1[4+0]
  m1[1+0] = h1*m2[1+0] + h2*m2[1+4] + h3*m2[1+8] + h4*m2[1+12]
  m1[2+0] = h1*m2[2+0] + h2*m2[2+4] + h3*m2[2+8] + h4*m2[2+12]
  m1[3+0] = h1*m2[3+0] + h2*m2[3+4] + h3*m2[3+8] + h4*m2[3+12]
  m1[4+0] = h1*m2[4+0] + h2*m2[4+4] + h3*m2[4+8] + h4*m2[4+12]
...省略
end

バーン。でかい…省略するね。

1行目と2行目は使ってないので削除しましょう。ついでにsin,cosも直接指定します。

function matrix4pushrotatex(m1,x)
  local s = math.sin(r)
  local c = math.cos(r)
  local m2 = {}
  -- m2[2+4] = c   m2[3+4] = s
  -- m2[2+8] = -s  m2[3+8] = c

  local h1,h2,h3,h4
  h1 = m1[1+0]
  h2 = m1[2+0]
  h3 = m1[3+0]
  h4 = m1[4+0]
  m1[2+0] = h1*m2[2+0] + h2*c + h3*-s + h4*m2[2+12]
  m1[3+0] = h1*m2[3+0] + h2*s + h3*c + h4*m2[3+12]
...省略
end

回転行列の残りの部分は0.0ですから、あらこれイラネ。

function matrix4pushrotatex(m1,x)
  local s = math.sin(r)
  local c = math.cos(r)

  local h2,h3
  h2 = m1[2+0]
  h3 = m1[3+0]
  m1[2+0] = h2*c + h3*-s
  m1[3+0] = h2*s + h3*c
...省略
end

となります。省略部分もこれと全く同じように最適化してできあがりです。

以上はx軸回転ですが、あとy軸、z軸、スケール、移動に関しても、全く同様の点順で最適化出来ます。

もっとケースを絞り込めば最適化も出来ますが、とりあえず汎用的に使うならこれぐらいでしょうか。

github

f:id:Ko-Ta:20161020233556p:image

  • github matrix.lua

https://github.com/Ko-Ta2142/disposable/blob/master/misc_matrix.lua?ts=4

適当においておきます。適当にお使いください。

クォーターニオンとかはありません。掃きだめにそこまで求めないでください:Q

githubのtab値かえられるの!?

デフォルトのtabspaceが8なのでムキィ!ってgithubですが、urlに「?ts=4」で変えられるんですね。

しらなかった

そんなの

2016-10-21

luaの内部最適化について

これメモリ確保してる?無駄に新しいの生成してない?など、luaの内部挙動についてマニュアルを見ても不明だった点を2,3書き残しておきたいと思います。

内部処理は今風でとても賢いです:Q

文字列の受け渡し

luaの引数の説明では「参照渡しされるのはテーブル」と表記されていて文字列に関しては明記されていません。てっことはもしかしてコピーされちゃうの?

というわけで、allocをフックしてメモリ確保の流れをログします。

local str = "test case !!!!!!!!!!!!!!!!"
custom.alloclog(true)
testcall(str)
print(str)
custom.alloclog(false)

function testcall(str)
  print(str)
end

custom.alloclog()はメモリ確保、解放ログ出力の自前命令、仮命令です。関数の呼び出し時にコピーされるか確認します。

一応参照渡しチェックと言うことで、元strの内容も出力しておきましょうか。

test case !!!!!!!!!!!!!!!!
test case !!!!!!!!!!!!!!!!

メモリ確保ログが出ないのでコピーされていません。”低レベル層”では参照渡しがされているのがわかります。

というわけで内部で連結してみましょう。

function testcall(str)
  str = str.."hoge!"
  print(str)
end
ptr:20890C0 osize:133 nsize:138 usage:244K
ptr:23AB9B0 osize:4 nsize:155 usage:244K
test case !!!!!!!!!!!!!!!!hoge!
test case !!!!!!!!!!!!!!!!

内部連結分の確保が現れましたが大元strの内容はちゃんと維持されますので、”高レベル層”では値渡しでコピーされているかのような振る舞いです。

ちゃんと今風な言語の文字列の動き(値渡しのような振る舞いが出来る文字列)で賢い文字列でした。ありがたい!

文字列においては変更が無い限り生成が発生しないので、数字などと同じコスト(リスク無し)で渡せます。

なので、

function hogeclass:getdata()
  return self.data
end
function hogeclass:setdata(d)
  self.data = d
end

というような、dataが100MBをこえる大きなバイナリデータのやりとりにgetter/setterを挟んでもコストが発生することはありません。安心安全。今すぐインストールです。


おまけ。

luaコンパイルにはちゃんと固定値(const)判断っぽいようなのもあるようで、

custom.alloclog(true)
local str = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
custom.alloclog(false)

このコードが実行されるときに"aaaaaaa...."のメモリが確保されることはありません。低レベル層では固定値"aaaaaaaaaaa...."の参照渡しで処理されます。

なので、関数の外に固定値として「const_name="hogehoge"」として宣言しなくても最適化してくれるようです。

ただし、テーブルはその限りではありませんのでご注意ください。

functionのキャッシュ

次はfunctionのコンパイル挙動について。

自分がよく使って怒られる、関数内関数みたいなものの場合のコストについてです。

function hoge(x1)
  function inhoge(x1,x2)
    return x1*x2
  end

  return inhoge(x1+1,x1+2)
end

もうちょっと低レベルに展開すると

function hoge(x1)
  local inhoge = function(x1,x2)
    return x1*x2
  end

  return inhoge(x1+1,x1+2)
end

となり、inhogeに無名関数が突っ込まれる形になります。こうしてみると、hoge()が呼ばれるごとにinhoge()のコンパイルが発生しそうですよね。

それを調べるためにinhoge()の関数ポインタを出力させます。

function hoge(x1)
  local inhoge = function(x1,x2)
    return x1*x2
  end
  print( tofunction(inhoge) )
  return inhoge(x1+1,x1+2)
end
function: 023D3998
function: 023D3998
function: 023D3998
function: 023D3998

全部同値が返ってきたので、function(関数)にはちゃんと使い回せるようにキャッシュみたいなのがあるようです。賢い。

えー同じになってるだけじゃ無いの〜と言うわけで100000ループテストで、関数を外に出した場合(読込時にコンパイルされる位置)と比較してもほぼ同値でしたので問題ないでしょう…。

これで安心して関数内関数をご使用ください。

ところでconsolas

win10になってからconsolasフォントは綺麗ですよね。VScodeでフォントとしての格を上げました。

そんなconsolasなんですが、多のエディタやHTMLで指定すると日本語フォントがイマイチ…VScodeのように綺麗に表示してくれません。(ものによってはおまえはプロポーショナルを強引に固定してるのかってぐらい文字が左右に寄っちゃったり…)

VScodeの設定では以下のような感じで日本語設定は有りません。

Consolas, 'Courier New', monospace

一応windows.FontLinkレジストリを見てみても、リンク定義はされていません。

YuGothicでもないような綺麗さですし…。

f:id:Ko-Ta:20161022192013j:image

正解はメイリオでした。

あれ?でも自分はwin7からのアプグレ組だから良いけど、win10クリーンインストールだと無いんじゃ…。

2016-10-20

luaのメモリローダー

lua5.1も気がつけば5.3です。早速使ってみたのですがstaring.pack/unpackとシフト演算子の実装がとてもとても嬉しいです。ぐへへへって声が出ちゃいました。もうこれは戻れないなと思いつつもjua-jitの関係で5.2から先は…なのでCでpack/unpack作ってました。不毛…。

luaに関してはまだ若かりし5.1の頃の記事が残ってますが、良い加減ちゃんと書き直したいので1,2,3を飛ばしてloader周りから書き直したいと思います。出来るだけ被らない内容で。

オンメモリ読み込み

組み込みで避けて通れないのがオンメモリ読み込みとその実装です。ファイルから読んじゃダメってやつです。デバッグは良いんですけどね。

luaの場合、スクリプト読み込みは「lua_load」を使って任意ストリーム(データを返す関数ポインタ)でロードできます。任意ポインタを関数callback時に渡せるので…

と思ったらもっと便利な「luaL_loadbuffer」があるのでこっち使えば良いですね。なんだよーもうloadでつくっちゃったよー。

  • luaL_loadbuffer

http://milkpot.sakura.ne.jp/lua/lua52_manual_ja.html#luaL_loadbuffer

ちなみに、loadstringという手もあるのですが、これだとチャンク名が設定できない関係上デバッグの際に読み込みファイル名がゲットできないという致命傷がございますのでご注意くださいませ。なんせデバッグは重要ですので。

それrequireだとダメですよね

はいそうです。これだと内部requireのロードが拾えません。なので、まず読み込むファイル、そしてrequireのローダを乗っ取るの2弾構えが必要です。

本家マニュアルによりますと

  package.searchers
このテーブルの各エントリは 検索関数 です。 モジュールを検索するとき、 require はモジュール名 (require に指定した引数) を引数にしてこれらの各検索関数を昇順で呼びます。 検索関数は別の関数 (モジュールの ローダー) およびそのローダーに渡される追加の引数、 またはモジュールを見つけられなかった理由を説明する文字列 (または何も言うべきことがなければ nil) を返すことができます。

任侠沈没に言ってわからん。

要約すると、requireで指定した文字列を渡すので、それをチャンク(load)にして返してください。エラーならそのメッセージを返してくださいということです。

言ってるかな?…言ってるかも…。

function onmemoryloader(modulename)
  local filename = modulename..".lua"
  local data = custom.load(filename)
  local chank,msg = load(data,filename)
  if chank~=nil then
    return chank
  else
    return msg
  end
end

最小構成はこんな感じかな?こんな感じかも。

「custom.load()」は仮の組み込み関数で、ファイル名を指定するとその内容の(nilを含んだ)文字列を返す命令とします。仮です。

luaに置いては基本バイナリは文字列で扱います。でもnilがあるとそこで止まっちゃうので「lua_pushlstring」でnilを許容した文字列をluaに送り込みます。という点が特殊なので作成の際は注意してください。

話が脱線すると、このあたりのバイナリデータの取り扱いの仕様もあるので、luaの標準文字コードは5.3でutf8でほぼ決定なのかなと思います。無理にutf16にしなくてもね。文字列やりとりする際は変換が必要なんですが、utf16とutf8はかなり高速に相互変換出来るのでまぁいいかなーって思います。

で、これをloaderにすり替えます。

if _VERSION=="Lua 5.1" then
	package.preload[1]=onmemoryloader
end
if _VERSION=="Lua 5.2" or _VERSION=="Lua 5.3" then
	package.searchers[1]=onmemoryloader
end

一応バージョンによって名前が違うので、両対応しておきましょう。

とりあえず一番最初のサーチに入れないと、普通にlua標準ファイル読み込みしちゃうので、一番最初に入れます。

この場所が最良なのかどうか、と言う点は私はわかりませんので公式に聞いてください。

githubにおいておきます

f:id:Ko-Ta:20161020233556p:image

  • github - memoryload.lua

https://github.com/Ko-Ta2142/disposable/blob/master/memoryload.lua?ts=4

まだgithub使い慣れてませんが適当に組んだものを置いておきます。zlibでお使いください。

使い方はこれをrequireすれば、それ以降のrequireで作用します。

最小限動作させるには「memoryload.onload」にロードしてバイナリ文字列を返す任意のイベント(function)をセットする必要があります。

utf8ベースで書かれてますので、sjisベースだとbom付きで失敗します。

utf16のチェックコードは付いてますが実装部分はありません。githubに変換luaコードもぶち込んでありますが、スクリプトファイルレベルではまぁ要らないよね。と思います。

弊害と利点

この方法ではDLLの読み込みは出来ません。やったら怒られました。なので、luasocketなどのモジュールと併用する際は気をつけてください。

また、moonscriptのようにloaderを乗っ取ってスクリプトをluaに変換するもの(コンパイル済は大丈夫)とも衝突します。その場合は、デバッグとリリースでloaderの順序を変えたりして回避するのが一番簡単かとおもいます。

利点は簡単なマクロの実装などもありますが、まずはutf8-bomなどの文字コード対応が一番幸せになれるかなって思います。

2016-10-10

luarocks(lua5.2 DLL) on VS2015(windows)

前に失敗したのでリベンジ。

VC(VS2013)にluasocketの.makeみながら全ソースぶっ込んで作ったことはあったのですが、monnscript使おうと思ったらモジュールの依存関係があってこれは辛いなぁ…ってことでluarocksさん。

luarocksと言われても…

lua用モジュール管理ツールです。と言われてもちょっとピンときませんよね。

luaが必要最小限の機能しか有していないので、拡張はlua言語ではなくDLLに頼ることになります。となるとコンパイルが必要。その環境依存性の高いコンパイルを吸収してくれる筈のツールになります。筈。

目的のモジュール名をお願いすると、ダウンロードしてきてコンパイルしてへいおまち!してくれる筈の夢のツールです。ありがたい!

とってもかんたんだよ(windows除く)

linuxなどでは特に問題なくluarocksがapt-getやyumなんかで出来ちゃうのですが、windowsだとそうもいきません。具体的にはコンパイルにluaのライブラリ「lua.lib」が必要なのですが、これは自力で作る必要があります。

コンパイルが成功するとluaのあるところに「share」と「lib」というフォルダを作ってluaソースと作ったDLLをそれぞれ別々に出力します。

って説明が無いので、最初迷いますよねこれ…。

まずはlua.lib

いきなりですがluaそのものをコンパイルする環境構築が必要です。自分はとりあえずVC2015で試してみます。

説明は省きますが、luaの公式ページからソースを一式ダウンロードして、空のライブラリプロジェクトに全ソースファイル突っ込んでエラーを見てちょっと抜いて、コンパイルして終わりです。詳しくは長くなるので割愛。

必要最低限な設定では以下のような感じ。

構成の種類:スタティック ライブラリ (.lib)
マルチ バイト文字セットを使用する:マルチ バイト文字セットを使用する(luaはこれ重要)
ターゲットの拡張子:.lib
プリプロセッサ:LUA_BUILD_AS_DLL;_CRT_SECURE_NO_WARNINGS
SDLチェック:いいえ
すべての既定ライブラリの無視:はい (/NODEFAULTLIB)

それほど難しくありませんが、後記のエラーのためもうちょっと細かい設定が必要になりますが、今のところはパスします。

ほんとにこのライブラリ大丈夫?

これはやらなくて大丈夫です。

exeでもいいのですが、自分はDLLを使うプログラムがあったのでそれで整合性をチェックしました。新しく空のプロジェクトを作ってDLLに設定、「LUALIB_API,LUA_API」があるソースをぶち込んでコンパイル。アプリに組み込み後、相当でかいlauコードが動いたので大丈夫でしょう。

luarocksダウンロードしてインストール!と思ったら5.1入ってるんですけど…

最初のインストールに使うインストーラー的なものなので問題ありません。が、そのままインストールすると5.1になっちゃうので、別途lua5.2が必要です。上の工程でexeも作っていればそれを、無ければ公式からバイナリが取得できます。

以下のフォルダ構成フォーマットに従ってファイルをコピーして、大元「c:\lua52\bin\」をwindowsの環境変数「path」に登録します。

c:\lua52\bin\lua52.exe
c:\lua52\bin\lua52.lib
c:\lua52\bin\lua52.dll
c:\lua52\include\*.h    //ソースファイルのヘッダだけ抜き出したもの(本当はもうちょっと絞り込むんですが)

落としたluarocksのinstall.batでインストール。「linstall /?」と打ち込むと設定が出てくるので以下のようなかんじで指定の場所にインストールします。(programfilesは権限が必要になるので今回は回避しておきます)

install /LV 5.2 /LUA c:\lua52\ /P c:\LuaRocks52

/LV:luaバージョン
/LUA:luaのあるところ
/P:luarocksのインストール先

ベコベコベコとファイルのサーチが始まります。この時にベースのluaのバージョンが決定します。なので5.3を用意してこのインストーラでまたインストールすれば5.3のluarocksが共存できます。

途中で止まった場合はそのパスを確認してください。現段階ではファイルコピーと設定ファイルのパス訂正ぐらいしかしてませんので、パスが合っていれば概ね大丈夫です。

ではさっそくモジュールげっと

最初にゲットするものといえばluasocketというのが世の習わしです。pokemonでいうところのpikachuuuuです。

c:\luarocks52\>luarocks install luasocket

わくわく感もつかの間、エラーが出ます。clってなに?

忘れてました、VCのコンパイラを使うので「デベロッパーコマンドプロンプト」で開き直して再度チャレンジしましょう。

するとまたエラーが出るはずです。LINK1107!なにやらDLLを組み込んでコンパイルしようとしてます。そこはlibつかってくださいluarockさん。

config-5.2.luaの修正

「config-5.2.lua」がluaだけど設定ファイルになります。中身はテーブルです。ってluaそのものです。こんな感じにDLLになってる「LUALIB」のところを修正します。

...
variables = {
    MSVCRT = 'MSVCRT',
    LUALIB = 'lua52.lib'
}
...

これで一応コンパイルが通るとは思いますが、なにやらリンク時に「???が再読込されてる」とかなんとか言われます。

どうやらlib生成のコンパイラオプションがluarocksの時と異なるので怒られてます。なので、VCを開いてlibのプロジェクト設定を変更します。

luarocksのログを見ると

cl -nologo -MD -O2 -c ?????

となってますので、プロジェクト設定では以下に対応します。

著作権情報の非表示:はい (/nologo)
ランタイムライブラリ:マルチスレッドDLL (/MD)
最適化:実行速度の最大化 (/O2)

これでもう一回rualockさんにお願いすると〜

link -dll -def:core.def -out:socket/core.dll c:/lua5.2.4/bin/lua52.lib src/luasocket.obj src/timeout.obj src/buffer.obj src/io.obj src/auxiliar.obj src/options.obj src/inet.obj src/except.obj src/select.obj src/tcp.obj src/udp.obj src/wsocket.obj ws2_32.lib
Microsoft (R) Incremental Linker Version 14.00.24215.1
Copyright (C) Microsoft Corporation.  All rights reserved.

   ライブラリ socket/core.lib とオブジェクト socket/core.exp を作成中
luasocket 3.0rc1-2 is now installed in c:\lua5.2.4\ (license: MIT)

わーい。エラー無く完了。お疲れ様です。

すっかり忘れている頃ですが、出力はluarocksではなくluaの方の「share」と「lib」にされています。

では使ってみよう…

出力されたファイル群をフォルダ構成を守ってluaを実行する所へコピペすれば準備完了です。

サンプルスクリプトを作って実行させましょう。luasocketのおまじないはこうです。

require("socket")

結果はこうです。

multiple vms detected.

深刻なエラーです。どうやらluaコアと読み込むDLLのlua内部情報が異なると出るそうです。

あぁー、先ほどlib更新したからDLLとEXEも更新しないとということで、この2つもプロジェクト設定を上記に従い再設定、再コンパイル、組み込みます。

multiple vms detected.

ぐえー。どうにもダメみたいです。

multiple vms detectedってなに?

疲れたのでluaの大元のソースをご開帳。

LUALIB_API void luaL_checkversion_ (lua_State *L, lua_Number ver) {
  const lua_Number *v = lua_version(L);
  if (v != lua_version(NULL))
    luaL_error(L, "multiple Lua VMs detected");
  else if (*v != ver)
    luaL_error(L, "version mismatch: app. needs %f, Lua core provides %f", ver, *v);
...

versionチェックなんですが、version値の比較の他に、アドレスの比較もやってるようです。

今回はVM Errorって言ってるのでそのアドレスで引っかかってるみたいです。何のアドレスを比較してるの?

LUA_API const lua_Number *lua_version (lua_State *L) {
  static const lua_Number version = LUA_VERSION_NUM;
  if (L == NULL) return &version;
  else return G(L)->version;
}

「static const」のアドレスですか…。きっとluasocket側でcheckversionをcallされてる弾かれてるんですね。luaの大元libをDLLに使ってるので一部2重コードになってますのでアドレスも違います。

つまりlua本体DLLから全命令をcallしなければなりません。なので最初の.confの設定にDLLが指定されていたのでしょう。エラーでますけど。

なーるほーどねーそういうことだったのか…。

まずDLLをコンパイルする

libだけでいけるかなーと思ったんですが、DLL生成が絶対必要になります。

厳密にはDLLじゃなくて、DLL出力した際に吐き出されるlibが必要です。とはいえ1:1対応の存在で別々に取って来てもダメなので、結局コンパイルして両方生成します。

DLLの生成方法の詳細は他のページでご覧ください!

一番簡単なのは全ソースぶち込んでエラー出たのを抜くのがてっとり早いです。(うえでは一端全部入りlibつくってからDLL吐き出してますが、必要なソースの選定に.make見ないとイケナイので面倒です)

lua52.dll    486kb
lua52.lib    30kb

容量は目安です。全部入りlua.libに比べてきわめて小さいlibが出来ます。このlibはDLLに命令をそのままリダイレクトさせるためだけのlibです。

これ使えって事だったの……名前一緒だしわからないですよね……。

この2つを「c:\lua52\bin\」にぶちこんで、「ruarocks install luasocket」してやります。

   ライブラリ socket/core.lib とオブジェクト socket/core.exp を作成中
luasocket 3.0rc1-2 is now installed in c:\lua5.2.4\ (license: MIT)

メッセージは上記と全く同じですが、出力されるcore.dllなどの容量が若干小さくなります。

これをluaの組み込み実行環境に持って行って

require("socket")

エラー無しで通ると思います。

終わり。疲れた…。

おまけ

ほんとは「moonscript」使いたかったんです。

試しに「luarocks install moonscript」して持って行ったら「multiple vms detected.」。え?なんで?

removeしてもinstallリストから除外されないしluarocksが壊れてるのかな?このモジュールは2つのモジュールの上に成り立っているので、それらが更新されていないとかそんな感じなのかな。一応リスト見て全部叩いては見たものの…。

結局再インストールして再コンパイルしたら上手くいきました。

2016-10-09

V8エンジン

JavaScript組み込みエンジン「V8」にちょっとだけ手を出してみました。

なぜ?lua言語にちょっとイライラしたから…つい魔が。

Chormiumeプロジェクトのアレです。ブラウザからJavaScriptエンジン部分だけをぶち抜いたもので、みんなもプログラムに組み込んでみよう!というアレです。

windowsでもほら簡単!って事だったんですけど…気軽に言ってくれるなぁ。

ビルド方法が大きく変わりました

ふふーん。なーんだgoogleからgit.cloneしておっぱいそんいれてgypするれば終わりじゃん!って思ってたんですがダメでした。

どうやらファイルが足りないみたいです。確認したところではClangあたりでしょうか。

というわけで、googleさんからではなく、Chormiumeプロジェクトさんの方から一式セットを頂くことにします。

と、数行で済ませてますがすでに1日が経過して若干やつれてます。

  • depot tools

https://www.chromium.org/developers/how-tos/install-depot-tools

(これもURL変わったりするのでだめな場合はgoogleさんに「depot tools V8 install」と聞いてみてください)

このあたりは過去と同じなので詳細は省きます。

これで揃うのは、python、git、gyp環境(ninja含む)です。

展開して、フォルダのルートで以下のバッチを実行するのですが、いくつか(DEPOT_TOOLS_WIN_TOOLCHAIN,GYP_MSVS_VERSION)windows環境変数に登録が必要なので、以下の内容なバッチファイルを作って一時的に登録させる手法を使うと楽ちんだと思います。

  • depot tools update/setup

depottools展開フォルダのルートでバッチ作成後実行

gclient
  • ツールパス指定

windows環境変数(ユーザ)にbin,root,gypのパスを追加。

f:id:Ko-Ta:20161009104655p:image

  • git.clone

お好きな場所にフォルダを作ってそこでバッチ作成後実行。

VSの環境に合わせて「GYP_MSVS_VERSION」は変えてください。私は2015でやりました。

set DEPOT_TOOLS_WIN_TOOLCHAIN=0
set GYP_MSVS_VERSION=2015
fetch v8

上手くいかない場合は、コマンドプロンプトを終了させたり、パスが通っているか「~> path」で確認してみてください。

というのもね、Explolerで「シフトを押しながらフォルダを右クリックでコマンドプロンプトを開く」が便利で使うんですけど、環境変数の反映は「次回”親”アプリケーション起動時」なんです。

この時、開いたコマンドプロンプトは子プロセスなので、開き直しても反映されないのをすっかり忘れて2時間時間を無駄にしました…。調べると設定するとイベントが飛んでくる筈なんですけどね…対応していないみたいで。

やったーコンパイル…できない!

コンパイルの流れは「gypさんにお願いするとsln(VSプロジェクトファイル)が貰えるのであとはVS上でやる」というのが以前までの方法でした。

これが新しくなって「gypさんにお願いするとninjaが出てきてlibまでケジメしてくれる」ということで根本的に変わってまして、slnを吐いてくれません!おのれninjaslayer!

ともあれ、とりあえずはninjaであってもコンパイルを通さないと行けませんのでそこまではやりましょう。

今回も環境変数の登録があるので以下のようなバッチをcloneした一番上(.gitignoreがあるところ)作り、念のためVisualStudioコマンドプロンプトから実行します。(風通のでも行けた気がするような)

set DEPOT_TOOLS_WIN_TOOLCHAIN=0
set GYP_MSVS_VERSION=2015
python gypfiles/gyp_v8

なんだか幾つかのファイルでエラーが出てると思います。

よくある日本語環境下でのsjis認識によるものらしいので、この際USモードでOSを再起動させます。

「コントロールパネル−言語−地域−管理−システムローケル」で「英語(米国)」にして再起動。

(これがユーザー単位に切り替えできたら良いのですが…プログラムのUnicodeチェックするときに使ってますがいつも再起動面倒です)

これで通るはずです:Q

終わったものの、特にメッセージも無いのでどこに何が生成されたのかさっぱりですが、これで「out\」というフォルダにninjaのmakeファイルが展開されました。

なのでコンパイルしないと。

ninjaと打ち込むと「build.ninja」のあるフォルダを指定してね!って言われるので以下のようにoutのなかの「任意のフォルダ」を指定してください。

ninja -C out/release

これでとりあえずはninjaコンパイルは上手くいく行ったと思います。おつかえさま:Q

ほほーlibファイル群がよゆーで10Mこえてるぜー!でけぇ!

ちげーよ!slnが欲しいんだよ!

ですよねー。

ninjaのマルチプラットフォームコンパイルシステムは素晴らしいとは言え、VSでプロジェクト内容をGUIでチェックできる点はとても偉大です。

  • Cannot generate solution file of v8 with gyp for visual studio 2013

http://stackoverflow.com/questions/38015585/cannot-generate-solution-file-of-v8-with-gyp-for-visual-studio-2013

KAOMOJIまでつかって実にチャーミングです。泣いてます。かわいそう。

私なんてもう流す涙も枯れてしまったわとかそんな危険な状態です。

gypさんに以下のようにお伺いを立てればslnが出力されます。

set DEPOT_TOOLS_WIN_TOOLCHAIN=0
set GYP_MSVS_VERSION=2015
gn gen --ide=vs out/Default

やった!これで「out\default」にslnとその部下達(プロジェクト)が吐き出されてました。

うげぇーもうだめしぬ…。

コンパイルはごめんなさい

とりあえずここまで。

コンパイルはまだまだ細かい設定とかあると思います。絶対ありますが、とりあえずここまでで許してください。

結局使ってるの?

使ってません…。今のところ。力尽きました。

いつか使うかも知れません。