pypy 可能比默认编译器慢的可能原因
Possible reasons why pypy might be slower than default compiler
我正在为学校做这个项目,我应该使脚本 运行 更快,因为它非常慢。在过去的几个月里,由于我无法访问实际脚本,我一直在测试我编写的虚拟脚本,它执行相同的任务。为此,我发现 pypy 与多处理一起使我的脚本 运行 至少快 10 倍。因此,在访问实际脚本后,我对其应用了多处理,并使用 pypy 运行 对其进行了处理。然而,令人惊讶的是,代码 运行 与 pypy 运行s 相比没有 pypy 慢 2 倍,而不是显示任何性能改进。可能是什么原因?实际脚本使用 numpy、pandas 等库,并建立数据库连接以将输出写入进程,以便稍后由 Web 服务器访问。与 pypy 相比,numpy 或 pandas 在常规编译器中的编译速度更快吗?如果不是,还有什么可以解释这个?另外,也欢迎任何让它更快的建议:)
P.S 已经应用了多处理,它只比原始代码快大约 40 秒,这是不够的。
编辑:添加代码
它是一个脚本,用于生成谁与谁接触了多长时间和接触地点 - 用于医院的接触者追踪。
基本上,它应该做的是,它读取一个 csv 文件,其中包含传感器在不同时间的所有位置,然后有一个算法生成所有联系人并将其写入数据库以供网络获取服务器稍后。
代码如下。它非常长,可能是我之前没有 post 的原因:)
def resampleData(_beaconDevice, _timeInterval, _locationPtsX, _locationPtsY, 数据库):
database.child("contact").child("progress").set(20)
beaconData = pd.DataFrame({'timestamp': _timeInterval, 'Device': _beaconDevice, 'Beacon Longtitude': _locationPtsX, 'Beacon Latitude': _locationPtsY})
beaconData.set_index('timestamp', inplace=True)
beaconData.index = pd.to_datetime(beaconData.index)
beaconData = beaconData.groupby('Device').resample('S')['Beacon Longtitude', 'Beacon Latitude'].mean().ffill()
return beaconData
def processTwo(connectedDev、temp、devicelist、increment、_patientlist、_beaconData、_start、_end、_devlist、_scale、数据库、用户、_distance):
for numPatients, patientName in enumerate(_patientlist):
timestamp = _beaconData.loc[patientName, :].index.tolist()
patientX = _beaconData.loc[patientName, 'Beacon Longtitude'].tolist()
patientY = _beaconData.loc[patientName, 'Beacon Latitude'].tolist()
progressUnit = (55/len(timestamp))
for t, timeNum in enumerate(timestamp):
if timeNum >= _start and timeNum <= _end:
for device, devNum in enumerate(_devlist):
if devNum != patientName:
if devNum in devicelist:
logger.log ("Finding Contacts...", timeNum)
if increment<55:
increment += progressUnit
try:
database.child("contact").child("progress").set(30+increment)
except: logger.info("exception")
isContact, contactLoc = inContact(patientName, patientX, patientY, devNum, t, _beaconData, _scale, _distance)
if isContact==True:
logger.log (patientName, "in contact with", devNum, "!")
temp.append(patientName)
temp.append(timeNum)
temp.append(int(devNum))
temp.append(patientX[t])
temp.append(patientY[t])
temp.append(contactLoc[0])
temp.append(contactLoc[1])
connectedDev.append(temp)
temp = []
processsTwo 函数是代码中其他七个密集计算函数之一。 for 循环处理 DataFrame。
The actual script uses libraries like numpy, pandas and does a database connection…
如果您的大部分时间花在 numpy、pandas 和数据库调用上,而不是花在 Python 循环或计算上,那么 PyPy 几乎没有什么可以加速的。
numpy 和 pandas 都是用 C 编写的扩展模块(带有一点 C++、Fortran 和汇编语言),大多数数据库库也是如此。扩展模块在安装时被编译为本地代码。而且无论是什么解释器驱动它,本机代码都会 运行 完全相同。特别是,它不会在 PyPy 中通过任何类型的 JIT。* 因此,除非您在某处进行一些重要的 Python 计算,否则 PyPy 无法使任何事情变得更快。
同时,PyPy 实际上可以让事情变得更慢。 CPython 可以直接访问像 numpy 这样的 CAPI 扩展,但是 PyPy 必须伪装成 CPython 才能与扩展代码对话,这是通过名为 CPyExt 的包装器实现的。 PyPy 中 numpy 上的 FAQ 表示 CPyExt 是 "infamously slow"。这有点 unfair/self-deprecating,特别是考虑到他们在过去 5 年中所做的所有工作;对于许多 numpy 程序,您甚至不会注意到其中的区别。但是在某些情况下您仍然会这样做。而且你提到了多进程,很多情况都涉及跨进程共享数组。
偶尔,使用 numpypy
分支(以 PyPy-friendly 方式重新实现 numpy 的核心)是值得的。截至 2018 年,这是一个已弃用的解决方案(最后一点不完整的部分可能永远不会完成),但如果出于某种原因你真的需要一起使用 numpy 和 PyPy,并且你 运行 正在进入其中一个那些慢速的地区,它仍然是一个选择。
* 如果您需要 JIT 数字代码,Jython 或 IronPython 可以与 JVM 或 .NET 运行time 的数字库一起使用,它们 运行 通过 JIT。但是,对于大多数用例,我不知道它们中的任何一个实际上和 numpy 一样快。同时,您可能想看看 numba
with numpy in CPython,它通常可以对您编写的包装器代码进行 JIT,从而比 PyPy 更好地驱动您的 numpy 工作。
我正在为学校做这个项目,我应该使脚本 运行 更快,因为它非常慢。在过去的几个月里,由于我无法访问实际脚本,我一直在测试我编写的虚拟脚本,它执行相同的任务。为此,我发现 pypy 与多处理一起使我的脚本 运行 至少快 10 倍。因此,在访问实际脚本后,我对其应用了多处理,并使用 pypy 运行 对其进行了处理。然而,令人惊讶的是,代码 运行 与 pypy 运行s 相比没有 pypy 慢 2 倍,而不是显示任何性能改进。可能是什么原因?实际脚本使用 numpy、pandas 等库,并建立数据库连接以将输出写入进程,以便稍后由 Web 服务器访问。与 pypy 相比,numpy 或 pandas 在常规编译器中的编译速度更快吗?如果不是,还有什么可以解释这个?另外,也欢迎任何让它更快的建议:)
P.S 已经应用了多处理,它只比原始代码快大约 40 秒,这是不够的。
编辑:添加代码 它是一个脚本,用于生成谁与谁接触了多长时间和接触地点 - 用于医院的接触者追踪。 基本上,它应该做的是,它读取一个 csv 文件,其中包含传感器在不同时间的所有位置,然后有一个算法生成所有联系人并将其写入数据库以供网络获取服务器稍后。
代码如下。它非常长,可能是我之前没有 post 的原因:)
def resampleData(_beaconDevice, _timeInterval, _locationPtsX, _locationPtsY, 数据库):
database.child("contact").child("progress").set(20)
beaconData = pd.DataFrame({'timestamp': _timeInterval, 'Device': _beaconDevice, 'Beacon Longtitude': _locationPtsX, 'Beacon Latitude': _locationPtsY})
beaconData.set_index('timestamp', inplace=True)
beaconData.index = pd.to_datetime(beaconData.index)
beaconData = beaconData.groupby('Device').resample('S')['Beacon Longtitude', 'Beacon Latitude'].mean().ffill()
return beaconData
def processTwo(connectedDev、temp、devicelist、increment、_patientlist、_beaconData、_start、_end、_devlist、_scale、数据库、用户、_distance):
for numPatients, patientName in enumerate(_patientlist):
timestamp = _beaconData.loc[patientName, :].index.tolist()
patientX = _beaconData.loc[patientName, 'Beacon Longtitude'].tolist()
patientY = _beaconData.loc[patientName, 'Beacon Latitude'].tolist()
progressUnit = (55/len(timestamp))
for t, timeNum in enumerate(timestamp):
if timeNum >= _start and timeNum <= _end:
for device, devNum in enumerate(_devlist):
if devNum != patientName:
if devNum in devicelist:
logger.log ("Finding Contacts...", timeNum)
if increment<55:
increment += progressUnit
try:
database.child("contact").child("progress").set(30+increment)
except: logger.info("exception")
isContact, contactLoc = inContact(patientName, patientX, patientY, devNum, t, _beaconData, _scale, _distance)
if isContact==True:
logger.log (patientName, "in contact with", devNum, "!")
temp.append(patientName)
temp.append(timeNum)
temp.append(int(devNum))
temp.append(patientX[t])
temp.append(patientY[t])
temp.append(contactLoc[0])
temp.append(contactLoc[1])
connectedDev.append(temp)
temp = []
processsTwo 函数是代码中其他七个密集计算函数之一。 for 循环处理 DataFrame。
The actual script uses libraries like numpy, pandas and does a database connection…
如果您的大部分时间花在 numpy、pandas 和数据库调用上,而不是花在 Python 循环或计算上,那么 PyPy 几乎没有什么可以加速的。
numpy 和 pandas 都是用 C 编写的扩展模块(带有一点 C++、Fortran 和汇编语言),大多数数据库库也是如此。扩展模块在安装时被编译为本地代码。而且无论是什么解释器驱动它,本机代码都会 运行 完全相同。特别是,它不会在 PyPy 中通过任何类型的 JIT。* 因此,除非您在某处进行一些重要的 Python 计算,否则 PyPy 无法使任何事情变得更快。
同时,PyPy 实际上可以让事情变得更慢。 CPython 可以直接访问像 numpy 这样的 CAPI 扩展,但是 PyPy 必须伪装成 CPython 才能与扩展代码对话,这是通过名为 CPyExt 的包装器实现的。 PyPy 中 numpy 上的 FAQ 表示 CPyExt 是 "infamously slow"。这有点 unfair/self-deprecating,特别是考虑到他们在过去 5 年中所做的所有工作;对于许多 numpy 程序,您甚至不会注意到其中的区别。但是在某些情况下您仍然会这样做。而且你提到了多进程,很多情况都涉及跨进程共享数组。
偶尔,使用 numpypy
分支(以 PyPy-friendly 方式重新实现 numpy 的核心)是值得的。截至 2018 年,这是一个已弃用的解决方案(最后一点不完整的部分可能永远不会完成),但如果出于某种原因你真的需要一起使用 numpy 和 PyPy,并且你 运行 正在进入其中一个那些慢速的地区,它仍然是一个选择。
* 如果您需要 JIT 数字代码,Jython 或 IronPython 可以与 JVM 或 .NET 运行time 的数字库一起使用,它们 运行 通过 JIT。但是,对于大多数用例,我不知道它们中的任何一个实际上和 numpy 一样快。同时,您可能想看看 numba
with numpy in CPython,它通常可以对您编写的包装器代码进行 JIT,从而比 PyPy 更好地驱动您的 numpy 工作。