這次發現了乙個golang的分詞庫gse,試試匯出為動態庫,用ffi載入。
由於之前對cgo不熟悉,以為go可以很方便的匯出到c,沒想到一開始就把我難倒。
panic: runtime error: cgo result has go pointer一開始直接在go裡返回了string,沒想到報錯了,原來go不允許匯出含有指標的資料結構
go type not supported in export: struct後來想,要不匯出string 的指標,但是如果只有指標位址,沒有長度,遍歷肯定會出錯,於是構造了乙個結構體,儲存指標位址和長度,沒想到還是不行。
這期間,由於工作忙(主要是懶),斷斷續續的看了一下cgo相關的內容,先跑通了c呼叫go,於是再試著用ffi,很快也跑通了。
在go裡,匯出乙個函式到c動態庫,其實非常簡單,需要在import c
包,並在匯出的函式加上export 函式名
,加上乙個空的main 函式即可,如:
package main
import (
"c")//必須和函式同名
//export plusone
func plusone(num int) int
func main()
編譯方法如下:
go build -buildmode=c-shared -o libdemo.so demo.go
就會自動生成 so 和 libdemo.h 標頭檔案,開啟libdemo.h,可以看到裡面是各種go 資料型別的定義,摘除部分如下:
typedef signed char goint8;
typedef unsigned char gouint8;
typedef short goint16;
typedef unsigned short gouint16;
typedef int goint32;
typedef unsigned int gouint32;
typedef long long goint64;
typedef unsigned long long gouint64;
可以看到還包含了乙個另外的標頭檔案#include
,可以使用gcc -e -p libdemo.h -o libdemo_unfold.h
展開stddef.h合併到乙個到頭檔案,然後複製我們需要的型別定義即可。
由於go的string,slice匯出後,都是乙個結構體,不是乙個簡單型別,這裡我們先看看string。
typedef struct gostring;
可以看到string有乙個char* 指標,和乙個表示長度的n,可以說明go的string是不帶'\n'的,和c的字串不同。然而一開始,我居然還特意加了'\n',然後給n也加1,結果發現不對,在go那邊加上輸出後,才發現出錯了。
對於這種結構體,用載入動態庫的ffi例項呼叫 new 方法。即$gostr = $ffi->new('gostring',0)
,注意new的第二個引數要傳0,表示這個物件php不用管理記憶體。在這個地方,我又掉坑里了。
然後要給p和n賦值,對於n,比較簡單,直接給字串長度,但是對於p,就比較麻煩。
翻看php文件,發現有個memcpy方法,於是試了一下,成功的實現了php和go之間傳string。
完整的**如下:
function makegostr(ffi $ffi, string $str): ffi\cdata
在上面的**裡,既有ffi的靜態方法,也有例項方法,它們之間的區別在於,靜態方法只有常用的資料型別,如果int,char;例項方法,才能呼叫載入的so裡面的型別。
下面我說一下三種呼叫思路,建議第一種,這裡就不貼**了,完整的**看github。
由於go不能返回slice string,那麼換個思路,把陣列拼接成字串,然後返回c.char。這種方式最簡單,而且在後面的跑分測試裡發現,也是最有效率的。
複雜的資料結構,可以序列化為string 然後返回c.char
既然不能返回,那麼我們修改傳入的引數是否可以呢。通過測試發現確實可行。
這就是一開始我的想法,這種方法有點麻煩,而且速度也不佔優。
先make lib
,生成go的動態庫,然後make php_test
和make go_test
檢視對比。
go的
testcut: goseg_test.go:18 cutchar 2000 次用時:41.511794msphp的testcut: goseg_test.go:26 cutpointer 2000 次用時:45.24684ms
testcut: goseg_test.go:34 cutslice 2000 次用時:42.537337ms
cutchar 2000 次用時:0.027052 s可以發現php居然比go的還快,比cjieba快了不知道多少倍,看來以後一些耗cpu的方式,可以用go來開發動態庫,給php用,比通過介面呼叫可以快很多。cutslice 2000 次用時:0.038451 s
cutpointer 2000 次用時:0.038257 s
假如go和php呼叫需要5ms,這樣2000次就是 10s了。可以發現ffi是介面呼叫的0.04/10 = 0.004,是幾百倍數量級的提公升。當然實際情況更複雜,但是效能提公升可是顯而易見的。
當然前提是選擇乙個效能高的ffi外部庫才行,如果比php還慢,那就不必了。
另外ffi可以預載入,鳥哥的部落格寫的很詳細了,大家可以去看看。
Go入門 方法呼叫
package main import container vector func main k2 vector.intvector k3 new vector.intvector k1.push 2 k2.push 3 k3.push 4 k1,k2,k3的型別分別是什麼?k1 的型別是vecto...
go 如何鏈式呼叫
go 的錯誤處理機制是返回錯誤,但是鏈式呼叫只能有乙個返回值,如果做到返回乙個值並且能夠處理錯誤資訊呢,可以用go 支援函式式程式設計實現。模擬htttp傳送請求的過程,逐步完成對request請求體的封裝然後呼叫傳送方法 1,定義了乙個request結構體模擬request請求 2,定義了乙個函式...
Go語言呼叫dll
user32 syscall.newlazydll imobiledevice.dll messageboxw user32.newproc idevice event subscribe messageboxw.call uintptr c.test uintptr s imobiledevice...