python - 使用 eval 优化 Python 读取大文件

标签 python file optimization eval

我正在编写一个 Python 脚本(使用 Python 3.2),在某些时候需要为字典中的每个键遍历一个大约 800.000 行的文件。 key 数量约为 150,000 个。文件中的行是以下格式的字典:

{'url': 'http://address.com/document/42/1998', 'referrer': 'http://address.com/search?&q=query1', 'session': '1', 'rank': 2, 'time': 1338447254}
{'url': 'http://address.com/document/55/17', 'referrer': 'http://address.com/search&q=query2', 'session': '1', 'rank': 2, 'time': 13384462462}

对于该文件中的每一行,我需要进行一些计算并存储结果。为了能够阅读字典并对其进行处理,我使用 eval。这将导致 120.000.000.000 次调用 eval,这需要很长时间。因此,我正在寻找一种优化方法。

欢迎您提出所有可能的优化建议。每一点都可能产生影响,但我主要对 eval 和读取文件的方式感兴趣。自动取款机。我认为 eval 的其他一些方法可能执行得更快,但我无法使 JSON 读取格式,并且使用 split 的效果还不是很好。 另外,我从文件中读取的方式可能会得到优化。我已经尝试了以下代码中的方法以及“with”(速度慢得多,但消耗的内存更少)。我还尝试使用 map 读取内存中的文件:

f_chunk = map(eval, codecs.open(chunk_file, "r", encoding="utf-8").readlines())

但这也不起作用。

无论如何,脚本的以下部分是重点部分。它在多个进程中运行:

def mine(id, tmp_sessions, chunk_file, work_q, result_q, init_qsize):
    #f_chunk = map(eval, codecs.open(chunk_file, "r", encoding="utf-8").readlines())
    f_chunk = codecs.open(chunk_file, "r", encoding="utf-8").readlines()

    while True:
        try:

            k = work_q.get()
            if k == 'STOP':

                work_q.task_done()
                break # reached end of queue

        except Queue.Empty:
            break

        #with codecs.open(chunk_file, "r", encoding="utf-8") as f_chunk:
        for line in f_chunk:
            #try:
            jlog_nest = dict()
            jlog_nest = eval(line)

            #jlog_nest = json.loads(line)
            #jlog_nest = line
            #jlog_nest = defaultdict(line)


            if jlog_nest["session"] == k: # If session is the same
                query_nest = prepare_test_cases_lib.extract_query(jlog_nest["referrer"])

                for q in tmp_sessions[k]:

                    if q[0] == query_nest:
                        url = jlog_nest["url"]
                        rank = jlog_nest["rank"]
                        doc_id = prepare_test_cases_lib.extract_document_id(url)

                        # Increase number of hits on that document, and save its rank
                        if doc_id in q[1]:
                            q[1][doc_id][0] += 1
                            q[1][doc_id][1].append(rank)
                        else:
                            q[1][doc_id] = [1, [rank]]
            #except:
            #    print ("error",k)

        result_q.put((k, tmp_sessions[k]))
        work_q.task_done()

如果有助于理解在运行上述代码之前发生的情况,tmp_session 可能看起来像这样:

tmp_sessions: {'39': [['q7', {}], ['q2', {}]], '40': [['q2', {}]]}

之后:

tmp_sessions: {'39': [['q7', {}], ['q2', {'133378': [1, [2]]}]], '40': [['q2', {'133378': [1, [2]]}]]}

在真实数据的子集上,我运行了 pstats 文件中的 562 个键和 2232 行,按时间降序排序(这只是顶部):

1284892 function calls in 76.810 seconds

Ordered by: cumulative time

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     8    0.000    0.000   77.985    9.748 {built-in method exec}
     8    1.607    0.201   77.978    9.747 prepare_hard_test_cases.py:29(mine)
1254384   75.051    0.000   76.220    0.000 {built-in method eval}
   562    0.008    0.000    0.050    0.000 queues.py:99(put)
     8    0.000    0.000    0.029    0.004 codecs.py:685(readlines)

由此看来,确实是eval占用了时间。

