並åå¦çæ§è½ãéè¦ãããã¼ã¿ãã¼ã¹ã©ã¤ãã©ãªTkrzwã ããã¤ãã«Goè¨èªããµãã¼ãããã並åå¦çãç°¡åã«æ¸ããGoè¨èªã¨ä¸¦åã«èªã¿æ¸ãã§ããDBMã®è¦ªåæ§ã¯é«ããããã§ã¯ãåè¨èªã®æ§è½æ¯è¼ãããä¸ã§ãGoã§ã®ç°¡åãªä½¿ãæ¹ã«ã¤ãã¦èª¬æããã
åè¨èªã§ã1000ä¸åã®ã¬ã³ã¼ãã®æ ¼ç´ï¼Setï¼ã¨æ¤ç´¢ï¼Getï¼ã®ã¹ã«ã¼ããããè¨æ¸¬ãã¦ã¿ããããã¼ã¨å¤ã¯ãããã8ãã¤ãã®ã¦ãã¼ã¯ãªæååã§ããããã¼ã¿æ§é ã«ã¯ããã·ã¥ãã¼ã¿ãã¼ã¹ãç¨ããããã·ã¥ãã±ããã®æ°ã¯ã¬ã³ã¼ãæ°ã®2åã¨ãããã©ã®è¨èªã§ãæ¨æºã©ã¤ãã©ãªã®ã¹ã¬ããæ©è½ã使ã£ã¦ãããGoã§ã¯å½ç¶ã´ã«ã¼ãã³ãèµ·åãã¦ããããã·ã³ã¯ç§ã®ãã¼ãPCï¼Core i7 8550U 1.8Ghzï¼ã§ãããåä½ã¯QPSï¼ã¯ã¨ãªæ¯ç§ï¼ã§ããã
1ã¹ã¬ãã Set | 1ã¹ã¬ãã Get | 4ã¹ã¬ãã Set | 4ã¹ã¬ãã Get | |
C++ | 1,328,341 | 1,615,792 | 2,243,716 | 3,678,283 |
C | 1,288,903 | 1,493,747 | 2,200,075 | 3,444,403 |
Go | 641,006 | 772,380 | 1,330,526 | 1,724,950 |
Java | 376,495 | 376,495 | 892,625 | 1,019,015 |
Python | 766,904 | 799,475 | 232,128 | 236,093 |
Ruby | 674,557 | 586,534 | 187,629 | 182,665 |
ã¾ããC++ã®çµæã«çç®ããããå¤ãã®ãã¼ãPCã§ã1ã¹ã¬ããã§100ä¸QPS以ä¸ã§èªã¿æ¸ãã§ãã並ååããã¨ãããªãã«æ§è½åä¸ãããã¨ããããã200ä¸QPSã®Setã300ä¸QPSã®Getãã§ããã°ãã»ã¨ãã©ã®ã¦ã¼ã¹ã±ã¼ã¹ã§ããã«ããã¯ã«ãªããã¨ã¯ãªãã ãããããã¦ããã®é常ã«èãã©ããã¼ã«éããªãCã¤ã³ã¿ã¼ãã§ã¤ã¹ã§ããã»ã¼åãæ§è½ãåºã¦ãããå°ãªãã¨ãæ¯åã®ã¯ã¨ãªã§mallocã1åå¤ãå¼ãã§ããã¯ããªã®ã§ãããå°ãé ããªãã¨æã£ã¦ããããå®éã«æ¸¬å®ããã¨ããã§ããªããããã
Goã®çµæãè¦ã¦ã¿ããããããã«CãC++ã«ã¯å£ããããã®ååãããã®ã¹ã«ã¼ããããåºãã¨ããã®ã¯é©ãã ãããã¦ã¹ã¬ããæ°ãä¸ããã¨ã¡ããã¨ã¹ã±ã¼ã«ãã¦ããããGoã¤ã³ã¿ã¼ãã§ã¤ã¹ã¯Cã¤ã³ã¿ã¼ãã§ã¤ã¹ã®èãã©ããã¼ãªã®ã ããGoã®æååãGoã®ãã¤ãã¹ã©ã¤ã¹ãä»ãã¦Cã®ãã¤ãé åã¨ç¸äºå¤æããã¨ããå®è£ ã«ãªã£ã¦ããããã®ãããã¯ããå°ãæé©åããä½å°ãããããã ããã¾ãããã¾ã§ã¡ããã¨ããæ§è½ãåºã¦ããã°å åãã¨ãæããã¹ã¬ããæ°ã«å¿ãã¦ã¹ã±ã¼ã«ããã£ã¦ã¨ãããéè¦ãªã®ã ã
JavaãGoã¨ä¼¼ããããªå¾åãªã®ã ããå ¨ä½çãªæ°å¤ãGoã®ååã«ãªã£ã¦ãããJavaã®ãã¤ãã£ãé¢æ°å¼ã³åºãï¼JNIï¼ã®ãªã¼ãããããGoã®ãã¤ãã£ãé¢æ°å¼ã³åºãï¼cgoï¼ã®ããããã大ããã¨ãããã¨ã ãããã¹ã¬ããæ°ã«å¿ãã¦ã¹ã±ã¼ã«ããã®ã¯è¯ãã¨ããã ã
Pythonã¨Rubyã¯ã°ãã¼ãã«ã¤ã³ã¿ã¼ããªã¿ããã¯ï¼GILï¼ã®ããå¦çç³»ãªã®ã§ãå¼ã³åºãå¦çãä¸å®ä»¥ä¸é«éãªå ´åã«ã¯ãã¹ã¬ããã使ã£ã¦ãã¹ã«ã¼ããããä¸ãããªãããããã¹ã¬ããåãæ¿ãã®ãªã¼ãã¼ãããã§é ããªããã¨ãå¤ããä»åã®ã±ã¼ã¹ãã¾ãã«ããã ãã·ã³ã°ã«ã¹ã¬ããã§ã¯Javaãåé§ãã¦Goã¨åçã®æ§è½ãåºã¦ãããããã«ãã¹ã¬ããã«ããã¨æ§è½ãã¬ã¿è½ã¡ãã¦ãã¾ããGetã¨Setã®æ§è½ãã»ã¨ãã©åããã¨ããããã¤ãã£ãå´ã§ã®å¦çã¯æ§è½ã«å½±é¿ããªãã¨å¤æã§ãããã¤ã¾ããPython/Rubyã®å¦çç³»å´ã®ãªã¼ãã¼ããããå¾éãã¦ãããã¨ã伺ãããTkrzwã§ã¯GILå¤ã§ãã¤ãã£ãé¢æ°ãå¼ã¶concurrentã¢ã¼ããè¨å®ã§ãããã®å®é¨çµæã¯concurrentã¢ã¼ãã®ãã®ã ããããããã¤ãã£ãé¢æ°ãååæ©ãå ´åãGILå¤ã§å®è¡ãGILå ã§å®è¡ãã¦ãå ¨ä½ã®ã¹ã«ã¼ãããã¯ãã¾ãå¤ãããªãã
è©å¤ã«éãããGoã¯å®è¡æ§è½ãè¯ã並åå¦çã«ãå¼·ãè¨èªã§ããã¨ãããã¨ã確ãããããããã¦ãGoã§Tkrzwã使ãã¨ã©ããªæãã«ãªãã®ãããµã³ãã«ã³ã¼ãã§è¦ã¦ããã ãããã
package main import ( "fmt" // ãããæ¸ãã ãã§åæã«Tkrzwã使ããããã«ãªã // ããã±ã¼ã¸ã¯ tkrzw ã¨ããååã§åãè¾¼ã¾ãã "github.com/estraier/tkrzw-go" ) func main() { // ãã¼ã¿ãã¼ã¹ãªãã¸ã§ã¯ããä½ã£ã¦ããã¼ã¿ãã¼ã¹ãã¡ã¤ã«ãéã dbm := tkrzw.NewDBM() dbm.Open("casket.tkh", true, "truncate=true,num_buckets=100") // ã¬ã³ã¼ããããã¤ãæ ¼ç´ãã // ãã¼ã¨å¤ã«ã¯ãã¤ãåã§ãæååã§ãæå®ã§ãã dbm.Set("first", "hop", true) dbm.Set("second", "step", true) dbm.Set("third", "jump", true) // å¤ãæ¤ç´¢ãã¦æååã¨ãã¦åå¾ãã fmt.Println(dbm.GetStrSimple("first", "*")) fmt.Println(dbm.GetStrSimple("second", "*")) fmt.Println(dbm.GetStrSimple("third", "*")) // ã¬ã³ã¼ããä¸ã¤ãã¤åãåºããã£ã³ãã«ã使ã£ã¦å ¨ã¬ã³ã¼ãã表示 for record := range dbm.EachStr() { fmt.Println(record.Key, record.Value) } // ãã¼ã¿ãã¼ã¹ãéãã dbm.Close() }
æè¿ã®Goå¦çç³»ã¯ãã¤ã³ãã¼ããã£ã¬ã¯ãã£ãã«GitHubã®ãªãã¸ããªãæ¸ãã ãã§åæã«go getãèµ°ã£ã¦ã¤ã³ã¹ãã¼ã«ãã¦ããã親åè¨è¨ã«ãªã£ã¦ããï¼Tkrzwæ¬ä½ãäºãã¤ã³ã¹ãã¼ã«ãã¦ããå¿ è¦ãããï¼ãã¤ã³ãã¼ããããããã¨ã¯tkrzw.NewDBMã§ãã¼ã¿ãã¼ã¹ãªãã¸ã§ã¯ããä½ã£ã¦ãOpenã¡ã½ããã§ãã¡ã¤ã«ã¨æ¥ç¶ããã°ãããããããããæååã®ãããã¨ã»ã¨ãã©åããããªä½¿ãæ¹ãã§ããã
Getã¨Setã¨Removeã ãã使ã£ã¦ããéãã¯åç´ãªAPIã¨ãè¨ããããTkrzwã®æ©è½ã¯ããªãè±å¯ã ãOpenã¡ã½ããã«ä¸ãããã©ã¡ã¼ã¿ãå¤ããã°ãããã·ã¥è¡¨ã®ãã¼ã¿ãã¼ã¹ã¨B+æ¨ã®ãã¼ã¿ãã¼ã¹ã¨ã¹ããããªã¹ãã®ãã¼ã¿ãã¼ã¹ãªã©ãåãæ¿ãããã¨ãã§ãããSearchã¡ã½ããã使ãã°ãç¯å²æ¤ç´¢ãæ£è¦è¡¨ç¾æ¤ç´¢ãé¡ä¼¼æ¤ç´¢ãªã©ãä¸æã§ã§ãããCompareExchangeã¡ã½ãããCompareExchangeMultiã¡ã½ããã使ãã°ãã¢ãããã¯ãªãã©ã³ã¶ã¯ã·ã§ã³ãå®ç¾ã§ããããã¤ã¬ã¯ãI/Oããµãã¼ããããã©ãããã¡ã¤ã«ã®APIãããã詳細ã«ã¤ãã¦ã¯ãã¡ãã®API文書ãã覧ããã ããããTkrzwèªä½ã«ã¤ãã¦ã¯ããはじめてのDBMãã¹ã©ã¤ããã覧ããã ãããã
ããå°ãçé¢ç®ãªä¾ã¨ãã¦ãCompareExchangeMultiã¡ã½ããã使ã£ããã³ã°ãã©ã³ã¶ã¯ã·ã§ã³ãè¡ãã³ã¼ããè¦ã¦ã¿ãããã¢ãªã¹ã¨ãããéè¡å£åº§ãæã£ã¦ããã¨ãã¦ãã¢ãªã¹ããããã«500åãééãããããã«ã¯ãã¢ãªã¹ã®ã¬ã³ã¼ãã®å¤ãã500ãå¼ããããã®ã¬ã³ã¼ãã®å¤ã«500ã足ãã¨ããæä½ãã¢ãããã¯ã«è¡ãå¿ è¦ããããã¢ããªã±ã¼ã·ã§ã³å´ã§ããã¯ã®ç®¡çãããã«ãããå®ç¾ãããã®ãCompareExchangeã¨ããææ³ã§ãããCAS = Compare-and-Swapã¨ãè¨ãã
package main import ( "fmt" "github.com/estraier/tkrzw-go" ) func main() { // ãã¼ã¿ãã¼ã¹ãéã // OrDieã¡ã½ããã¯ãã¹ãã¼ã¿ã¹ãæåã§ãªãå ´åã«ãããã¯ãèµ·ãã // å®éã®ã·ã¹ãã ã§ã¯é©å½ãªã¨ã©ã¼ä¼æ¬å¦çãæ¸ãã㨠dbm := tkrzw.NewDBM() dbm.Open("casket.tkt", true, "truncate=true,num_buckets=100").OrDie() // deferã使ã£ã¦ãã¼ã¿ãã¼ã¹ã確å®ã«éããã // ã¨ã©ã¼ãã§ãã¯ãããå ´åã«ã¯funcã§ã©ããããã¨ããã defer func() { dbm.Close().OrDie() }() // ããã¨ã¢ãªã¹ã®éè¡å£åº§ãä½ãã // ãã¼ãå¤ã«æ°å¤ãæå®ãã¦ãåæã«å¤æãã¦ãããã dbm.Set("Bob", 1000, false).OrDie() dbm.Set("Alice", 3000, false).OrDie() // ééãã©ã³ã¶ã¯ã·ã§ã³ãã¢ãããã¯ã«è¡ãé¢æ° transfer := func(src_key string, dest_key string, amount int64) *tkrzw.Status { // å¤ãã¬ã³ã¼ãã®å¤ã調ã¹ãã old_src_value := tkrzw.ToInt(dbm.GetStrSimple(src_key, "0")) old_dest_value := tkrzw.ToInt(dbm.GetStrSimple(dest_key, "0")) // ã¬ã³ã¼ãã®æ°ããå¤ãè¨ç®ããã new_src_value := old_src_value - amount new_dest_value := old_dest_value + amount if new_src_value < 0 { return tkrzw.NewStatus(tkrzw.StatusApplicationError, "insufficient value") } // äºåæ¡ä»¶ã¨äºå¾æ¡ä»¶ãè¨å®ãããããããã¯ãã¼ã¨å¤ã®ãã¢ã®ã¹ã©ã¤ã¹ã old_records := []tkrzw.KeyValueStrPair{ {src_key, tkrzw.ToString(old_src_value)}, {dest_key, tkrzw.ToString(old_dest_value)}, } new_records := []tkrzw.KeyValueStrPair{ {src_key, tkrzw.ToString(new_src_value)}, {dest_key, tkrzw.ToString(new_dest_value)}, } // äºåæ¡ä»¶ã«åãå ´åã«ãäºå¾æ¡ä»¶ã«ç§»è¡ããã // ä»ã®ã¹ã¬ããã«ããå¤æ´ã§äºåæ¡ä»¶ã«åããªããªã£ãå ´åãå®å ¨ã«å¤±æããã return dbm.CompareExchangeMultiStr(old_records, new_records) } // æåããã¾ã§ééãã©ã³ã¶ã¯ã·ã§ã³ã試ã¿ãã var status *tkrzw.Status for num_tries := 0; num_tries < 100; num_tries++ { status = transfer("Alice", "Bob", 500) // äºåæ¡ä»¶ã«åããªãã£ãå ´åã¯StatusInfeasibleErrorã«ãªãã // ããã§ãªãå ´åã¯æåãè´å½çã¨ã©ã¼ãªã®ã§ããããã«ããã«ã¼ãããæããã if !status.Equals(tkrzw.StatusInfeasibleError) { break } } status.OrDie() // åã ã®ã¬ã³ã¼ããåãåºãããã®ã¤ãã¬ã¼ã¿ãä½ãã // å ¨ã¦ã®ã¤ãã¬ã¼ã¿ã¯Destructã§ç ´æ£ãããã¨ã iter := dbm.MakeIterator() defer iter.Destruct() // æåã®ã¬ã³ã¼ãã«ã¤ãã¬ã¼ã¿ãé£ã°ãã iter.First() for { // ç¾å¨ã®ã¬ã³ã¼ããåå¾ãã¦è¡¨ç¤ºã key, value, status := iter.GetStr() if !status.IsOK() { break } fmt.Println(key, value) // ã¤ãã¬ã¼ã¿ã次ã®ã¬ã³ã¼ãã«é²ããã iter.Next() } }
ã¤ãã¬ã¼ã¿ã使ãå ´åã使ãçµãã£ãã¤ãã¬ã¼ã¿ãDestructã§ç ´æ£ããã®ãå¿ããªãã§æ¬²ãããã¾ããéãããã¼ã¿ãã¼ã¹ã¯Closeã§éããã®ãå¿ããªãã§æ¬²ãããdeferãæ´»ç¨ããã¨ããã
CompareExchangeMultiã¯ãã¤ãåã¨ãã¦ã¬ã³ã¼ãã®ãã¼ã¨å¤ããæ±ããCompareExchangeMultiStrã¯ãã®ã©ããã¼ã§ãæååã§ã¬ã³ã¼ãã®ãã¼ã¨å¤ãæ±ããCompareExchangeMultiã¯ããã®ã¬ã³ã¼ããåå¨ããªããã¨ããæ¡ä»¶ãå¤ãnilã®ã¬ã³ã¼ãã¨ãã¦è¡¨ç¾ãããCompareExchangeMultiStrã§ã¯ãstringã¯nilã«ã§ããªãã®ã§ã空æååã®å¤ãéåå¨ãæå³ãããéåå¨ãæ±ããããè¤æ°ã¬ã³ã¼ãã®ã¢ãããã¯å¦çãã§ããã®ã§ãCompareExchangeMultiã¯ä¸è½ã®æ´æ°æä½ã ã¨è¨ãããçè«çã«ã¯ãGetã¨CompareExchangeMultiã ããããã°ãä»ã®ã¡ã½ããã¯ä¸åãªãã¦ããã©ããªã«è¤éãªãã©ã³ã¶ã¯ã·ã§ã³ãå®ç¾ã§ãããäºåæ¡ä»¶ã空ãªã¹ãã«ããã°SetMultiãRemoveMultiã®ä»£ç¨ã¨ãã¦ã使ããã
ç¹æ®ãªã¦ã¼ã¹ã±ã¼ã¹ã ã¨ã¯æãããSetAndGetã¨ãRemoveAndGetã¨ãããã¡ã½ãããå½¹ã«ç«ã¤å ´åãããã ãããã¬ã³ã¼ãã«æ°ããå¤ãè¨å®ãããåé¤ããããã¤ã¤ããã®æ´æ°æä½ã®åã®ã¬ã³ã¼ãã®å¤ãåãåºããã®ã ãæ¬æ¥2åã«åãã¦ãããã°ãªããªããããªæä½ã1åã§ã§ããã¨ããæ§è½ä¸ã®å©ç¹ãããã¨ã¨ãã«ãã¢ãããã¯ã«ãããè¡ãããã¨ããã®ãéè¦ã ã並åå¦çï¼ã¨ããã並è¡å¦çï¼ã®æèã§ã¯ãååæ§ï¼atomicityï¼ã¯æéè¦æ¦å¿µã§ããã¨è¨ã£ã¦ãéè¨ã§ã¯ãªããGoè¨èªã«ãã£ã¦ä¸¦åå¦çã身è¿ã«ãªã£ã¦ããããããã§ãTkrzwã®è¨è¨ã®åºæ¬ç念ã§ãã並åå¦çã¸ã®æé©åããã£ã¨æ¥ã®ç®ãè¦ããããããªãã¨æå¾ ãã¦ããã
ã¾ã¨ãããã¼ã¿ãã¼ã¹ã©ã¤ãã©ãªTkrzwãGoãã使ããããã«ãªã£ãã並åå¦çã«å¼·ããã®äºã¤ã®çµã¿åããã¯ãªããªãé¢ç½ãããããªããã¨æã£ã¦ãããæ£ç´ãªãã¨ãè¨ãã¨ãGoã¯ãã£ã¨é ãã¨æã£ã¦ããã®ã§ãä»ã¾ã§æãä»ãã¦ããªãã£ãã並åå®è¡ã§ããã¾ã§ãã£ããã¨ããæ§è½ãåºããªããC/C++ãããªãã¨é§ç®ã ã¨æã£ã¦ããæ§ã ãªä»äºãGoã§åºæ¥ããã ããããªä¸ã§é«éãªãã¼ã¿ã¹ãã¬ã¼ã¸ã欲ãããªã£ãæã«ã¯Tkrzwã®ãã¨ãæãåºãã¦ãããã¨å¹¸ãã ã