首页 > 解决方案 > 如何解决以元组为值的嵌套字典的性能问题?

问题描述

我编写了一个程序,它逐行读取 csv 文件并同时进行分组操作。这运行得相当好,直到数据集达到某个阈值,程序开始随时间线性增加。

用于处理这些文件的代码如下:

import time
data = {}
with open("./[path]/[to]/[file].csv") as f:
    start = time.time()
    i = 0
    f.readline() # Ignore header line
    for l in f:
        _, client, date, amount, io, ca, cat, mt = l.split(',')
        amount = float(amount)
        sumDay, sizeDay, meanDay = data.setdefault(client, {}).setdefault(date, (0, 0, 0))
        data[client][date] = (sumDay + amount, sizeDay + 1, (meanDay * sizeDay + amount) / (sizeDay + 1))
        i += 1
        if i % 500000 == 0:
            print(time.time() - start)
            start = time.time()

csv 文件结构(去掉了一些不必要的信息)如下:

,ClientId,Date,Amount,[...]
0,C5841516,2020-01-01 09:00:00,137.71,[...]
1,C3317977,2020-01-01 09:00:00,136.51,[...]
2,C5107526,2020-01-01 09:00:00,1060.94,[...]
3,C5087842,2020-01-01 09:00:00,130.89,[...]
4,C2938277,2020-01-01 09:00:00,793.64,[...]
5,C9799246,2020-01-01 09:00:00,734.12,[...]
6,C2035898,2020-01-01 09:00:00,107.03,[...]
7,C9310000,2020-01-01 09:00:00,89.17,[...]
8,C4054857,2020-01-01 09:00:00,123.3,[...]
9,C4067165,2020-01-01 09:00:00,846.7,[...]

我已经打印了从数据集中读取最后 500k 行所花费的时间。这些值与 csv 中的客户端计数和行数一起描述如下。

5k clients 3.8m lines | 10.5k clients 16m lines | 11k clients 16.7m lines
0.5572941303253174    | 0.5842881202697754      | 0.7967188358306885
0.5774965286254883    | 0.6746623516082764      | 1.1029884815216064
0.6105935573577881    | 0.6639199256896973      | 1.4002125263214111
0.7133028507232666    | 0.6712498664855957      | 1.883500337600708
0.7059860229492188    | 0.6454544067382812      | 2.0164811611175537
0.6402852535247803    | 0.6314959526062012      | 2.3338706493377686
0.6159951686859131    | 0.6306426525115967      | 2.6350154876708984
0.6128799915313721    | 0.6442699432373047      | 3.265746831893921
0.6154458522796631    | 0.6928198337554932      | 3.409559726715088
0.6257271766662598    | 0.6547060012817383      | 3.964728355407715
0.61846923828125      | 0.6538820266723633      | 4.057897329330444  
0.6247692108154297    | 0.6627013683319092      | 4.411986589431763
0.6428296566009521    | 0.655012845993042       | 4.914895296096802
0.6292152404785156    | 0.6570413112640381      | 6.285972833633423
0.6239409446716309    | 0.6641659736633301      | 5.72735857963562
...                   | ...                     | goes up to 20s
9.5s total            | 20s total               | 265s total

数据集大小增加 5%(10.5k 与 11k 客户端)使时间随时间线性增加,而对于较小的数据集,无论大小如何,时间都保持不变。

我发现用列表替换存储 sumDay、sizeDay 和 meanDay 的元组会使代码不会随着时间的推移而增加。但是对于较小的数据集,这表现得更差。(每 50 万行约 0.9 秒,而不是每 50 万行约 0.6 秒)

这里发生了什么?为什么存储在嵌套字典中的元组会使代码在达到某个键阈值后随时间增加?如果不使用列表而不是元组,如何避免这种情况,因为这会影响 50% 的性能?

标签: pythonpython-3.xdictionarytuples

解决方案


这是 GC https://bugs.python.org/issue2607#msg65341 非常古老但可悲的是真的


推荐阅读