【问题标题】:python performance issue while searching in a huge list在大量列表中搜索时出现python性能问题
【发布时间】:2019-08-20 17:52:58
【问题描述】:

我需要(显着地)加快在“巨大”单维无符号值列表中的搜索。该列表有 389.114 个元素,我需要在添加项目之前执行检查以确保它不存在 我做了 1500 万次这样的检查... 当然,这需要太多时间 我发现最快的方法是:

if this_item in my_list:
    i = my_list.index(this_item)
else:
    my_list.append(this_item)
    i = len(my_list)
    ...

我正在根据时间序列日志构建数据集 这些(巨大的)日志中的一列是一条短信,非常冗长 为了显着加快这个过程,我用 Adler32() 将这个文本转换成一个无符号的,并得到一个唯一的数值,这很棒 然后我将消息存储在 PostgreSQL 数据库中,并以此值为索引

对于我的日志文件的每一行(总共 1500 万条),我需要更新我的唯一消息数据库(389.114 条唯一消息) 这意味着对于每一行,我需要检查消息 ID 是否属于我的内存列表

我尝试了“... in list”,与字典、numpy 数组、将列表转换为字符串并使用 string.search()、在具有良好索引的数据库中进行 sql 查询相同... 当列表加载到内存中时,没有什么比“if item in list”更好的了(非常快)

if this_item in my_list:
    i = my_list.index(this_item)
else:
    my_list.append(this_item)
    i = len(my_list)

对于 1500 万次迭代,其中包含一些内容并且在列表中没有搜索: - 生成2张1500万行的表(特征和目标)需要8分钟 - 当我激活上面的代码来检查消息 ID 是否已经存在时,需要 1 小时 35 百万 ...

我该如何优化这个?

感谢您的帮助

【问题讨论】:

  • 测试一个集合的成员很快。
  • 很抱歉告诉你,但你的问题不好。您不只是提出建议,而是针对您正在解决的特定问题提出性能建议,但您只提供了一个极其模糊的陈述并显示了 2 行伪代码。性能非常严格地取决于完全您在做什么。例如,我可以告诉您只需在该代码中删除 my_list.index 调用,因为您不需要这两行中的元素索引。 显然您确实需要这些信息,但取决于您需要它的方式和时间,最快的实现可能会发生巨大变化。
  • 例如:您可以首先添加所有这些“消息”,只有在完成后,您才能构建从消息到索引的映射,并使用它对索引执行快速查找。不知道这是否可行,或者您需要“在线”算法或其他什么。在任何情况下,如果您想要性能建议,您必须提供一个最小的工作示例,人们可以使用该示例来实际分析可能的解决方案......或者您希望人们随机选择你,然后你需要实施它们并回来说“这比以前更糟”?两个部分都浪费了时间。
  • 例子和我写的一样简单。我从一些文件中逐行读取大量日志,并且对于我读取的每一行(= 1 用拆分解析的字符串),我调用一个函数来转换/简化它,然后在其中写入一个新行输出文件。
  • 我认为这部分代码对这个问题没有兴趣。我调用的函数提取日志字符串的所有“列”,其中之一是文本消息。此文本消息可能会在不同的日志中多次出现。这意味着对于 1500 万行日志,我调用 1500 万次函数,如果 txt 消息尚未存储和索引,它将检查 1500 万次

标签: list performance search python-3.7


【解决方案1】:

如果你的代码大致是这样的:

my_list = []
for this_item in collection:
  if this_item in my_list:
    i = my_list.index(this_item)
  else:
    my_list.append(this_item)
    i = len(my_list)
    ...

然后它将在 O(n^2) 时间内运行,因为列表的 in 运算符是 O(n)。

如果您使用字典(通过哈希表实现),则可以实现线性时间:

my_list = []
table = {}
for this_item in collection:
  i = table.get(this_item)
  if i is None:
    i = len(my_list)
    my_list.append(this_item)
    table[this_item] = i
  ...

当然,如果您不关心按原订单处理商品,您可以这样做:

for i, this_item in enumerate(set(collection)):
  ...

【讨论】:

  • 谢谢 Alex(和最初的 Mark)。
  • 谢谢亚历克斯(最初是马克)。你是对的,使用 set() 而不是 list() 改进了成员资格测试(从 2 倍快到 20 倍的大列表快)!对于我的测试,我不需要检索索引,因为索引是存储的值,所以不需要获取它。只需要测试一个未签名到一组未签名(而不是列表)的成员资格。
  • 为了很简单,我还是用一个列表来获取初始sql查询结果,如果是新的就添加一个元素。但是我将列表复制到带有 my_set=set(my_list) 的集合中,每当我修改 my_list(添加一个元素),然后: my_set 中的 my_item 比 my_list 中的 my_item 快得多!非常感谢
  • 我逐行读取了 10 个日志文件,总共 1500 万行。对于每一行,我处理“列”(拆分),将文本转换为数字,将字典存储到 PostgreSQL 数据库中(供以后查询)。这部分仅检查消息是否已存储就很耗时。一旦用关节而不是文本“简化”了一行,我将其存储在最终文件中以合并所有日志,即 TimeSeries,然后将此文件加载到内存中的列表(1Gb)中,按时间戳对列表进行排序,并重写文件。
  • 然后我读取了这个最终排序的文件并将数据存储到 PostgreSQL 中的 2 个表中以供以后的机器学习使用。我还可以优化最后一部分,尝试一步完成,但实际上整个过程需要 5 分钟(而不是 1 小时 30 分钟,这要归功于 set() ;-)),而且已经好多了
猜你喜欢
  • 1970-01-01
  • 2023-02-06
  • 1970-01-01
  • 2020-09-04
  • 2018-04-13
  • 1970-01-01
  • 2020-12-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多