加速Python中字符串搜索的策略

时间:2017-04-10 19:24:20

标签: python string search optimization key-value-store

我需要一些帮助。我在学习Python的过程中一直在研究文件搜索应用程序,到目前为止,这是一次非常有趣的体验,学到了很多东西并且意识到实际上有多少。

所以,它是我的第一个应用程序,它需要快速!我不满意(除其他事项外)为稀疏搜索找到匹配的速度。

应用程序将文件和文件夹名称缓存为dbm键,搜索基本上是通过这些键运行搜索词。

GUI在Tkinter中,为了避免卡住,我将搜索循环放在一个帖子中。线程通过队列从GUI接收查询,然后通过另一个队列传回结果。

代码的外观如下:

def TMakeSearch(fdict, squeue=None, rqueue=None):
    '''Circumventing StopIteration(), did not see speed advantage'''

    RESULTS_PER_BATCH=50

    if whichdb(DB)=='dbhash' or 'dumb' in whichdb(DB): 
        '''iteration is  not implemented for gdbm and (n)dbm, forced to
        pop the keys out in advance for "for key in fdict:" ''' 

        fdict=fdict 
    else:
        # 'dbm.gnu', 'gdbm', 'dbm.ndbm', 'dbm'
        fdict=fdict.keys()

    search_list=None
    while True:  
        query=None
        while not squeue.empty():        
            #more items may get in (or not?) while condition is checked
            query=squeue.get()
        try:    
            search_list=query.lower().encode(ENCODING).split()
            if Tests.is_query_passed:
                print (search_list)
        except:
            #No new query, or a new database has been created and needs to be synced
                sleep(0.1)
                continue
        else:
            is_new_query=True

        result_batch=[] 
        for key in fdict: 
            separator='*'.encode(ENCODING)   #Python 3, yaaay
            filename=key.split(separator)[0].lower()

            #Add key if matching    
            for token in search_list:
                if not token in filename:
                    break              
            else: 
                #Loop hasn't ended abruptly    
                result_batch.append(key)

            if len(result_batch)>=RESULTS_PER_BATCH: 
                #Time to send off a batch
                rqueue.put((result_batch, is_new_query))
                if Tests.is_result_batch: 
                    print(result_batch, len(result_batch))
                    print('is_result_batch: results on queue')

                result_batch=[]
                is_new_query=False
                sleep(0.1)

                if not squeue.empty(): 
                    break

        #Loop ended naturally, with some batch<50        
        rqueue.put((result_batch, is_new_query))    

一旦结果很少,结果就不再是实时的,而是需要几秒钟,这就是我的120GB硬盘。

我相信它可以更快,并希望实时搜索。

有哪些方法可以加快搜索速度?

我当前的标记都涉及增加我使用的功能 - 以某种方式使用多处理,使用cython,或许以某种方式使用ctypes使搜索绕过Python运行时。

然而,我怀疑有一些更简单的事情可以让它工作,因为我不熟悉Python和优化。

请帮助!

  • 我希望尽可能保留在标准库中,作为概念证明和可移植性(目前我只将scandir作为Python上的外部库&lt; 3.5),所以例如ctypes更适合cython

  • 如果相关/有帮助,其余代码就在这里 - https://github.com/h5rdly/Jiffy

修改

这是该功能的核心,需要做一些预先安排:

for key in fdict: 
    for token in search_list:
        if not token in key:
            break
    else:     
        result_batch.append(key)

其中search_list是一个字符串列表,而fdict是一个字典或一个dbm(没有看到两者的速度差异)。

这是我希望更快的结果,以便结果实时到达,即使只有很少的键包含我的搜索词。

编辑2: 在@hpaulj的建议中,我将dbm密钥放在一个(冻结的)集合中,以便在Windows / Python27(dbhash)上获得明显的改进: enter image description here

我有一些警告 -

  • 对于我使用的~50Gb,冻结集需要28Mb,如pympler.asizeof。所以对于完整的1Tb,我怀疑它会占用很多RAM。

  • 在linux上,出于某种原因,转换不仅没有帮助,而且查询本身也因为搜索持续时间的某些奇怪原因而实时停止实时更新,使GUI看起来不够密集

  • 在Windows上,这几乎和我想要的一样快,但仍然不能立即变形。

所以这就是这个补充:

if 'win' in sys.platform:
    try:
        fdict=frozenset(fdict)
    except:
        fdict=frozenset(fdict.keys())

由于大型磁盘需要大量的RAM,我想我现在将其添加为可选的更快搜索,&#34; Scorch Mode&#34;。

我想知道接下来要做什么。我想也许,如果我能以某种方式将键/文件名导出到ctypes可以传递的数据类型,我可以弹出相关的C函数来进行搜索。 此外,也许可以学习Python字节码并进行一些低级优化。

我希望这个速度和Python一样快,请指教。

0 个答案:

没有答案