ws: tiny asynchronous webserver framework without uasyncio
Re: ws: tiny asynchronous webserver framework without uasyncio
Wow, you really spent some effort to prove your point.
I guess it depends on your use-case for your server. You are dealing with tiny (scripted) requests and (simple) responses... is that what your end goal will look like?
If you are aiming for a more general purpose framework, then I would do more realistic testing... starting with just serving static content. I know you have your webrepl.html example, but I am thinking of a more typical multi-threaded browser request loading a page with style sheets, script files and images, etc...
Using PicoWeb, doing just do this, it does struggle with some pages (on an ESP8266). That said, it actually handles the multiple incoming request pretty well.
I guess it depends on your use-case for your server. You are dealing with tiny (scripted) requests and (simple) responses... is that what your end goal will look like?
If you are aiming for a more general purpose framework, then I would do more realistic testing... starting with just serving static content. I know you have your webrepl.html example, but I am thinking of a more typical multi-threaded browser request loading a page with style sheets, script files and images, etc...
Using PicoWeb, doing just do this, it does struggle with some pages (on an ESP8266). That said, it actually handles the multiple incoming request pretty well.
- pythoncoder
- Posts: 5956
- Joined: Fri Jul 18, 2014 8:01 am
- Location: UK
- Contact:
Re: ws: tiny asynchronous webserver framework without uasyncio
It's always possible to rewrite code based on cooperative multi-tasking in a way which doesn't use it. A library like uasyncio facilitates an event-driven programming paradigm but fundamentally the application is a state machine(SM) which interacts with hardware. Cooperative multi tasking provides a way of writing that single SM as a number of separate coroutines, each being its own simple SM. The same program logic may be achieved by explicitly coding a single SM; but for non trivial applications it can rapidly become revoltingly complicated and hard to maintain.
The uasyncio library, with all due respect to the programming skills of @pfalcon, is Python not thaumaturgy
The question is whether there is any gain in code readability or performance in avoiding uasyncio. I'd be astonished if a web application yielded performance gains but it would be interesting to see practical evidence based on a true like-for-like comparison.
Peter Hinch
Index to my micropython libraries.
Index to my micropython libraries.
Re: ws: tiny asynchronous webserver framework without uasyncio
I have done this and I am seeing similar results, 5-7X performance difference. I'll post some test results at a later date, but even in production static content can be compiled to a single compressed document it that's what it takes to offer a great user experience.
I agree that it would be nice to stick with uasyncio for maintainability reasons. I have used twisted, so I know how important that is. I have provided two examples showing a large performance difference and I'm confident that we'll see the same difference as the project scales. The problem here is the implementation of select, and as much as I would like to dig in and find out why we are getting such poor performance from select, it is not something I'm prepared for. select.poll needs to be in a tight loop of native code, otherwise the performance tanks and it waits around all day on the select statement even when the TCP buffer is full. Also, it is MUCH faster to buffer 536 bytes at a time before writing to the socket, otherwise subsequent writes will result in short writes until the buffer drains. These are platform specific performance quirks and might only apply to the ESP8266. But the performance difference is real.pythoncoder wrote: ↑Thu Nov 16, 2017 12:11 pmI'd be astonished if a web application yielded performance gains but it would be interesting to see practical evidence based on a true like-for-like comparison.
I do prefer to use the standard tools, but the users of my product won't care how standards compliant or maintainable the code is. Faster is better. My application requires both an HTTP server and an MQTT client. I'm going to try using the same select loop for both. I wish we could have frozen native code.
Re: ws: tiny asynchronous webserver framework without uasyncio
I'm going to admit that I am lost by the last two posts... I'm new to Python in general and not very strong in the http protocol itself. Jacob, can you generate a simple script using ws that would serve the contents of a path that I pass in? That way I could test it for my needs. Here is the PicoWeb app I currently use...
Hopefully it should be trivial (for you).
Thanks.
Code: Select all
#
# Basic PicoWeb application for serving up content in a sub-folder
#
import ure as re
import picoweb
# sub-folder we will serve conent from
rootdir = "wwwroot/"
# handle the root URL
def index(req, resp):
yield from app.sendfile(resp, rootdir + "index.html")
# handle all other resource (file) requests
def file(req, resp):
fname = req.url_match.group(1)
yield from app.sendfile(resp, rootdir + fname)
# define the picoweb routing table
ROUTES = [
("/", index),
(re.compile("^/(.+)"), file),
]
# setup the logging that is used by picoweb
import logging
logging.basicConfig(level=logging.DEBUG)
# run the server
app = picoweb.WebApp(None, ROUTES)
app.run(host="0.0.0.0", port=80, debug=True)
Thanks.
- pythoncoder
- Posts: 5956
- Joined: Fri Jul 18, 2014 8:01 am
- Location: UK
- Contact:
Slow performance. But is uasyncio the culprit?
@jacob19 My interest in uasyncio is primarily in hardware interfacing rather than web applications; I have benchmarked and tested it fairly extensively and it is highly efficient. On the Pyboard it can task switch in 288μs. On an ESP8266 it takes 2.2ms. I'm sure you'll agree that in the context of web applications that's fast. It's possible that the uasyncio IORead/IOWrite mechanism may be the culprit but from your description it sounds as if poll/select is the actual cause. So discarding uasyncio as a workround for a performance issue with poll/select sounds rather like throwing away the baby with the bathwater
If you could devise a minimal test case demonstrating the problem with select and raise an issue I'm sure the maintainers would address it, which would benefit us all. It might even be a quicker solution to completing your application than rewriting the webserver.
If you could devise a minimal test case demonstrating the problem with select and raise an issue I'm sure the maintainers would address it, which would benefit us all. It might even be a quicker solution to completing your application than rewriting the webserver.
Peter Hinch
Index to my micropython libraries.
Index to my micropython libraries.
Re: ws: tiny asynchronous webserver framework without uasyncio
Code: Select all
import ws
import os
rootdir = '/wwwroot'
ws.routefile('/',rootdir+'/index.html')
for file in os.listdir(rootdir):
ws.routefile('/'+file,rootdir+'/'+file)
ws.serve()
I'm certain that the culprit is select. If select.poll is called from within a tight loop of native code then it runs fast, and prebuffering writes into chunks before passing them to the socket reduces iteration to make it even faster. uasyncio cannot not take advantage of these optimizations. I can think of several workarounds, but writing a simple webserver is easier.pythoncoder wrote: ↑Fri Nov 17, 2017 10:35 amIt's possible that the uasyncio IORead/IOWrite mechanism may be the culprit but from your description it sounds as if poll/select is the actual cause.
I agree 100% that the best solution is to improve the performance of select. It looks like uselect support was only enabled on the ESP8266 in December 2016. https://github.com/micropython/micropython/pull/2411. Someone made a platform specific implementation, but it was abandoned when moduselect.c was generalized for use across all ports. I will do as you suggest.pythoncoder wrote: ↑Fri Nov 17, 2017 10:35 amIf you could devise a minimal test case demonstrating the problem with select and raise an issue I'm sure the maintainers would address it, which would benefit us all. It might even be a quicker solution to completing your application than rewriting the webserver.
Re: ws: tiny asynchronous webserver framework without uasyncio
Thanks for the test code. Unfortunately I could only get it to serve the root level files and none of the ones in sub-directories like the style sheet and the image. Even when I build the routes for those files...
Anyway, it looks like you are narrowing down the suspects on where the bottleneck is. So that's good.
Code: Select all
import ws
import os
rootdir = '/wwwroot'
ws.routefile('/',rootdir+'/index.html')
def addfiles(path):
for file in os.ilistdir(path):
if file[1] == 16384:
addfiles(path + '/' + file[0])
else:
rpath = path + '/' + file[0]
rootlen = len(rootdir) + 1
vpath = rpath[rootlen:]
ws.routefile(vpath, rpath)
addfiles(rootdir)
ws.serve()
Re: ws: tiny asynchronous webserver framework without uasyncio
@bitninja, The paths require '/' to be prepended. So adjust your code like this:
ws.routefile('/'+vpath, rpath)
ws.routefile('/'+vpath, rpath)
Re: ws: tiny asynchronous webserver framework without uasyncio
OK, that fixed it.
Well after my testing with Chrome and Edge (I'm on Windows) I still don't see the performance gains between the two, so I'm not sure what to think. I'm using the profiling tool in Chrome and the times are pretty consistent...
Here is the capture of ws loading the webrepl.html...
Well after my testing with Chrome and Edge (I'm on Windows) I still don't see the performance gains between the two, so I'm not sure what to think. I'm using the profiling tool in Chrome and the times are pretty consistent...
Here is the capture of ws loading the webrepl.html...
Re: ws: tiny asynchronous webserver framework without uasyncio
I appreciate the testing.
15 seconds is what I'm seeing with picoweb, or without native code.
I'm getting a consistent 2 second load time for the single file webrepl with ws. In both Chrome and Firefox on Linux.
Here's my screen capture of it loading in chrome in <2s:
http://jacobstoner.com/screenshot.jpg
I am using MicroPython v1.9.3-7 on the ESP8266 in STA mode. This is after a fresh boot and running these commands:
I tested loading a page with several resources totaling ~200kb, it only took a few seconds.
I would really like to figure out why you are not getting the same results:
1. Are you on the ESP8266?
2. Is the device connected to the network in STA mode?
15 seconds is what I'm seeing with picoweb, or without native code.
I'm getting a consistent 2 second load time for the single file webrepl with ws. In both Chrome and Firefox on Linux.
Here's my screen capture of it loading in chrome in <2s:
http://jacobstoner.com/screenshot.jpg
I am using MicroPython v1.9.3-7 on the ESP8266 in STA mode. This is after a fresh boot and running these commands:
Code: Select all
import ws
ws.routefile('/webrepl.html','/wwwroot/webrepl.html')
ws.serve()
I would really like to figure out why you are not getting the same results:
1. Are you on the ESP8266?
2. Is the device connected to the network in STA mode?