這篇文章主要講解了“什么是C語(yǔ)言動(dòng)態(tài)庫(kù)”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“什么是C語(yǔ)言動(dòng)態(tài)庫(kù)”吧!
10年積累的網(wǎng)站建設(shè)、成都網(wǎng)站設(shè)計(jì)經(jīng)驗(yàn),可以快速應(yīng)對(duì)客戶(hù)對(duì)網(wǎng)站的新想法和需求。提供各種問(wèn)題對(duì)應(yīng)的解決方案。讓選擇我們的客戶(hù)得到更好、更有力的網(wǎng)絡(luò)服務(wù)。我雖然不認(rèn)識(shí)你,你也不認(rèn)識(shí)我。但先網(wǎng)站設(shè)計(jì)后付款的網(wǎng)站建設(shè)流程,更有梨林免費(fèi)網(wǎng)站建設(shè)讓你可以放心的選擇與我們合作。
我們以這個(gè) repo 為例: https:// github.com/aklomp/base6 4 . 這是一個(gè) C 編寫(xiě)的 Base64 編碼/解碼庫(kù), 而且支持SIMD.
可以簡(jiǎn)單運(yùn)行下這個(gè)庫(kù)的 benchmark:
karminski@router02:/data/works/base64$ make clean && SSSE3_CFLAGS=-mssse3 AVX2_CFLAGS=-mavx2 make && make -C test ... Testing with buffer size 100 KB, fastest of 10 * 100 AVX2 encode 12718.47 MB/sec AVX2 decode 14542.81 MB/sec plain encode 3657.40 MB/sec plain decode 3433.23 MB/sec SSSE3 encode 7269.55 MB/sec SSSE3 decode 8173.10 MB/sec ...
我的 CPU 是 Intel(R) Xeon(R) CPU E3-1246 v3 @ 3.50GHz, 可以看到CPU如果支持 AVX2 的話(huà), 可以達(dá)到 12GB/s 以上, 這個(gè)性能非常強(qiáng)悍, 甚至連普通的SSD都跟不上了.
我們需要的第一步是把這個(gè) repo 編譯為動(dòng)態(tài)庫(kù). 但是這個(gè) repo 并沒(méi)有提供動(dòng)態(tài)庫(kù)的編譯選項(xiàng), 所以我們魔改下這個(gè)項(xiàng)目的 Makefile.
CFLAGS += -std=c99 -O3 -Wall -Wextra -pedantic # Set OBJCOPY if not defined by environment: OBJCOPY ?= objcopy OBJS = \ lib/arch/avx2/codec.o \ lib/arch/generic/codec.o \ lib/arch/neon32/codec.o \ lib/arch/neon64/codec.o \ lib/arch/ssse3/codec.o \ lib/arch/sse41/codec.o \ lib/arch/sse42/codec.o \ lib/arch/avx/codec.o \ lib/lib.o \ lib/codec_choose.o \ lib/tables/tables.o SOOBJS = \ lib/arch/avx2/codec.so \ lib/arch/generic/codec.so \ lib/arch/neon32/codec.so \ lib/arch/neon64/codec.so \ lib/arch/ssse3/codec.so \ lib/arch/sse41/codec.so \ lib/arch/sse42/codec.so \ lib/arch/avx/codec.so \ lib/lib.so \ lib/codec_choose.so \ lib/tables/tables.so HAVE_AVX2 = 0 HAVE_NEON32 = 0 HAVE_NEON64 = 0 HAVE_SSSE3 = 0 HAVE_SSE41 = 0 HAVE_SSE42 = 0 HAVE_AVX = 0 # The user should supply compiler flags for the codecs they want to build. # Check which codecs we're going to include: ifdef AVX2_CFLAGS HAVE_AVX2 = 1 endif ifdef NEON32_CFLAGS HAVE_NEON32 = 1 endif ifdef NEON64_CFLAGS HAVE_NEON64 = 1 endif ifdef SSSE3_CFLAGS HAVE_SSSE3 = 1 endif ifdef SSE41_CFLAGS HAVE_SSE41 = 1 endif ifdef SSE42_CFLAGS HAVE_SSE42 = 1 endif ifdef AVX_CFLAGS HAVE_AVX = 1 endif ifdef OPENMP CFLAGS += -fopenmp endif .PHONY: all analyze clean all: bin/base64 lib/libbase64.o lib/libbase64.so bin/base64: bin/base64.o lib/libbase64.o lib/libbase64.so $(CC) $(CFLAGS) -o $@ $^ lib/libbase64.o: $(OBJS) $(LD) -r -o $@ $^ $(OBJCOPY) --keep-global-symbols=lib/exports.txt $@ lib/libbase64.so: $(SOOBJS) $(LD) -shared -fPIC -o $@ $^ $(OBJCOPY) --keep-global-symbols=lib/exports.txt $@ lib/config.h: @echo "#define HAVE_AVX2 $(HAVE_AVX2)" > $@ @echo "#define HAVE_NEON32 $(HAVE_NEON32)" >> $@ @echo "#define HAVE_NEON64 $(HAVE_NEON64)" >> $@ @echo "#define HAVE_SSSE3 $(HAVE_SSSE3)" >> $@ @echo "#define HAVE_SSE41 $(HAVE_SSE41)" >> $@ @echo "#define HAVE_SSE42 $(HAVE_SSE42)" >> $@ @echo "#define HAVE_AVX $(HAVE_AVX)" >> $@ $(OBJS): lib/config.h $(SOOBJS): lib/config.h # o lib/arch/avx2/codec.o: CFLAGS += $(AVX2_CFLAGS) lib/arch/neon32/codec.o: CFLAGS += $(NEON32_CFLAGS) lib/arch/neon64/codec.o: CFLAGS += $(NEON64_CFLAGS) lib/arch/ssse3/codec.o: CFLAGS += $(SSSE3_CFLAGS) lib/arch/sse41/codec.o: CFLAGS += $(SSE41_CFLAGS) lib/arch/sse42/codec.o: CFLAGS += $(SSE42_CFLAGS) lib/arch/avx/codec.o: CFLAGS += $(AVX_CFLAGS) # so lib/arch/avx2/codec.so: CFLAGS += $(AVX2_CFLAGS) lib/arch/neon32/codec.so: CFLAGS += $(NEON32_CFLAGS) lib/arch/neon64/codec.so: CFLAGS += $(NEON64_CFLAGS) lib/arch/ssse3/codec.so: CFLAGS += $(SSSE3_CFLAGS) lib/arch/sse41/codec.so: CFLAGS += $(SSE41_CFLAGS) lib/arch/sse42/codec.so: CFLAGS += $(SSE42_CFLAGS) lib/arch/avx/codec.so: CFLAGS += $(AVX_CFLAGS) %.o: %.c $(CC) $(CFLAGS) -o $@ -c $< %.so: %.c $(CC) $(CFLAGS) -shared -fPIC -o $@ -c $< analyze: clean scan-build --use-analyzer=`which clang` --status-bugs make clean: rm -f bin/base64 bin/base64.o lib/libbase64.o lib/libbase64.so lib/config.h $(OBJS)
看不懂沒(méi)關(guān)系, Makefile 是如此的復(fù)雜, 我也看不懂, 僅僅是憑著感覺(jué)修改的, 然后他就恰好能運(yùn)行了... 注意 Makefile 的縮進(jìn)一定要用 "\t", 否則不符合語(yǔ)法會(huì)報(bào)錯(cuò).
然后我們進(jìn)行編譯:
AVX2_CFLAGS=-mavx2 SSSE3_CFLAGS=-mssse3 SSE41_CFLAGS=-msse4.1 SSE42_CFLAGS=-msse4.2 AVX_CFLAGS=-mavx make lib/libbase64.so
這樣我們就得到了libbase64.so 動(dòng)態(tài)庫(kù) (在 lib 里面). 這里還順便開(kāi)啟了各種 SIMD 選項(xiàng). 如果不需要的話(huà)可以關(guān)閉.
當(dāng)然這只是魔法, 不是煉金術(shù), 所以是需要付出努力的, 我們要手動(dòng)實(shí)現(xiàn)動(dòng)態(tài)庫(kù)的橋接, 首先我們需要查看我們要調(diào)用的函數(shù)需要什么參數(shù). 這兩個(gè)定義很簡(jiǎn)單, 我們需要傳入:
const char *src size_t srclen char *out size_t *outlen int flags
void base64_encode(const char *src, size_t srclen, char *out, size_t *outlen, int flags); int base64_decode(const char *src, size_t srclen, char *out, size_t *outlen, int flags);
然后我們就可以開(kāi)始編寫(xiě) ffi 橋接程序了. 首先把需要的庫(kù)全都包含進(jìn)來(lái), 注意, 多用 local 沒(méi)壞處, 使用 local 可以有效從局部查詢(xún), 避免低效的全局查詢(xún). 甚至其他包中的函數(shù)都可以 local 一下來(lái)提升性能.
動(dòng)態(tài)庫(kù)的話(huà)用專(zhuān)用的 ffi.load
來(lái)引用.
然后定義一個(gè) _M 用來(lái)包裹我們的庫(kù). 這里跟 JavaScript 很像, JavaScript 在瀏覽器里有 window, Lua 有 _G. 我們要盡可能避免封裝好的庫(kù)直接扔給全局, 因此封裝起來(lái)是個(gè)好辦法.
-- init local ffi = require "ffi" local floor = math.floor local ffi_new = ffi.new local ffi_str = ffi.string local ffi_typeof = ffi.typeof local C = ffi.C local libbase64 = ffi.load("./libbase64.so") -- change this path when needed. local _M = { _VERSION = '0.0.1' }
然后是用 ffi.cdef 聲明 ABI 接口, 這里更簡(jiǎn)單, 直接把源代碼的頭文件中的函數(shù)聲明拷過(guò)來(lái)就完事了:
-- cdef ffi.cdef[[ void base64_encode(const uint8_t *src, size_t srclen, uint8_t *out, size_t *outlen, size_t flags); int base64_decode(const uint8_t *src, size_t srclen, uint8_t *out, size_t *outlen, size_t flags); ]]
接下來(lái)是最重要的類(lèi)型轉(zhuǎn)換:
-- define types local uint8t = ffi_typeof("uint8_t[?]") -- uint8_t * local psizet = ffi_typeof("size_t[1]") -- size_t * -- package function function _M.base64_encode(src, flags) local dlen = floor((#src * 8 + 4) / 6) local out = ffi_new(uint8t, dlen) local outlen = ffi_new(psizet, 1) libbase64.base64_encode(src, #src, out, outlen, flags) return ffi_str(out, outlen[0]) end function _M.base64_decode(src, flags) local dlen = floor((#src + 1) * 6 / 8) local out = ffi_new(uint8t, dlen) local outlen = ffi_new(psizet, 1) libbase64.base64_decode(src, #src, out, outlen, flags) return ffi_str(out, outlen[0]) end
我們用 ffi_typeof 來(lái)定義需要映射的數(shù)據(jù)類(lèi)型, 然后用 ffi_new 來(lái)將其實(shí)例化, 分配內(nèi)存空間. 具體來(lái)講:
我們定義了2種數(shù)據(jù)類(lèi)型, 其中, local uint8t = ffi_typeof("uint8_t[?]")
類(lèi)型用來(lái)傳輸字符串, 后面的問(wèn)號(hào)是給 local out = ffi_new(uint8t, dlen)
中的 ffi_new
函數(shù)準(zhǔn)備的, 它的第二個(gè)參數(shù)可以指定實(shí)例化該數(shù)據(jù)類(lèi)型時(shí)的長(zhǎng)度. 這樣我們就得到了一個(gè)空的字符串?dāng)?shù)組, 用來(lái)裝 C 函數(shù)返回的結(jié)果. 這里的 dlen 計(jì)算出了源字符串 base64 encode 之后的長(zhǎng)度, 分配該長(zhǎng)度即可.
同樣, local psizet = ffi_typeof("size_t[1]")
指定了一個(gè) size_t *
類(lèi)型. C 語(yǔ)言里面數(shù)組就是指針, 即 size_t[0]
與 site_t*
是等價(jià)的. 因此我們分只有一個(gè)元素的 size_t
數(shù)組就得到了指向 size_t
類(lèi)型的指針. 然后在 local outlen = ffi_new(psizet, 1)
的時(shí)候后面的參數(shù)寫(xiě)的也是1, 不過(guò)這里寫(xiě)什么已經(jīng)無(wú)所謂了, 它只是不支持傳進(jìn)去空, 所以我們相當(dāng)于傳了個(gè) placeholder.
在使用這個(gè)值的時(shí)候, 我們也是按照數(shù)組的模式去使用的: return ffi_str(out, outlen[0])
.
需要注意的是, 一定要將 require "ffi"
以及 ffi.load
放在代碼最底層, 否則會(huì)出現(xiàn) table overflow
的情況.
最后, 這個(gè)文件是這樣子的:
--[[ ffi-base64.lua @version 20201228:1 @author karminski <code.karminski@outlook.com> ]]-- -- init local ffi = require "ffi" local floor = math.floor local ffi_new = ffi.new local ffi_str = ffi.string local ffi_typeof = ffi.typeof local C = ffi.C local libbase64 = ffi.load("./libbase64.so") -- change this path when needed. local _M = { _VERSION = '0.0.1' } -- cdef ffi.cdef[[ void base64_encode(const uint8_t *src, size_t srclen, uint8_t *out, size_t *outlen, size_t flags); int base64_decode(const uint8_t *src, size_t srclen, uint8_t *out, size_t *outlen, size_t flags); ]] -- define types local uint8t = ffi_typeof("uint8_t[?]") -- uint8_t * local psizet = ffi_typeof("size_t[1]") -- size_t * -- package function function _M.base64_encode(src, flags) local dlen = floor((#src * 8 + 4) / 6) local out = ffi_new(uint8t, dlen) local outlen = ffi_new(psizet, 1) libbase64.base64_encode(src, #src, out, outlen, flags) return ffi_str(out, outlen[0]) end function _M.base64_decode(src, flags) local dlen = floor((#src + 1) * 6 / 8) local out = ffi_new(uint8t, dlen) local outlen = ffi_new(psizet, 1) libbase64.base64_decode(src, #src, out, outlen, flags) return ffi_str(out, outlen[0]) end return _M
好了, 大功告成, 我們寫(xiě)個(gè) demo 調(diào)用一下試試:
-- main.lua local ffi_base64 = require "ffi-base64" local target = "https://example.com" local r = ffi_base64.base64_encode(target, 0) print("base64 encode result: \n"..r) local r = ffi_base64.base64_decode(r, 0) print("base64 decode result: \n"..r)
root@router02:/data/works/libbase64-ffi# luajit -v LuaJIT 2.1.0-beta3 -- Copyright (C) 2005-2020 Mike Pall. https://luajit.org/ root@router02:/data/works/libbase64-ffi# luajit ./main.lua base64 encode result: aHR0cHM6Ly9leGFtcGxlLmNvbQ== base64 decode result: https://example.com
感謝各位的閱讀,以上就是“什么是C語(yǔ)言動(dòng)態(tài)庫(kù)”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)什么是C語(yǔ)言動(dòng)態(tài)庫(kù)這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!
網(wǎng)站欄目:什么是C語(yǔ)言動(dòng)態(tài)庫(kù)
鏈接分享:http://www.rwnh.cn/article46/pcohhg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站設(shè)計(jì)、、手機(jī)網(wǎng)站建設(shè)、定制網(wǎng)站、移動(dòng)網(wǎng)站建設(shè)、微信小程序
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話(huà):028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)