【问题标题】:Optimizing parsing of massive python dictionary, multi-threading优化海量python字典解析,多线程
【发布时间】:2020-03-09 00:46:17
【问题描述】:

让我们以一个小的 Python 字典为例,其中的值是整数列表。

example_dict1 = {'key1':[367, 30, 847, 482, 887, 654, 347, 504, 413, 821],
    'key2':[754, 915, 622, 149, 279, 192, 312, 203, 742, 846], 
    'key3':[586, 521, 470, 476, 693, 426, 746, 733, 528, 565]}

假设我需要解析列表的值,我已将其实现到以下函数中:

def manipulate_values(input_list):
    return_values = []
    for i in input_list:
        new_value = i ** 2 - 13
        return_values.append(new_value)
    return return_values

现在,我可以轻松地解析该字典的值,如下所示:

for key, value in example_dict1.items():
    example_dict1[key] = manipulate_values(value)

结果如下:

example_dict1 = {'key1': [134676, 887, 717396, 232311, 786756, 427703, 120396, 254003, 170556, 674028], 
     'key2': [568503, 837212, 386871, 22188, 77828, 36851, 97331, 41196, 550551, 715703], 
     'key3': [343383, 271428, 220887, 226563, 480236, 181463, 556503, 537276, 278771, 319212]}

这对于小型词典非常有效。

我的问题是,我有一个包含数百万键和长列表的庞大字典。如果我应用上述方法,算法将会非常缓慢。

如何优化上述内容?

(1) 多线程---除了传统的threading 模块之外,字典中是否有更有效的选项可用于多线程这个for语句?

(2) 更好的数据结构是否合适?

我问这个问题是因为,在这种情况下,我很困惑如何最好地进行。我没有看到比字典更好的数据结构,但是跨字典(然后跨值列表)的 for 循环非常慢。这里可能有一些设计得更快的东西。

编辑:正如您可以想象的那样,这有点像一个玩具示例 --- 所讨论的函数比 x**2-13 稍微复杂一些。

我更感兴趣的是如何使用具有数百万键的字典以及长长的值列表来实现价值。

【问题讨论】:

  • 你有足够的内存将所有内容存储在一个 numpy 数组中吗?
  • 线程对您没有帮助,因为 python 全局解释器锁 (GIL) 在 python 级别强制执行协作多线程 - 一次只能运行一个线程,因此没有并行性
  • 列表的大小都一样吗?
  • 人们提到转换为 numpy。如果这些列表首先在 numpy 中创建,效率会更高。
  • 如果你使用 numpy,它会释放 GIL,你可以潜在地将处理提供给线程池。它通常不值得池管理的性能损失。

标签: python multithreading dictionary optimization bigdata


【解决方案1】:

如果您可以将所有内容存储在 numpy 数组中,则处理速度会更快。我将每个列表的大小增加了 50 万倍以测试可扩展性,这些是我的结果:

from timeit import timeit
import numpy as np

n = 500000
example_dict1 = {'key1':[367, 30, 847, 482, 887, 654, 347, 504, 413, 821]*n,
    'key2':[754, 915, 622, 149, 279, 192, 312, 203, 742, 846]*n, 
    'key3':[586, 521, 470, 476, 693, 426, 746, 733, 528, 565]*n}

def manipulate_values(input_list):
    return_values = []
    for i in input_list:
        new_value = i ** 2 - 13
        return_values.append(new_value)
    return return_values

用你的方法:

for_with_dictionary = timeit("""
for key, value in example_dict1.items():
    example_dict1[key] = manipulate_values(value)
""", "from __main__ import example_dict1,manipulate_values ",number=5)

print(for_with_dictionary)

>>> 33.2095841

使用 numpy:

numpy_broadcasting = timeit("""
array = np.array(list(example_dict1.values()))
array = array ** 2 - 13
""", "from __main__ import example_dict1, np",number=5)
print(numpy_broadcasting)

>>> 5.039885

速度有显着提升,至少6倍。

【讨论】:

  • 这5秒中有多少是转化,多少是计算?
  • 并且计算可以通过就地操作更节省空间array**=2;array-=13
  • @Marcos 谢谢。我很好奇这如何转化为更多的字典键——实际上,字典有数百万个键。
  • 另一个问题:是否可以使用 NumPy 框架进行多线程处理?可以通过让每个线程使用一组唯一的键来尝试这个,例如答案或许在这里:stackoverflow.com/questions/30060088/…
  • 我最初将字典作为熊猫字典,我使用的是.apply()。太慢了。
【解决方案2】:

如果你有足够的内存:

example_dict2 = dict(zip(example_dict1.keys(), np.array(list(example_dict1.values()))**2 -13))
>>> example_dict2
{'key1': array([134676,    887, 717396, 232311, 786756, 427703, 120396, 254003,
       170556, 674028]), 'key2': array([568503, 837212, 386871,  22188,  77828,  36851,  97331,  41196,
       550551, 715703]), 'key3': array([343383, 271428, 220887, 226563, 480236, 181463, 556503, 537276,
       278771, 319212])}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-07-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多