为何PyPy处理用户输入构建列表并打印的速度慢于CPython?
这确实是个反直觉的情况——毕竟PyPy靠着JIT编译,大多时候比CPython快不少,但你的代码刚好踩中了PyPy的几个“软肋”,反而让CPython跑赢了:
1. JIT Warm-Up Overhead Kills Small/Fast Tasks
PyPy的核心优势是即时编译(JIT):它会在代码反复执行同一段逻辑时,把这段逻辑编译成机器码,后续执行就会快很多。但你的代码逻辑太简单了——循环里的操作无非是几个判断和输出,就算测试用例的t不小,整个程序的运行时间可能还没等PyPy完成JIT预热就结束了。反而CPython是纯解释执行,没有额外的编译预热成本,对付这种短平快的任务,直接跑反而更高效。
2. IO Operations Are a Bottleneck (And PyPy’s IO Isn’t Always Better)
你的代码有大量的input()和print()调用——这属于IO密集型操作。PyPy的标准库IO实现和CPython有差异,在频繁调用print()的场景下,PyPy的性能反而不如CPython。毕竟CPython的IO层经过了几十年的打磨优化,小批量多次输出的场景下,它的开销比PyPy更小。
3. Trivial Loops Don’t Benefit From JIT
你的主循环里的逻辑太简单了:只是判断两个条件,然后输出值。这种极其基础的循环,JIT编译器根本没机会发挥优势——编译这段简单代码的时间,甚至比直接解释执行它的时间还长。CPython处理这种简单循环的开销反而更低,自然跑得更快。
How to Make This Code Fast on PyPy?
如果想让PyPy发挥它的优势,重点是优化IO操作,减少频繁调用print()的开销:
- 把所有输出先收集到一个列表里,最后一次性输出,这样能大幅减少IO系统调用的次数:
import math import sys def main(): data = sys.stdin.read().split() t = int(data[0]) a = [] b = [] for s in data[1:t+1]: n = float(s) a.append(math.floor(n)) b.append(math.ceil(n)) l = -sum(a) output = [] i = 0 while i < len(a): if l > 0 and a[i] != b[i]: output.append(str(b[i])) l -= 1 else: output.append(str(a[i])) i += 1 print('\n'.join(output)) if __name__ == "__main__": main()
这么改之后,不管是PyPy还是CPython都会更快,而且PyPy的JIT能更好地优化核心的循环逻辑,最终跑起来会比CPython还快。
另外,如果题目输入的数都是整数或者固定格式的小数,尽量避免用float()转换——用字符串处理或者直接转整数(如果符合题意)能进一步减少开销,但这得看题目具体要求。
内容的提问来源于stack exchange,提问作者Susanta Mukherjee

