我想達到接近C的速度來處理sqlite和正則表達式模式搜索。我意識到其他庫和FTS4將會是更快或者更好的解決方案,但這並不是我所要求的。可以使用Python的函數式編程來完全避免解釋器和方法的開銷?
我發現只要我不使用lambda或者定義的方法或python代碼,CPython公開的某些原語和C級函數可以直接作爲sqlite自定義函數注入,並且在運行時,即使除了返回一個常數之外沒有任何操作,也可以實現10倍的提升。但是,我還沒有準備好開始創建擴展,我試圖避免使用像Cython這樣的工具將C和python混合在一起。
我設計了以下測試代碼,揭示了這些性能差異,並利用第三方庫cytoolz(撰寫方法)提供的一些加速來實現某些功能樣式的邏輯,同時避免了lambda表達式。 (:Windows 8.1中的64位家用OS),省去打印報表:
import sqlite3
import operator
from cytoolz import functoolz
from functools import partial
from itertools import ifilter,chain
import datetime
from timeit import repeat
import re,os
from contextlib import closing
db_path='testdb.sqlite'
existed=os.path.exists(db_path)
re_pat=re.compile(r'l[0-3]+')
re_pat_match=re.compile(r'val[0-3]+')
with closing(sqlite3.connect(db_path)) as co, co as co:
if not existed:
print "creating test data"
co.execute('create table test_table (testval TEXT)')
co.executemany('insert into test_table values (?)',(('val%s'%v,) for v in xrange(100000)))
def count(after_from=''):
print co.execute('select count(*) from test_table %s'%(after_from,)).fetchone()[0]
def python_return_true(v):
return True
co.create_function('python_return_true',1,python_return_true)
co.create_function('python_lambda_true',1,lambda x: True)
co.create_function('custom_lower',1,operator.methodcaller('lower'))
co.create_function('custom_composed_match',1,functoolz.compose(partial(operator.is_not,None),re_pat_match.match))
data=[None,type('o',(),{"group":partial(operator.truth,0)})] # create a working list with a fallback object
co.create_function('custom_composed_search_text',1,functoolz.compose(
operator.methodcaller('group'), # call group() on the final element (read these comments in reverse!)
next, # convert back to single element. list will either be length 1 or 2
partial(ifilter,None), # filter out failed search (is there a way to emulate a conditional method call via some other method??)
partial(chain,data), # iterate list (will raise exception if it reaches result of setitem which is None, but it never will)
partial(data.__setitem__,0), # set search result to list
re_pat.search # first do the search
))
co.create_function('custom_composed_search_bool',1,functoolz.compose(partial(operator.is_not,None),re_pat.search))
_search=re_pat.search # prevent an extra lookup in lambda
co.create_function('python_lambda_search_bool',1,lambda _in:1 if _search(_in) else None)
co.create_function('custom_composed_subn_alternative',1,functoolz.compose(operator.itemgetter(1),partial(re_pat.subn,'',count=1)))
for to_call,what in (
(partial(count,after_from='where 1'),'pure select'),
(partial(count,after_from='where testval'),'select with simple compare'),
(partial(count,after_from='where python_return_true(testval)'),'select with python def func'),
(partial(count,after_from='where python_lambda_true(testval)'),'select with python lambda'),
(partial(count,after_from='where custom_lower(testval)'),'select with python lower'),
(partial(count,after_from='where custom_composed_match(testval)'),'select with python regex matches'),
(partial(count,after_from='where custom_composed_search_text(testval)'),'select with python regex search return text (chain)'),
(partial(count,after_from='where custom_composed_search_bool(testval)'),'select with python regex search bool (chain)'),
(partial(count,after_from='where python_lambda_search_bool(testval)'),'select with python regex search bool (lambda function)'),
(partial(count,after_from='where custom_composed_subn_alternative(testval)'),'select with python regex search (subn)'),
):
print '%s:%s'%(what,datetime.timedelta(0,min(repeat(to_call,number=1))))
與Python 2.7.8 32位輸出
pure select:0:00:00.003457
select with simple compare:0:00:00.010253
select with python def func:0:00:00.530252
select with python lambda:0:00:00.530153
select with python lower:0:00:00.051039
select with python regex matches:0:00:00.066959
select with python regex search return text (chain):0:00:00.134115
select with python regex search bool (chain):0:00:00.067687
select with python regex search bool (lambda function):0:00:00.576427
select with python regex search (subn):0:00:00.136042
我可能會去與的一些變化「選擇與python正則表達式搜索布爾(鏈)「上面。所以我的問題是2部分。
SQLITE3會如果create_function()調用創建返回任何東西,但原始的,它能夠理解的功能失效,所以MatchObject該搜索()返回需要轉換,因此鏈式「不爲空」方法。對於搜索文本返回功能,這會變成醜陋(不是非常簡單),就像您在源代碼中看到的一樣。有沒有一種比元素到迭代器轉換策略更容易的替代方法,當我試圖讓一個非python函數有選擇地顯示MatchObject的組時,只有當它在搜索正則表達式以便與sqlite3一起使用時返回?我一直在與Python的速度作鬥爭:是否使用數據庫函數而不是Python或函數,或者列表而不是字典或對象,浪費了將變量名稱複製到本地名稱空間的代碼行,使用生成器而不是其他方法調用或內聯循環和函數,而不是從Python可以提供的抽象中獲益。我還應該考慮哪些其他函數/庫,這些函數/庫會使我獲得巨大的效率回報(我至少說話10倍),同時仍然使用Python來搭建腳手架?我知道實際上會加速python代碼本身的程序(pypi,cython),但是它們似乎使用起來更危險,或者仍然因python的語言結構限制優化而受到影響,因爲它假設代碼總是被「解釋」?也許有幾種ctypes暴露的方法和策略可以在快速文本處理領域獲得回報?我知道這些圖書館主要關注科學,統計和數學加速,但我對這個領域並不特別感興趣。