python 使用类成员变量,拼接字符串导致运行极慢

由于需求,需要对前期一个流量处理脚本进行重写,写成了类的形式,实际运行的时候发现速度变得极慢,原来要0.7秒处理完,改后变成5秒处理完,原来2秒处理完的现在改后变成要100多秒,但是写法和函数几乎是照搬过来的一开始没看出问题

def __loadPcap(self, shell_str: str):
        """
        调用tshark处理pcap包,处理的结果放在pipe里,逐行读取,达到上限后就退出
        """
        cmd = shlex.split(shell_str)
        with subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) as fp:
            try:
                line_num = 0
                pipe_line = fp.stdout.readline()
                cmd_status = fp.poll()

                while pipe_line or cmd_status:
                    try:
                        line_str = pipe_line.decode('utf-8')
                    except UnicodeDecodeError:
                        # UrlEncodedFormEntity编码格式默认是iso_8859_1制
                        line_str = pipe_line.decode('iso_8859_1', errors="ignore")
                    if line_num > self.max_lines and line_str == '  },\n':
                        self.json_str += "}]"
                        self.parse_msg.append("1\t" + self.parse_status['1'] + "\texceed limit 500000 lines")
                        break

                    self.json_str += line_str
                    line_num += 1
                    pipe_line = fp.stdout.readline()
                    cmd_status = fp.poll()

            except Exception as e:
                self.parse_msg.append("3\t" + self.parse_status['3'] + "\tread json str error--" + str(e))

排查后发现原因出在self.json_str += line_str,原来的程序用的是本地变量,json_str+=line_str就没事,我这边测试了下发现结果如下:
1.把self.json_str改成类成员class.json_str 依然很慢
2.一开始用本地变量json_str,最后的时候再把json_str赋值给self.json_str 速度变为之前一样
3.self.json_str+=line_str,其中line_str改成固定为空字符串和“hi”短字符串,发现时间变短,但是依旧比本地变量时间长一些

原因:
self.json_str += line_str 在字符串很大时导致性能下降的问题确实是由于字符串拼接的方式。当使用 += 进行字符串拼接时,Python会为新的字符串分配一块新的内存,然后将原始字符串和新增加的部分复制到新内存中。随着字符串变得越来越大,这种方式的内存分配和复制操作会变得越来越耗时。应该使用StringIO来进行操作,这么写比用本地变量+=拼接字符串还要快

from io import StringIO

json_str = StringIO()
# ...
json_str.write(line_str)
# ...
json_str = json_str.getvalue()

那么为什么用本地变量还勉强能跑,用对象变量直接炸了呢?个人理解是,使用局部变量 json_str 时,每次字符串拼接都会创建一个新的字符串对象,而原始字符串对象将不再被引用。由于局部变量的生命周期较短,垃圾回收器可以更快地回收不再使用的内存。

使用类实例属性 self.json_str 时,垃圾回收器可能无法立即回收不再使用的内存。这是因为实例属性的生命周期通常比局部变量要长,这导致垃圾回收器在处理不再使用的内存时表现出较大的延迟,所以用实例属性会变慢这么多,特别是json_str很长很大的时候

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值