使用Multi处理和ssdeep Python对类似文件进行分组时出现问题

时间:2012-05-18 15:18:17

标签: python multithreading multiprocessing hash similarity

我正在尝试使用deephashes处理超过1.3 mil的文件(SSDEEP)http://code.google.com/p/pyssdeep

它的作用是什么。它生成Hashes(在3-6分钟内生成1.3 mil),然后相互比较以获得相似性结果。比较非常快,但只运行单个进程不会使事情完成。所以我们放入Python Multiprocessing模块来完成任务。< / p>

结果是在30分钟内完成了1.3密耳的文本文件。使用18核(Quad Xeon处理器,总计24 CPUS)

以下是每个流程的工作原理:

  • 生成SSDEEP Sums。
  • 将这些总和列表拆分为5000组。
  • 在18个过程中比较每个块1对5000:每次迭代比较18个和。
  • 根据相似度得分(默认为75)
  • 对结果进行分组
  • 删除了已经检查过下一次迭代的文件。
  • 从下一个文件开始,即&lt;下一组75%得分
  • 重复,直到完成所有组。
  • 如果有未包含的文件(与任何文件不相似),则会将其添加到剩余列表中。

完成所有处理后,剩下的文件将被合并并递归地相互比较,直到没有结果为止。

问题是,当文件列表被分成较小的(5000)文件时。有些文件包含在前5000个块中但未包含在另一个组中,这使得组不完整。

如果我在没有分块的情况下运行,则完成循环需要很长时间。超过18小时,没有完成,。不知道有多久。

请建议我。

使用的模块:multiprocessing.Pool,ssdeep python

def ssdpComparer(lst, threshold):
    s = ssdeep()
    check_file = []
    result_data = []
    lst1 = lst
    set_lst = set(lst)

    print '>>>START'
    for tup1 in lst1:
        if tup1 in check_file:
            continue
        for tup2 in set_lst:
            score = s.compare(tup1[0], tup2[0])
            if score >= threshold:
                result_data.append((score, tup1[2], tup2[2])) #Score, GroupID, FileID
                check_file.append(tup2)
        set_lst = set_lst.difference(check_file)
    print """####### DONE #######"""
    remain_lst = set(lst).difference(check_file)

    return (result_data, remain_lst)



def parallelProcessing(tochunk_list, total_processes, threshold, source_path, mode, REMAINING_LEN = 0):
    result = []
    remainining = []
    pooled_lst = []
    pair = []
    chunks_toprocess = []

    print 'Total Files:', len(tochunk_list)

    if mode == MODE_INTENSIVE:
        chunks_toprocess = groupWithBlockID(tochunk_list) #blockID chunks
    elif mode == MODE_THOROUGH:
        chunks_toprocess = groupSafeLimit(tochunk_list, TOTAL_PROCESSES) #Chunks by processes
    elif mode == MODE_FAST:
        chunks_toprocess = groupSafeLimit(tochunk_list) #5000 chunks

    print 'No. of files group to process: %d' % (len(chunks_toprocess))
    pool_obj = Pool(processes = total_processes, initializer = poolInitializer, initargs = [None, threshold, source_path, mode])
    pooled_lst = pool_obj.map(matchingProcess, chunks_toprocess) #chunks_toprocess
    tmp_rs, tmp_rm = getResultAndRemainingLists(pooled_lst)
    result += tmp_rs
    remainining += tmp_rm

    print 'RESULT LEN: %s, REMAINING LEN: %s, P.R.L: %s' % (len(result), len(remainining), REMAINING_LEN)
    tmp_r_len = len(remainining)

    if tmp_r_len != REMAINING_LEN and len(result) > 0 :
        result += parallelProcessing(remainining, total_processes, threshold, source_path, mode, tmp_r_len)
    else:
        result += [('','', rf[2]) for rf in remainining]

    return result

def getResultAndRemainingLists(pooled_lst):
    g_result = []
    g_remaining = []

    for tup_result in pooled_lst:
        tmp_result, tmp_remaining = tup_result
        g_result += tmp_result
        if tmp_remaining:
            g_remaining += tmp_remaining

    return (g_result, g_remaining)

1 个答案:

答案 0 :(得分:0)

第一条建议:在你的情况下,不需要 check_file 作为list =&gt;将其更改为 set() - 然后应该更好(最后解释)。

如果你需要有块可能这样的程序就足够了:

def split_to_chunks(wholeFileList):
    s = ssdeep()
    calculated_chunks = []
    for someFileId in wholeFileList:
        for chunk in calculated_chunks:
            if s.compare(chunk[0], someFileId) > threshold:
                chunk.append(someFileId)
                break
        else: # important: this else is on 'for ' level
            # so if there was no 'break' so someFileId is a base for new chunk:
            calculated_chunks.append( [someFileId] )
    return calculated_chunks

之后你可以过滤结果: groups = filter(lambda x:len(x)&gt; 1,结果) remaining = filter(lambda x:len(x)== 1,result)

注意:此算法假设块的第一个元素是“基础”。结果的好处在很大程度上取决于ssdeep的行为(我可以想象一个奇怪的问题:ssdeep有多少传递?)如果这种相似性那么应该是...

最糟糕的情况是,如果任何一对s.compare(fileId1,fileId2)的得分不满足阈值条件(那么复杂度为n ^ 2,因此在您的情况下为1.3mln * 1.3mln)。

没有简单的方法来优化这种情况。让我们想象一下情况,其中s.compare(file1,file2)总是接近0然后(据我所知),即使你知道s.compare(A,B)非常低而且s.compare(B,C)非常那么你还是不能说s.compare(A,C)=&gt;所以你需要进行n * n次操作。

另一个注意:假设你使用了太多的结构和很多列表,例如:

set_lst = set_lst.difference(check_file)

这条指令创建了新的set(),并且set_lst和check_file中的所有元素都至少被触摸过一次,因为check_file是一个列表所以没有办法优化'差异'函数而且它有复杂性:len(check_file) * log(len(set_lst))

基本上:如果这些结构正在增长(几乎是130万),那么你的计算机需要执行更多的计算。如果你使用check_file = set()而不是[](list),那么它的复杂性应该是:len(set_lst)+ len(check_file)

同样检查元素是否在python的列表(数组)中:

if tup1 in check_file:

因为 check_file 是列表 - &gt;如果tup1不在列表中,你的cpu需要将tup1与所有元素进行比较,因此复杂度为len(check_file) 如果你将check_file更改为set,那么复杂性将在log2附近(len(check_file)) 让我们看起来更直观,假设len(* check_file *)= 1mln,你需要多少比较?

set:log2(1mln)= log2(1000000)~20

list:len(check_file)= 1mln