首页 > 编程问答 >Python 中更快的套接字

Python 中更快的套接字

时间:2024-07-20 21:03:00浏览次数:12  
标签:python performance sockets python-3.x

我有一个用 Python 编写的服务器客户端,它通过 LAN 运行。该算法的某些部分密集使用套接字读取,其执行速度比用 C++ 编写的几乎相同的 慢 3-6 倍。有哪些解决方案可以使 Python 套接字读取速度更快? 我实现了一些简单的缓冲,我的用于处理套接字的类如下所示:

P.S.:分析还显示大部分时间都花在读取套接字上。| ||编辑:

import socket
import struct

class Sock():
    def __init__(self):
        self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.recv_buf = b''
        self.send_buf = b''

    def connect(self):
        self.s.connect(('127.0.0.1', 6666))

    def close(self):
        self.s.close()

    def recv(self, lngth):
        while len(self.recv_buf) < lngth:
                self.recv_buf += self.s.recv(lngth - len(self.recv_buf))

        res = self.recv_buf[-lngth:]
        self.recv_buf = self.recv_buf[:-lngth]
        return res

    def next_int(self):
        return struct.unpack("i", self.recv(4))[0]

    def next_float(self):
        return struct.unpack("f", self.recv(4))[0]

    def write_int(self, i):
        self.send_buf += struct.pack('i', i)

    def write_float(self, f):
        self.send_buf += struct.pack('f', f)

    def flush(self):
        self.s.sendall(self.send_buf)
        self.send_buf = b''

因为数据是在已知大小的块中接收的,所以我可以立即读取整个块。 所以我将我的代码更改为:

'从套接字在这段代码中看起来是最佳的。但现在

class Sock():
    def __init__(self):
        self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.send_buf = b''

    def connect(self):
        self.s.connect(('127.0.0.1', 6666))

    def close(self):
        self.s.close()

    def recv_prepare(self, cnt):
        self.recv_buf = bytearray()
        while len(self.recv_buf) < cnt:
            self.recv_buf.extend(self.s.recv(cnt - len(self.recv_buf)))

        self.recv_buf_i = 0

    def skip_read(self, cnt):
        self.recv_buf_i += cnt

    def next_int(self):
        self.recv_buf_i += 4
        return struct.unpack("i", self.recv_buf[self.recv_buf_i - 4:self.recv_buf_i])[0]

    def next_float(self):
        self.recv_buf_i += 4
        return struct.unpack("f", self.recv_buf[self.recv_buf_i - 4:self.recv_buf_i])[0]

    def write_int(self, i):
        self.send_buf += struct.pack('i', i)

    def write_float(self, f):
        self.send_buf += struct.pack('f', f)

    def flush(self):
        self.s.sendall(self.send_buf)
        self.send_buf = b''

recv 成为第二个瓶颈,每次调用需要大约 1 毫秒(3000 个 CPU 周期)来解包。是否有可能让它们更快,就像在 C++ 中一样? next_int and next_float became the second bottleneck, they take about 1 msec (3000 CPU cycles) per call just to unpack. Is it possible to make them faster, like in C++?


好的,让我们探讨一下如何加快你的 Python 套接字代码的速度。你做了一些重要的观察,并且你走在正确的轨道上!这里有一个结合你的洞察力和一些额外技巧的细分:

你已经做过的,而且工作得很好的

  1. 缓冲: 你的 Sock 类中的 recv_buf 是一个 优秀的 优化。它通过最小化从套接字读取的系统调用次数来极大地提高性能。系统调用会产生开销,因此将它们成批读取是关键。

  2. 预先读取( recv_prepare ): 你知道你的数据块的大小,这是一个巨大的优势。通过提前读取整个块,你进一步减少了开销并提高了数据局部性。

让我们优化 next_int next_float

你的分析表明 struct.unpack 成为了瓶颈。这是有道理的,因为它在幕后进行了一些工作(数据复制、类型转换等)。以下是如何解决这个问题的方法:

  • 内存视图( memoryview ): 内存视图提供了一种在不复制数据的情况下访问字节缓冲区的方法。尝试一下:

    ```python def next_int(self): self.recv_buf_i += 4 return struct.unpack("i", memoryview(self.recv_buf)[self.recv_buf_i-4:self.recv_buf_i])[0]

    def next_float(self): self.recv_buf_i += 4 return struct.unpack("f", memoryview(self.recv_buf)[self.recv_buf_i-4:self.recv_buf_i])[0] ```

  • NumPy(如果你正在处理数值数据): 如果你正在进行大量的数值处理,NumPy 是你最好的朋友。它可以用优化的 C 代码进行解包,这比纯 Python 快得多。

    ```python import numpy as np

    def recv_prepare(self, cnt): self.recv_buf = bytearray() # ... (你的读取逻辑)

    # 如果你知道数据的类型,指定 dtype(例如 '<f4' 用于小端浮点数)
    self.recv_view = np.frombuffer(self.recv_buf, dtype=np.float32) 
    self.recv_buf_i = 0
    

    def next_float(self): val = self.recv_view[self.recv_buf_i] self.recv_buf_i += 1 return val ```

