【问题标题】:Python bool compare vs string compare, which is faster?Python bool compare vs string compare,哪个更快?
【发布时间】:2021-12-22 08:23:19
【问题描述】:

我需要处理一个非常大的 CSV 文件。

在此过程中,第一行需要特别注意。

因此,显而易见的代码是检查 csv 行中的值。 但这意味着对每一行(大约 200.000)进行字符串比较

另一种选择是设置一个布尔值并让布尔比较在“或”表达式中排在第一位。

两个选项如下:

import csv

def do_extra_processing():
    pass


def do_normal_processing():
    pass


if __name__ == "__main__":

    with open('file.csv', newline='') as csvfile:
        lines = csv.reader(csvfile, delimiter=';')

        line_checked: bool = False

        for line in lines:
            # Check the first line: Option 1
            if line[1] == "SomeValue":
                # Every line of the 200000 lines does the string-compare
                do_extra_processing()
            do_normal_processing()

            # Check the first line: Option 2
            if (line_checked) or (line[1] == "SomeValue"):
                # Every line of the 200000 lines does the boolean-compare first and does not evaluate the string compare
                do_extra_processing()
                line_checked = True
            do_normal_processing()

我已经检查过,在“或”表达式中,当第一部分为 True 时,不会计算第二部分。

布尔值在 for 循环上方初始化,并在 extra_processing 完成时在 if 语句中设置。

问题是:使用 bool-compare 的第二个选项明显更快吗?

(无需转换为 ,与37615264 的问题不同)

【问题讨论】:

  • 您好!你能解释一下如何以及在哪里设置布尔值吗?布尔值总是比字符串比较快
  • 感谢@Charley 的快速回复。我已经编辑了问题并解释了布尔值的设置位置。
  • 您的布尔值将保持 True,因为您不会将其重置为 false,因此额外的处理只进行一次。这似乎不是你想要的?所以第二个选项不起作用,似乎不可能实现和抑制字符串比较。
  • 你说“在这个过程中,第一行需要特别注意”。那么你应该使用not line_checked and ... 而不是not line_checked or ... 吗?我认为布尔值的重点是避免字符串比较和除第一行之外的所有行的额外处理,但这不是你的代码 or 所做的。
  • @BertC 它的作用已经很清楚了。不清楚的是你为什么要这样做。我的意思是,您的 bool 的效果是,在第一行中您检查 bool 而不是比较两个字符串,这样可以节省一点,但这是以 additional bool 检查为代价的 每隔 行(您仍然对所有这些行进行字符串比较)。这似乎是一个明显的坏主意,而且不清楚你为什么要考虑

标签: python


【解决方案1】:

(编辑/注意:这适用于我认为 OP 的代码打算做的事情,而不是它实际做的事情。我 asked 是否像我怀疑的那样是一个错误。)

原始版本的作用:

  1. 加载line
  2. 加载1
  3. 加载line[1]
  4. 加载字符串常量。
  5. 进行字符串比较,得到一个布尔值。
  6. 检查布尔值的真实性。

布尔优化版本的作用:

  1. 加载line_checked
  2. 检查布尔值的真实性。

哪个更快?猜一下 :-)。但更好的衡量标准是,您可能会发现两者都不重要,即两者都比每行剩余的实际处理速度要快得多。

无论如何,这里有两个想法需要不需要为第一行之后的行做额外的工作:

  1. 单独的代码:
    with open('file.csv', newline='') as csvfile:
        lines = csv.reader(csvfile, delimiter=';')

        for line in lines:
            if line[1] == "SomeValue":
                do_extra_processing()
            do_normal_processing()
            break

        for line in lines:
            do_normal_processing()
  1. 在第一行之后切换处理函数:
    with open('file.csv', newline='') as csvfile:
        lines = csv.reader(csvfile, delimiter=';')

        def process():
            if line[1] == "SomeValue":
                do_extra_processing()
            do_normal_processing()
            nonlocal process
            process = do_normal_processing
            
        for line in lines:
            process()

未测试。如果您将该代码块保留在全局空间中,后一种解决方案可能需要global 而不是nonlocal。不过,将其放入函数中可能是个好主意。

一个小基准:如果您有我怀疑的错误,并且 bool 旨在避免除第一行之外的所有字符串比较和额外处理,那么我会得到这样的时间:

11.5 ms  11.6 ms  11.6 ms  if is_first_line and line[1] == "Somevalue": doesnt_happen_in_other_lines
45.1 ms  45.3 ms  45.3 ms  if line[1] == "Somevalue": doesnt_happen_in_other_lines

代码(Try it online!):

from timeit import repeat

setup = '''
is_first_line = False
line = [None, "Othervalue"]
'''

