Skip to content

GitLab

  • Projects
  • Groups
  • Snippets
  • Help
    • Loading...
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
    • Contribute to GitLab
  • Sign in / Register
pypy
pypy
  • Project overview
    • Project overview
    • Details
    • Activity
    • Releases
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributors
    • Graph
    • Compare
  • Issues 603
    • Issues 603
    • List
    • Boards
    • Labels
    • Service Desk
    • Milestones
  • Merge Requests 13
    • Merge Requests 13
  • CI/CD
    • CI/CD
    • Pipelines
    • Jobs
    • Schedules
  • Operations
    • Operations
    • Incidents
    • Environments
  • Packages & Registries
    • Packages & Registries
    • Container Registry
  • Analytics
    • Analytics
    • CI/CD
    • Repository
    • Value Stream
  • Wiki
    • Wiki
  • Snippets
    • Snippets
  • Members
    • Members
  • Activity
  • Graph
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
Collapse sidebar

This instance will be upgraded to Heptapod 0.21.0rc2 on 2021-04-16 at 11:00 UTC+2 (a few minutes of down time)

  • PyPy
  • pypypypy
  • Issues
  • #2091

Closed
Open
Created Jul 21, 2015 by Bitbucket Importer@bitbucket_importerMaintainer

non-blocking socket.send slow (gevent)

Created originally on Bitbucket by jamadden (Jason Madden)

gevent implements a blocking socket.sendall for non-blocking sockets with a simple loop over socket.send, catching EWOULDBLOCK as needed. (This isn't necessarily specific to gevent, of course.) In benchmarks, this is substantially slower under PyPy than it is under CPython, around 5 to 6 times slower.

Here's a small example that reproduces the problem; start the script once with an argument to be the server and put it in the background, then again to be the client. (This is a simplified, non-gevent version of a benchmark Denis wrote; it's the only benchmark that PyPy is outperformed by CPython.)

#! /usr/bin/env python
from __future__ import print_function
import sys
import time
import socket


def serve():
	server = socket.socket()
	server.bind(("127.0.0.1", 9999))
	server.listen(1)
	while True:
		client, _ = server.accept()

		while client.recv(4096):
			pass


def _sendall(conn, data):
	data_memory = memoryview(data) # if memoryview is left out, CPython gets slow; makes no diff to PyPy
	len_data_memory = len(data_memory)
	data_sent = 0
	while data_sent < len_data_memory:
		try:
			data_sent += conn._sock.send(data_memory[data_sent:])
		except socket.error as ex:
			if ex.args[0] == 35: # EWOULDBLOCK
				continue
			raise

def main():
	length = 50 * 0x100000
	data = b"x" * length
	spent_total = 0


	conn = socket.create_connection(("", 9999))
	conn._sock.setblocking(0) # non-blocking is crucial

	N = 20
	for i in range(N):
		start = time.time()
		_sendall(conn, data)
		spent = time.time() - start
		print("%.2f MB/s" % (length / spent / 0x100000))
		spent_total += spent

	print("~ %.2f MB/s" % (length * N / spent_total / 0x100000))


if __name__ == "__main__":
	if len(sys.argv) > 1:
		serve()
	else:
		main()

On one machine, CPython sends at ~ 1160MB/s, while PyPy 2.6/2.7 sends at ~150MB/s.

The _sendall function is a simplified version of what gevent actually uses to implement socket.sendall.

Interestingly, on CPython, if you take out the call to memoryview and instead pass the raw string argument to socket.send, it performs similarly to PyPy. This leads me to guess that it's something to do with pinning the buffer in memory repeatedly that's slowing PyPy down.

I've tried variations on how the data gets sliced to no avail. I have found that increasing the socket's SO_SNDBUF increases performance---using a very large buffer gets us about halfway to CPython performance.

Is there anything I can do as a maintainer of gevent to improve the performance of socket.sendall? I'm not against using PyPy internal functions, I just couldn't find any to use :) Or should I recommend that users set large write buffers on their sockets? Or is this a "bug" in PyPy that can be improved?

To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information
Assignee
Assign to
None
Milestone
None
Assign milestone
Time tracking
None
Due date
None