python-hyper / wsproto

Sans-IO WebSocket protocol implementation
https://wsproto.readthedocs.io/
MIT License
261 stars 38 forks source link

over 10% regression in small message performance #152

Closed belm0 closed 2 years ago

belm0 commented 3 years ago

using the newly minted bench/connection.py with some tweaks:

$ pip install wsproto==0.14.1 wsaccel
$ python bench/connection.py
4.636s elapsed, 93usec per iteration

$ pip install wsproto==1.0.0
$ python bench/connection.py
5.192s elapsed, 104usec per iteration

Benchmark patch follows. It should probably be expanded with command line args for count, message size, etc.

diff --git a/bench/connection.py b/bench/connection.py
index 6585944..bc10bfa 100644
--- a/bench/connection.py
+++ b/bench/connection.py
@@ -5,16 +5,16 @@ from typing import List
 import wsproto

 random_seed = 0
-mu = 125 * 1024
-sigma = 75 * 1024
-iterations = 5000
+mu = 125 #* 1024
+sigma = 75 #* 1024
+iterations = 50000
 per_message_deflate = False

 rand = random.Random(random_seed)

-client_extensions: List[wsproto.extensions.Extension] = []
+#client_extensions: List[wsproto.extensions.Extension] = []
 if per_message_deflate:
     pmd = wsproto.extensions.PerMessageDeflate()
     offer = pmd.offer()
@@ -23,11 +23,11 @@ if per_message_deflate:
     client_extensions.append(pmd)
 client = wsproto.connection.Connection(
     wsproto.ConnectionType.CLIENT,
-    extensions=client_extensions,
+    #extensions=client_extensions,
 )

-server_extensions: List[wsproto.extensions.Extension] = []
+#server_extensions: List[wsproto.extensions.Extension] = []
 if per_message_deflate:
     pmd = wsproto.extensions.PerMessageDeflate()
     offer = pmd.offer()
@@ -35,23 +35,24 @@ if per_message_deflate:
     pmd.accept(offer)
 server = wsproto.connection.Connection(
     wsproto.ConnectionType.SERVER,
-    extensions=server_extensions,
+    #extensions=server_extensions,
 )

 start = time.perf_counter()
 for i in range(iterations):
     client_msg = b"0" * max(0, round(rand.gauss(mu, sigma)))
-    client_out = client.send(wsproto.events.BytesMessage(client_msg))
+    client_out = client.send(wsproto.events.BytesMessage(data=client_msg))
     server.receive_data(client_out)
     for event in server.events():
         pass

     server_msg = "0" * max(0, round(rand.gauss(mu, sigma)))
-    server_out = server.send(wsproto.events.TextMessage(server_msg))
+    server_out = server.send(wsproto.events.TextMessage(data=server_msg))
     client.receive_data(server_out)
     for event in client.events():
         pass
 end = time.perf_counter()

-print(f"{end - start:.4f}s")
+dt = end - start
+print(f"{dt:.3f}s elapsed, {dt/iterations * 1e6:.0f}usec per iteration")
pgjones commented 3 years ago

I'm not sure what a possible solution is here, I don't think wsaccel is an option.

belm0 commented 3 years ago

Has anyone done performance profiling on wsproto?

pgjones commented 2 years ago

As wsaccel is no longer maintained (regression came from switching away) I think there is no clear solution to reverse this. However, I'm now testing a mypyc compiled wsproto that could provide a solution.