Kyoto Tycoon+Lua-JITæ¡å¼µ+MessagePack=ç¡æµ
ã¯ããã«
http://fallabs.com/blog-ja/promenade.cgi?id=100
ã¾ã¨ã
ãã¼ã¿ãã¼ã¹ãµã¼ãä¸ã§ã®ã¹ã¯ãªããã£ã³ã°ç¨éã§ã¯Luaã¯æå¼·ã®ä¸è§ã§ãããããã¦KCã®Visitorã¤ã³ã¿ã¼ãã§ã¤ã¹ã¨ã®ç¸æ§ãæ群ã§ãããTTã®æ代ã«ãããªãé¢ç½ããã¨ãã§ããããKTã«ãªã£ã¦ããããã«ä¾¿å©ã«ä½¿ããããã«ãªã£ãããã²ãã²ãã試ãããã
2å¹´åã®ä½è ã®è¨äºã§ããï¼ã¾ãã«é¢ç½ãã¦ãã¾ãã¾ããï¼
ä»åã¯ï¼ããã«ç°¡åã«æ§è½ãä¸ããå®é¨ããã£ã¦ã¿ã¾ãããï¼
Luaã¨Lua-JITã§ã¯ï¼ã©ããLua-JITã®æ¹ãæ§è½ãè¯ããããªã®ã§ï¼
Lua v.s. Lua-Jit - なぜか数学者にはワイン好きが多い
Lua-JIT対å¿ã®Kyoto Tycoonãä½ãã¾ãï¼ããã¯ï¼Luaã®ä»£ããã«Lua-JITããªã³ã¯ããã ãã§ä½ããã¨ãã§ãã¾ãï¼
ãã®ä¸ã§ï¼Kyoto Tycoonãç°¡åã«æ¡å¼µãã¦ã¿ã¾ãï¼
Kyoto Tycoonã§ã¯ï¼Luaã¹ã¯ãªããã£ã³ã°æ¡å¼µããã¶ããã ãã§ï¼åºæ¬çãªKVSã®Set/Getãããã£ããã¨æ©è½æ¡å¼µã§ãã¾ãï¼
ã¾ãã¯ææ©ãå®é¨ããããã«ï¼æ§è½ã¯ä½ãæ°ã«ããçç£æ§ã®é«ãã®ããã«Rubyã使ãã¾ãï¼
Rubyã®Kyoto Tycoonãã¤ã³ãã£ã³ã°ã¯ï¼ãªãã£ã·ã£ã«ã«ãµãã¼ãããã¦ãã¾ãï¼ã¨ããã®ã¯ã¦ã½ã§ï¼ã½ã±ããéä¿¡ãHTTPéä¿¡ã§ã¢ã¯ã»ã¹ãã§ããã®ã§ï¼Rubyãããªãã¦ããªãã£ã·ã£ã«ã«ãµãã¼ãããã¦ããã¨è¨ãã¾ãï¼
Fundamental Specifications of Kyoto Tycoon Version 1
次ã«ï¼MessagePackã®Rubyãã¤ã³ãã£ã³ã°ï¼ããã¯ããã¤ãçºè¡¨ããã¦ãã¾ããï¼ä¸çªã·ã³ãã«ãªgemã§ã¤ã³ã¹ãã¼ã«ã§ãããã®ã使ãã¾ããï¼MessagePackã®ä½è
ã«ãããã®ã§ããï¼
msgpack | RubyGems.org | your community gem host
Luaã«å¯¾ããMessagePackã沢山ããã¾ããï¼Lua-JIT対å¿ã§ä»ã©ã¤ãã©ãªã¸ã®ä¾åãå°ãªããã®ãæ¢ãã¾ããï¼
幸ãï¼ã¾ã¨ãè¨äºãæ¸ãã¦ããã¦ãã人ããã¾ããï¼
The state of MessagePack in Lua · GitHub
The state of MessagePack in Lua
So, to sum things up:
Lua-JITã§ä¾åãå°ãªãã¨ãããã¨ã§ï¼ç§ã¯luajit-msgpack-pureã使ãã¾ããï¼
Kyoto Tycoonã®Set/Getå½ä»¤ã®ã¹ã¯ãªããã«ããä¹ã£åã
Kyoto Tycoon(ããã¯ã¨ã³ãã¯Kyoto Cabinet)ã¯ï¼åºæ¬çã«ãã¼ãããªã¥ã¼ãæååã ããããã¤ããªãã¼ã¿ã ãããä½ã§ãçªã£è¾¼ãã¾ãï¼åç´ãªæåå-æååã®KVSãããªãã¦ï¼é åãããã·ã¥ãªã©ã®æ§é ãæã¡è¾¼ã¿ããï¼é常ã¯JSONãªããã«ã·ãªã¢ã©ã¤ãºãã¦çªã£è¾¼ãã®ã§ããï¼ä½åï¼JSONã¯ç©ºéå¹çãæéå¹çãæªãã§ãï¼JSONã«ãã¤ããªãæã¡è¾¼ãã BSONãï¼ç¹ã«å¹çãè¯ãããã§ã¯ç¡ãã£ãã¨ããã®ãç§ã®çµè«ï¼JSON/BSON/MessagePack 処理速度・データサイズ完全比較 - なぜか数学者にはワイン好きが多い
ããã§ï¼getãsetãLuaã§æ¸ãã¦ãã¾ãã¾ãï¼
éä¿¡ãããã³ã«ãï¼é å¼µã£ã¦HTTPãããªãã¦ãã¤ããªãããã³ã«ã§è¡ãã¾ãï¼
ã¨ã¯è¨ã£ã¦ãï¼éä¸ã®ãããã³ã«ã¯getãsetãå¦çããLuaã¹ã¯ãªããã«ã¯é¢ä¿ãç¡ãã®ã§ï¼ã¾ãé ã«get/setã«ã¼ãã³ããããåºãã¦ã¿ã¾ãï¼
- é常ã®Kyoto Tycoon Serialize/Deserializeã«ããget/set
-- set a record by Kyoto serializer function set(inmap, outmap) -- Bulkã§ã¯ãªãï¼Key/Valueã®ãã¢ãä¸çµãã¤setãããã¨ãä»®å® local key, value = next(inmap) local err = false local serial = kt.mapdump(inmap) -- visitor function local function visit(key, value, xt) return serial end -- perform the visitor atomically if not db:accept(key, visit) then kt.log("error", "inserting a record failed") err = true end if err then return kt.EVEINTERNAL end return kt.RVSUCCESS end -- get a record by Kyoto deserializer function get(inmap, outmap) -- getã¯ï¼åºå®æååkeyã«å¯¾ããvalueãkeyåã¨ä»®å® local key = tostring(inmap.key) if not key then return kt.RVEINVALID end local serial = db:get(key) if not serial then return kt.RVELOGIC end local rec = kt.mapload(serial) for rkey, rvalue in pairs(rec) do outmap[rkey] = rvalue end return kt.RVSUCCESS end
æ®éã«HTTPã®ASCIIãããã³ã«ã§ã¢ã¯ã»ã¹ãããã¨ãã§ãã¾ãï¼ä¸è¨ã®ããã°ã©ã ãkt_ex1.luaã¨ãããã¡ã¤ã«åã§ä¿åããã¨ããã¨ï¼Kyoto Tycoonãµã¼ãã®ç«ã¡ããã¯ãããªæãã§ãï¼
ktserver -ls -scr kt_ex1.lua data.kch
cURLã§ã¢ã¯ã»ã¹ãã¦ã¿ã¾ãï¼setã¯ãããªæãï¼
> curl "http://192.168.0.100:1978/rpc/play_script?name=set&_abcde-key1=abcde-value1"
ãabcde-key1ãããã¼ã§ï¼ãabcde-value1ããããªã¥ã¼ã§ãï¼getã¯ãããªæãï¼
> curl "http://gauss:1978/rpc/play_script?name=get&_key=abcde-key1" _abcde-key1 abcde-value1
ã¯ãï¼å¼æ°ã§æ¸¡ãããã¼ãabcde-key1ãã¨ï¼ããã«å¯¾ããããªã¥ã¼ãã¿ãåºåãã§é£ãã§ãã¾ããï¼
- ãã¤ããªãã¼ã¿ãç¡çããStringã«ã·ãªã¢ã©ã¤ãºï¼ï¼ï¼ãã¦KVSã«æ ¼ç´ãããã¼ã¸ã§ã³
-- set a record by String serializer function setraw(inmap, outmap) -- Bulkã§ã¯ãªãï¼Key/Valueã®ãã¢ãä¸çµãã¤setãããã¨ãä»®å® local key, value = next(inmap) if not key then return kt.RVEINVALID end local err = false local serial = value -- visitor function local function visit(key, value, xt) return serial end -- perform the visitor atomically if not db:accept(key, visit) then kt.log("error", "inserting a record failed") err = true end if err then return kt.EVEINTERNAL end return kt.RVSUCCESS end -- get a record by String deserializer function getraw(inmap, outmap) -- getã¯ï¼åºå®æååkeyã«å¯¾ããvalueãkeyåã¨ä»®å® local key = tostring(inmap.key) if not key then return kt.RVEINVALID end local serial = db:get(key) if not serial then return kt.RVELOGIC end local rec = serial outmap.value=tostring(rec) return kt.RVSUCCESS end
æ®éã«HTTPã®ASCIIãããã³ã«ã§ã¢ã¯ã»ã¹ãããã¨ãã§ãã¾ãï¼ä¸è¨ã®ããã°ã©ã ãkt_ex2.luaã¨ãããã¡ã¤ã«åã§ä¿åããã¨ããã¨ï¼Kyoto Tycoonãµã¼ãã®ç«ã¡ããã¯ãããªæãã§ãï¼
ktserver -ls -scr kt_ex2.lua data.kch
cURLã§ã¢ã¯ã»ã¹ãã¦ã¿ã¾ãï¼setã¯ãããªæãï¼
> curl "http://192.168.0.100:1978/rpc/play_script?name=setraw&_abcde-key1=abcde-value1"
ãabcde-key1ãããã¼ã§ï¼ãabcde-value1ããããªã¥ã¼ã§ãï¼getã¯ãããªæãï¼
> curl "http://gauss:1978/rpc/play_script?name=getraw&_key=abcde-key1" _abcde-key1 abcde-value1
ã¯ãï¼å¼æ°ã§æ¸¡ãããã¼ãabcde-key1ãã¨ï¼ããã«å¯¾ããããªã¥ã¼ãã¿ãåºåãã§é£ãã§ãã¾ããï¼
- ãã¤ããªãã¼ã¿ãMessagePackã使ã£ã¦ã·ãªã¢ã©ã¤ãºãããã¼ã¸ã§ã³
-- set a record by MessagePack function setmp(inmap, outmap) -- Bulkã§ã¯ãªãï¼Key/Valueã®ãã¢ãä¸çµãã¤setãããã¨ãä»®å® local key, value = next(inmap) if not key then return kt.RVEINVALID end local err = false local serial = value -- visitor function local function visit(key, value, xt) return serial end -- perform the visitor atomically if not db:accept(key, visit) then kt.log("error", "inserting a record failed") err = true end if err then return kt.EVEINTERNAL end return kt.RVSUCCESS end -- get a record by MessagePack deserializer function getmp(inmap, outmap) -- getã¯ï¼åºå®æååkeyã«å¯¾ããvalueãkeyåã¨ä»®å® local key = tostring(inmap.key) if not key then return kt.RVEINVALID end local serial = db:get(key) if not serial then return kt.RVELOGIC end local rec = serial outmap[key]=tostring(rec) return kt.RVSUCCESS end
å®ã¯MessagePackã®å¦çã¯ï¼ã¯ã©ã¤ã¢ã³ãå´ã§è¡ããã¨ãæ³å®ãã¦ããã®ã§ï¼setraw/setmpã¨getraw/getmpã¯ï¼å
¨ãåä¸ã§ãï¼
é常ã®get/setã®ã¿ãï¼ã·ãªã¢ã©ã¤ãº/ãã·ãªã¢ã©ã¤ãºã®å¦çããµã¼ãå´ã§è¡ãªã£ã¦ãã¾ãï¼
ãã¤ããªãããã³ã«ã«ããã½ã±ããéä¿¡ã¢ã¯ã»ã¹
- é常ã®Kyoto Tycoonã¢ã¯ã»ã¹ã®å ´å
Rubyã«ããããã°ã©ã ã¯ï¼ä¾ãã°æ¬¡ã®ããã«ãªãã§ãããï¼
require "socket" # setããããªã¥ã¼ãã¼ã¿ãä½ã b = []; (1..4000).each{|item| b << item; } start = Time.now(); (1..10000).each{|i| s = TCPSocket.open("192.168.1.100", 1978); # Set by Kyto Tycoon serializer out = [0xb4].pack("c*") + [0, "set".size(), 1].pack("N*") + "set"\ + ["key#{i}".size(), Marshal.dump(b).size()].pack("N*") + "key#{i}" + Marshal.dump(b);ã# Marshalã§é åãã·ãªã¢ã©ã¤ãºãã s.write(out); s.close(); # Get by Kyoto Tycoon deserializer out = [0xb4].pack("c*") + [0, "get".size(), 1].pack("N*") + "get"\ + ["key".size(), "key#{i}".size()].pack("N*") + "key" + "key#{i}"; s = TCPSocket.open("192.168.1.100", 1978); s.write(out); g = s.read(1); if g.getbyte(0) == 0xb4 then rnum = s.read(4).unpack('N*')[0]; ksize = s.read(4).unpack('N*')[0]; vsize = s.read(4).unpack('N*')[0]; key = s.read(ksize); value = Marshal.load(s.read(vsize)); # Marshalã§é åããã·ãªã¢ã©ã¤ãºãã s.close(); # ãã¾ã«ç®è¦ç¢ºèªãã if i % 1000 == 0 then puts "set value=#{b}" puts "get value=#{value}"; end end; } puts "time = #{Time.now()-start}";
- Stringã¯ã©ã¹ã§ã·ãªã¢ã©ã¤ãºï¼ï¼ï¼ãããã¼ã¸ã§ã³
require "socket" # setããããªã¥ã¼ãã¼ã¿ãä½ã b = []; (1..4000).each{|item| b << item; } start = Time.now(); (1..10000).each{|i| s = TCPSocket.open("192.168.1.100", 1978); # Rubyã®Stringã¯ã©ã¹ã¯ï¼Javaã®byteãCã®charã®ããã«ï¼ãã¤ããªãæ±ããã®ã§å ¨é¨Stringã«ãã¡ãã # Set by String out = [0xb4].pack("c*") + [0, "setraw".size(), 1].pack("N*") + "setraw"\ + ["key#{i}".size(), b.to_s.size()].pack("N*") + "key#{i}" + b.to_s s.write(out); s.close(); # Get by String out = [0xb4].pack("c*") + [0, "getraw".size(), 1].pack("N*") + "getraw"\ + ["key".size(), "key#{i}".size()].pack("N*") + "key" + "key#{i}"; s = TCPSocket.open("192.168.1.100", 1978); s.write(out); g = s.read(1); if g.getbyte(0) == 0xb4 then rnum = s.read(4).unpack('N*')[0]; ksize = s.read(4).unpack('N*')[0]; vsize = s.read(4).unpack('N*')[0]; key = s.read(ksize); value = s.read(vsize); s.close(); # ããã®ç®è¦ç¢ºèªã§ã¯set valueã¨get valueã¯åä¸ã«è¦ãããï¼get valueã¯æååãªã®ã§ï¼ # å®éã«é åã¨ãã¦ä½¿ãå ´åã¯ï¼value.splitãªã©ã§é åã«å¤æããå¿ è¦ããã if i % 1000 == 0 then puts "set value=#{b}" puts "get value=#{value}"; end end; } puts "time = #{Time.now()-start}";
- MessagePackãã¡ãããã¼ã¸ã§ã³
require "msgpack"; require "socket" # setããããªã¥ã¼ãã¼ã¿ãä½ã b = []; (1..4000).each{|item| b << item; } start = Time.now(); (1..10000).each{|i| s = TCPSocket.open("192.168.1.100", 1978); # Set by MessagePack serializer out = [0xb4].pack("c*") + [0, "setmp".size(), 1].pack("N*") + "setmp"\ + ["key#{i}".size(), MessagePack::pack(b).size()].pack("N*") + "key#{i}" + MessagePack::pack(b) s.write(out); s.close(); # Get by MessagePack deserializer out = [0xb4].pack("c*") + [0, "getmp".size(), 1].pack("N*") + "getmp"\ + ["key".size(), "key#{i}".size()].pack("N*") + "key" + "key#{i}"; s = TCPSocket.open("192.168.1.100", 1978); s.write(out); g = s.read(1); if g.getbyte(0) == 0xb4 then rnum = s.read(4).unpack('N*')[0]; ksize = s.read(4).unpack('N*')[0]; vsize = s.read(4).unpack('N*')[0]; key = s.read(ksize); value = MessagePack::unpack(s.read(vsize)); s.close(); # ãã¾ã«ç®è¦ç¢ºèªãã if i % 1000 == 0 then puts "set value=#{b}" puts "get value=#{value}"; end end; } puts "time = #{Time.now()-start}";
ãã³ããã¼ã¯çµæ
3ã¤ã¯æ¬å½ã«è¯ãä¼¼ã¦ãã¾ãï¼é常ãã¼ã¸ã§ã³ã¨MessagePackãã¼ã¸ã§ã³ã¯ï¼Rubyã®Marshalã使ããMessagePackã使ããã®éãã§ã·ãªã¢ã©ã¤ãº/ãã·ãªã¢ã©ã¤ãºãã¦getãããé åã¨ãã¦ããã«ä½¿ããå½¢ã§ããï¼Stringãã¼ã¸ã§ã³ã¨MessagePackãã¼ã¸ã§ã³ã¯ãµã¼ãå´ããã°ã©ã ãåä¸ã§ãï¼ã¾ãï¼Stringãã¼ã¸ã§ã³ã®ã¯ã©ã¤ã¢ã³ãããã°ã©ã ãKyoto Tycoonã·ãªã¢ã©ã¤ãºã®ãµã¼ãããã°ã©ã ã«çªã£è¾¼ãã§ããã®ã¾ã¾åãã¾ãï¼
ã«ãé¢ãããï¼æ§è½å·®ãåºã¾ãã®ã§ç´¹ä»ãã¾ãï¼
æ¯è¼ã¯ï¼ããªã¥ã¼ã®é·ããå¤åãããã¨ãã®ï¼å®è¡æéã¨Hash KVSãã¡ã¤ã«ã®ãµã¤ãºãè¦ããã¨ã«ãã¾ãï¼
ã¾ãã¯å®è¡æéã§ãï¼
MessagePackãã¼ã¸ã§ã³ããã³ããã§éãã§ãï¼ã¨ããããã«ã¯ï¼åå ã¯ãã¡ã¤ã«ãµã¤ãºã«ããã®ã§ãããã¨æã£ã¦è¦ã¦ã¿ã¾ãã¨ï¼ï¼ï¼
ãã¯ãMessagePackãã¼ã¸ã§ã³ã®ãã¡ã¤ã«ãµã¤ãºãå°ããã§ãï¼
å½ç¶ï¼æ¸¬å®ã«ã¯ãã¤ããªãããã³ã«ã®ã¹ã¯ãªããå®è¡ã使ãã¾ããï¼cURLãwgetã¯ä½¿ã£ã¦ãã¾ããï¼
æããï¼JavaãC++ã§ãã«ãã¹ã¬ããã¢ã¯ã»ã¹ãããã¨ï¼ãã£ã¨å·®ãåºããã¨ã§ãããï¼