statements = [
    'if is_first_line and line[1] == "Somevalue": doesnt_happen_in_other_lines',
    'if line[1] == "Somevalue": doesnt_happen_in_other_lines',
]

for _ in range(3):
    for stmt in statements:
        ts = sorted(repeat(stmt, setup))[:3]
        print(*('%4.1f ms ' % (t * 1e3) for t in ts), stmt)
    print()

【讨论】:

    【解决方案2】:

    在进一步测试之前,我建议使用第二个版本,因为我们都知道测试布尔值比测试字符串相等性更简单

    然后我做了我建议@AidenEllis 做的事情(Windows 上的 Python 3.10),有点惊讶:

    timeit('x = "foo" if a == b else "bar"', '''a=True
    b=False
    ''')
    0.031938999999511
    timeit('x = "foo" if a == b else "bar"', '''a=True
    b=True
    ''')
    0.032499900000402704
    timeit('x = "foo" if a == b else "bar"', '''a="Somevalue"
    b="Somevalue1"
    ''')
    0.03237569999964762
    

    没什么大不了的……

    然后我尝试了:

    timeit('x = "foo" if a else "bar"', 'a=True')
    0.022047000000384287
    timeit('x = "foo" if a else "bar"', 'a=False')
    0.020898400000078254
    

    快了近 30%,看起来不错……

    最后

    timeit('x = "foo" if (a or (b == c)) else "bar"', '''a=True
    b="Somevalue"
    c="Somevalue1"
    ''')
    0.022851300000183983
    

    仍然很重要,但这意味着测试布尔值比比较两个值更快,无论值的类型如何,即使它们是布尔值。和我预期的不太一样……

    我的结论是,我们正在研究实现细节(我给出 Python 版本的原因),唯一明智的答案是 这并不重要:如果有的话,收益应该可以忽略不计与实际处理时间相比。

    【讨论】:

    • 嗯,实际上我怀疑他们有错误,应该使用and 而不是or。但正如路易斯的问题评论所指出的那样,他们想要做什么并不完全清楚。此外,他们使用if 语句,它只在真实情况下执行某些操作,在错误情况下不执行任何操作。而您的条件表达式在这两种情况下都有作用。
    • @KellyBundy:你说得对,我上次的测试相当愚蠢......
    • 我在答案末尾添加了另一个基准测试,就像我认为他们的代码可以正常工作一样,还包括加载line[1]。布尔优化版本只需要四分之一的时间。
    • 我不会把你原来的最后一次测试称为“愚蠢”,顺便说一句。毕竟,它相当于 OP 的代码。你有False而不是他们的True,但你也省略了他们的not,所以取消了。您更新的代码与 OP 的不同。正如您的原始版本及其时间所显示的那样,OP 的“优化”使其变慢。它避免了第一行的单个字符串比较,代价是每隔一行进行一次额外的布尔检查。这显然是个坏主意,这也是我高度怀疑他们的意图的一个原因。
    • 还有一件事:我想说的是,测试一个布尔值比比较两个布尔值更快。因为比较结果是一个布尔值,然后这个布尔值被测试。因此,比较确实做了更多的工作。我猜 Python 可以通过首先检查类型并针对某些类型进行优化来检查==,但这对于每个比较来说都是额外的成本。而且很可能会产生总体负面影响。 (虽然如果 Python 真的做到了,我不会完全感到惊讶,但不会是第一个 optimization not visible in bytecode)。
    【解决方案3】:

    可能不是您正在寻找的快速答案,但您为什么不比较两者的处理时间,分别进行两种方式,然后检查哪个完成得更快。

    如果您只想快速比较两组不同的代码,请使用它:

    import time
    
    start = time.perf_counter()
    
    # do your processes here
    
    finish = time.perf_counter()
    total = finish - start
    print(f"Process Time: {round(total * 1000, 2)}ms")
    

    好吧,回到它XD

    【讨论】:

    • 在 Python 中,您不应该建议使用原始时间来对处理进行基准测试,因为标准库包含 timeit 模块,它可以很好地重复操作多次(可能有初始化阶段)以限制外部过程的影响。不会因为基准建议很好而投反对票,但使用 timeit 会更好......
    • @SergeBallesta OP 说这是一个“非常大的 CSV 文件”。我不认为timeit 很有优势,至少不是因为你提到的原因。
    • 是的,我也认为这里不需要时间。但我刚刚发现 timeit.default_counter() 更准确。但是你可以在这里保持简单。
    • 刚才那个答案编辑真的让我笑了。期待 Serge 的反应,就像你在拖钓他们:-)
    • 啊,XD oki,这是我在 StackOverflow 的第一天,所以,慢慢习惯吧:>
    猜你喜欢
    • 1970-01-01
    • 2013-02-26
    • 2011-12-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多