如何让我的代码更高效?

时间:2012-12-21 00:53:54

标签: python

我有一个包含tool_id,时间和消息的元组列表。我想从这个列表中选择消息匹配某些字符串的所有元素,以及所有其他元素,其中时间在该工具的任何匹配消息的某些差异内。

以下是我目前的做法:

# record time for each message matching the specified message for each tool 
messageTimes = {} 
for row in cdata:   # tool, time, message 
    if self.message in row[2]: 
        messageTimes[row[0], row[1]] = 1 

# now pull out each message that is within the time diff for each matched message 
# as well as the matched messages themselves 

def determine(tup): 
    if self.message in tup[2]: return True      # matched message 

    for (tool, date_time) in messageTimes: 
        if tool == tup[0]: 
            if abs(date_time-tup[1]) <= tdiff: 
               return True 

    return False 


cdata[:] = [tup for tup in cdata if determine(tup)] 

此代码有效,但运行时间太长 - 例如当cdata有600,000个元素(这是我的应用程序的典型元素)时,运行需要2个小时。

此数据来自数据库。最初我只使用SQL获取了我想要的数据,但这也花了太长时间。我只选择了我想要的消息,然后为每个进行另一个查询的消息选择每个消息的时间差异。这导致了成千上万的查询。所以我改变它以立即拉出所有潜在的匹配,然后在python中处理它,认为这会更快。也许我错了。

任何人都可以就加快这个问题给我一些建议吗?

更新我的帖子以显示我在SQL中所做的事情。

我在SQL中所做的非常简单。第一个查询类似于:

SELECT tool, date_time, message 
FROM event_log
WHERE message LIKE '%foo%'
AND other selection criteria

这足够快,但可能会返回20或3万行。然后我循环遍历结果集,并为每一行运行这样的查询(其中dt和t是来自上面选择的行中的date_time和工具):

SELECT date_time, message
FROM event_log
WHERE tool = t 
AND ABS(TIMESTAMPDIFF(SECOND, date_time, dt)) <= timediff

这花了大约一个小时。

我还尝试在一个嵌套查询中进行操作,其中内部查询从第一个查询中选择行,外部查询选择时间差异行。这花了更长时间。

所以现在我选择没有消息LIKE'%foo%'子句,我回到了600,000行并尝试从python中提取我想要的行。

4 个答案:

答案 0 :(得分:6)

优化SQL的方法是在一个查询中完成所有操作,而不是迭代超过20K行并为每个查询执行另一个查询。

通常这意味着您需要添加JOIN,或者偶尔添加子查询。是的,只要重命名一个或两个副本,就可以将表连接到自身。所以,像这样:

SELECT el2.date_time, el2.message 
FROM event_log as el1 JOIN event_log as el2
WHERE el1.message LIKE '%foo%'
AND other selection criteria
AND el2.tool = el1.tool
AND ABS(TIMESTAMPDIFF(SECOND, el2.datetime, el1.datetime)) <= el1.timediff

现在,这可能不会快速开箱即用,因此有两个步骤可以改进它。

首先,查找显然需要编制索引的列。显然tooldatetime需要简单的索引。 message可以从一个简单的索引中受益,或者,如果你的数据库有更好的东西,可能更有趣,但鉴于初始查询足够快,你可能不需要担心它。

偶尔,这就足够了。但通常情况下,你无法正确猜测一切。并且可能还需要重新排列查询的顺序等等。因此,您将需要EXPLAIN查询,并查看数据库引擎正在执行的步骤,并查看它在执行的操作慢速迭代查找,它可以进行快速索引查找,或者在小集合之前迭代大型集合。

答案 1 :(得分:2)

对于表格数据,您无法浏览Python pandas库,其中包含针对此类查询的高度优化代码。

答案 2 :(得分:0)

我通过更改我的代码修复了这个问题:

- 首先我将messageTimes作为工具键入的列表的字典:

messageTimes = defaultdict(list)    # a dict with sorted lists

for row in cdata:   # tool, time, module, message
    if self.message in row[3]:
        messageTimes[row[0]].append(row[1])

- 然后在确定函数中我使用了bisect:

 def determine(tup):
    if self.message in tup[3]: return True      # matched message

    times = messageTimes[tup[0]]
    le = bisect.bisect_right(times, tup[1])
    ge = bisect.bisect_left(times, tup[1])
    return (le and tup[1]-times[le-1] <= tdiff) or (ge != len(times) and times[ge]-tup[1] <= tdiff)

通过这些更改,花费超过2小时的代码花了不到20分钟,甚至更好,一个耗时40分钟的查询耗时8秒!

答案 3 :(得分:0)

我做了2个更改,现在20分钟查询需要3分钟:

found = defaultdict(int)
def determine(tup):
    if self.message in tup[3]: return True      # matched message

    times = messageTimes[tup[0]]
    idx = found[tup[0]]
    le = bisect.bisect_right(times, tup[1], idx)
    idx = le
    return (le and tup[1]-times[le-1] <= tdiff) or (le != len(times) and times[le]-tup[1] <= tdiff)