编辑:按照建议,我尝试使用literal_eval。我实际上发现这个试图找到解决方案,但认为它与 eval 相同。我刚刚运行了它。它确实产生了相同的结果,但运行时间非常糟糕:

 50205868 function calls (37662028 primitive calls) in 121.494 seconds

 Ordered by: cumulative time

 ncalls  tottime  percall  cumtime  percall filename:lineno(function)
      8    0.001    0.000  121.494   15.187 {built-in method exec}
      8    0.008    0.001  121.493   15.187 <string>:1(<module>)
      8    4.935    0.617  121.485   15.186 prepare_hard_test_cases.py:29(mine)
1254384    5.088    0.000  116.425    0.000 ast.py:39(literal_eval)
1254384    1.098    0.000   71.432    0.000 ast.py:31(parse)
1254384   70.333    0.000   70.333    0.000 {built-in method compile}
13798224/1254384   22.996    0.000   39.336    0.000 ast.py:51(_convert)
7526304    8.539    0.000   23.042    0.000 ast.py:63(<genexpr>)
25087680    8.371    0.000    8.371    0.000 {built-in method isinstance}
    8    0.001    0.000    0.047    0.006 codecs.py:685(readlines)

编辑 2:我现在尝试了两种新方法。第一个是从每一行手动提取键和值,构建一个要处理的字典。这在我的测试集上运行得更快一些:

51460252 function calls in 45.207 seconds

 Ordered by: cumulative time

 ncalls  tottime  percall  cumtime  percall filename:lineno(function)
      8    0.001    0.000   45.207    5.651 {built-in method exec}
      8    0.003    0.000   45.207    5.651 <string>:1(<module>)
      8    1.701    0.213   45.203    5.650 prepare_hard_test_cases.py:68(mine)
1254384    5.725    0.000   43.391    0.000 prepare_hard_test_cases.py:36(extractDict)
6271920   23.433    0.000   37.665    0.000 prepare_hard_test_cases.py:20(extractKeyValue)
18819074   11.308    0.000   11.308    0.000 {method 'find' of 'str' objects}
25092651    2.927    0.000    2.927    0.000 {built-in method len}

这是个好消息,但更好的是我使用 pickle 的第二种方法。现在我得到:

30091 function calls in 5.285 seconds

Ordered by: cumulative time

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     8    0.000    0.000    5.285    0.661 {built-in method exec}
     8    0.003    0.000    5.285    0.661 <string>:1(<module>)
     8    0.173    0.022    5.281    0.660 prepare_hard_test_cases.py:68(mine)
   570    0.001    0.000    5.057    0.009 queues.py:113(get)
  2281    3.925    0.002    3.925    0.002 {method 'acquire' of '_multiprocessing.SemLock' objects}
   570    1.133    0.002    1.133    0.002 {method 'recv' of '_multiprocessing.PipeConnection' objects}
     8    0.029    0.004    0.029    0.004 {built-in method load}

当我有时间时,我会尝试将这种方法应用于整个集合。

有什么建议吗?

最诚挚的问候, 卡斯帕

最佳答案

你应该给ast.literal_eval()尝试一下,它是专为这项工作而设计的,并且可能会更快。

eval() 速度慢、不安全,而且通常是个坏主意。如果您认为您需要它,请四处看看,我向您保证 99.99% 的情况下您都不需要。

另一个说明:

f_chunk = codecs.open(chunk_file, "r", encoding="utf-8").readlines()
...

实际上应该是:

with open(chunk_file, "r", encoding="utf-8") as f_chunk:
    ...

文件是迭代器,因此使用readlines()只会降低程序的内存效率。使用 with 确保您的文件在完成后正确关闭(就像在 3.x 中一样,您可以只使用 open() 而不是 codecs.open( ),因为它已更新以支持后者的额外功能)。

除此之外,据我所知,每一行数据都应该是有效的 JSON,因此 json 模块也应该可以工作。

关于python - 使用 eval 优化 Python 读取大文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15410025/

相关文章:

python - FFmpeg 最小化内存使用或向 youtube-dl 添加超时

python - PyBluez 未检测到内置蓝牙适配器

MATLAB 我想最小化差异拟合

ios - 从 Swift IOS 套接字获取 InputStream 中的文件

c - 奇怪的 append 行为

postgresql - PostgreSql 中的临时文件

php - 检查数据库中是否存在数据

Python Pandas 通过将值连接到列名来组合 2 个数据框

python - 无法运行旁遮普语

python - 格式化要写入的文件