其他需要考虑的事项

  • 网络: 虽然你专注于套接字代码,但请记住,网络本身可能是瓶颈。确保你的网络设置已针对 LAN 环境进行了优化(例如,千兆以太网)。
  • 算法: 如果在进行了这些优化之后,你仍然需要更多的速度,则可能需要重新访问你算法的 Python 部分。C++ 可以 更快,但通常情况下,精心编写的 Python 可以非常高效。
  • Cython 或 Numba: 对于性能至关重要的代码部分,你可以考虑使用 Cython(编写可以编译为 C 扩展的 Python 超集)或 Numba(使用即时编译来加速 Python 函数)。

概要

通过使用 memoryview 或 NumPy 优化解包,你应该会看到性能得到显著提升。持续分析你的代码将帮助你确定剩余的瓶颈。祝你好运,玩得开心!

标签:python,performance,sockets,python-3.x
From: 10742639

相关文章

  • .py文件是python脚本吗
    .py文件是python的脚本文件。Python在执行时,首先会将.py文件中的源代码编译成Python的bytecode(字节码),然后再由PythonVirtualMachine(Python虚拟机)来执行这些编译好的bytecode。这种机制的基本思想跟Java,.NET是一致的。然而,PythonVirtualMachine与Java或.NET的Virtual......
  • anaconda与python是什么关系
    Anaconda是Python的一个发行版,里面内置了很多工具,不用单独安装,因为做了优化也免去了单独安装带来的一些麻烦。Anaconda是一种Python语言的免费增值开源发行版,用于进行大规模数据处理、预测分析,和科学计算,致力于简化包的管理和部署。Anaconda使用软件包管理系统Conda进行包管......
  • 用于检查 Google Gemini 支持的所有 GenerativeAI 模型的 Python 代码是什么?
    作为GenerativeAI世界的新手,我正在尝试加载预先训练的文本生成模型并做一些不起作用的事情。这就是我加载GenerativeAI模型的方式。fromvertexai.generative_modelsimportGenerativeModelgeneration_model=GenerativeModel("gemini-pro")由于它不......
  • 想让字典操作更优雅?自定义Python字典类型,简化你的代码库!
    目录1、继承dict类......
  • Vispy,一个专门用于创建交互式可视化的python库
    目录什么是Vispy?为什么选择Vispy?安装Vispy基础概念创建第一个Vispy可视化2D图形的进阶使用3D图形的绘制交互性结论什么是Vispy?Vispy是一个高性能的Python库,专门用于创建交互式可视化。它支持2D和3D数据的可视化,并且可以轻松地集成到各种应用程序中。Vispy的核心优......
  • python 复制 excel 保留文档中完全相同的参数(样式、单元格大小和融合、边框...)
    我正在寻找一种在其他文件中复制和excel的方法。我有一个“file_1.xlsx”,但我想要一个不存在的“file_1_copy.xlsx”。副本必须与原始文件完全相同,这意味着单元格大小、它们的融合、单元格中文本的颜色、背景、如果有边框,就好像我用右键单击。我有:importopenpyxlfromope......
  • WebKit 引擎:WebSockets 的幕后英雄
    WebKit引擎:WebSockets的幕后英雄在现代Web应用中,WebSockets提供了一种在用户和服务器之间建立持久、全双工通信的方法。与传统的HTTP请求相比,WebSockets可以减少通信开销,提高性能,并支持实时通信。WebKit作为许多流行浏览器的底层引擎,对WebSockets的支持至关重要。本文将......
  • python查看某个包的当前安装版本以及最新版本
    方法1:使用pip和--outdated参数你可以使用piplist--outdated命令来查看哪些包有更新版本可用。这个命令会列出所有安装的包以及它们在PyPI上的最新版本。piplist--outdated这将输出一个包列表,包含当前版本和最新版本,例如:PackageVersionLatestTyp......
  • 【python】错误 SyntaxError: invalid syntax的解决方法总结
    【python】错误SyntaxError:invalidsyntax的解决方法总结在Python编程中,SyntaxError:invalidsyntax是一个常见的错误,通常表示Python解释器在尝试解析代码时遇到了语法错误。这种错误可能由多种原因引起,包括拼写错误、缺少关键字、不恰当的缩进等。本文将深入探讨......
  • MiniQMT国债逆回购策略Python代码全解析
    文章目录......