Skip to content

uasyncio on esp8266 #141

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 4, 2017
Merged

Conversation

mzdaniel
Copy link
Contributor

@mzdaniel mzdaniel commented Dec 26, 2016

Thank you so much Damien, Paul and the MicroPython community for this amazing project!

Since I read about this project and found a few weeks back that python3.5 can be run in the esp8266 (with less than 64kb of memory), I was immediatelly hook. Once esp8266 gained uselect it seemed that with a little more effort uasyncio was in reach and made a POC (https://github.com/mzdaniel/micropython-lib/tree/uasyncio-esp8266_poc). Here is the revised PR after utimeq landed on the esp. Tested with the latest MicroPython v1.8.6-247-g05aebb9-dirty on 2016-12-26 on an Olimex board.

@dpgeorge
Copy link
Member

Thanks for the contribution. It's good to see that the only modifications to uasyncio is to do with the fd number. But I'm not sure if this particular PR will be merged because @pfalcon is working on it and possibly has an alternative solution.

Anyway, we can keep this PR open until uasyncio is supported on esp8266.

@pfalcon
Copy link
Contributor

pfalcon commented Dec 29, 2016

Yes, this is definitely step in the direction it should be done, but it should be done in right way, e.g. final implementation won't have any sock_fd() function. But before going to networking support on esp8266 (which will require many more changes than this patch), simpler things are being worked on. For example, the first stage is making sure uasyncio.core trivial scheduling can work w/o memory allocation (done), next stage is making the same work with uasyncio (simple as it seems, but subclassing breaks no-alloc behavior; in progress). Next stage isn't even related to uasyncio, but due to do deficiencies in HW API, all current samples for the previously described functionality are currently broken (don't work as expected from a board to board), so that needs to be fixed. Then can patch unix port uselect module to accept entire objects, and the more complex task of adding efficient buffering for partial reads so uasyncio networking can actually work as expected.

@mzdaniel , you're welcome to test changes to uasyncio* being landed. Btw, I'd be curious how performance was affected when you ran tests (https://github.com/micropython/micropython-lib/tree/master/uasyncio/benchmark) against this patch?

@mzdaniel
Copy link
Contributor Author

Great to meet you, Damien and Paul.

@dpgeorge: Sure. As a python instructor, uasyncio enables async/await semantics which is wonderful.

@pfalcon:

Yes, this is definitely step in the direction it should be done, but it should be done in right way, e.g. final implementation won't have any sock_fd() function.

Certainly. It is done this way as stopgap to facilitate testing in esp8266.

...
you're welcome to test changes to uasyncio* being landed. Btw, I'd be curious how performance was affected when you ran tests (https://github.com/micropython/micropython-lib/tree/master/uasyncio/benchmark) against this patch?

Not sure how reliable is this data, as was taken from an xubuntu in GUI mode, each one freshly rebooted. Tried quick single user mode, and to my surprise python event loop doesn't seem to be available? (happen in both micropython and asyncio)

I've also found unittest is in the lib and preparing a PR there with updates to help us testing.

test-boom-heavy.sh
==================

Server Software: Unknown
Running GET http://127.0.0.1:8081
    Host: localhost:8081
Running 1000 queries - concurrency 30
ECONNRESET
[================================================================>.] 99% Done
-------- Status codes --------
Code 200                1000 times.

RPS: Request Per Second
BSI: Boom Speed Index


with 24125191ce0  (without PR)
----------------

Successful calls        1000
Total time              112.0376 s  
Average                 3.0639 s  
Fastest                 0.1675 s  
Slowest                 5.8360 s  
Amplitude               5.6686 s  
Standard deviation      0.808355
RPS                     8
BSI                     :(


With 28acc93ef93  (with sock_fd function)
----------------

Successful calls        1000
Total time              109.3224 s  
Average                 2.9810 s  
Fastest                 0.1657 s  
Slowest                 7.1593 s  
Amplitude               6.9936 s  
Standard deviation      0.856418
RPS                     9
BSI                     :(



test-ab-light.sh
================

Server Software:        
Server Hostname:        localhost
Server Port:            8081

Document Path:          /
Document Length:        12000 bytes

Concurrency Level:      100


with 24125191ce0  (without PR)
----------------

Time taken for tests:   0.541 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      120190000 bytes
HTML transferred:       120000000 bytes
Requests per second:    18468.11 [#/sec] (mean)
Time per request:       5.415 [ms] (mean)
Time per request:       0.054 [ms] (mean, across all concurrent requests)
Transfer rate:          216765.80 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.2      0       2
Processing:     1    5   0.4      5       7
Waiting:        1    5   0.5      5       7
Total:          2    5   0.4      5       7

Percentage of the requests served within a certain time (ms)
  50%      5  66%      5  75%      6  80%      6  90%      6
  95%      6  98%      6  99%      7 100%      7 (longest request)


With 28acc93ef93  (with sock_fd function)
----------------

Time taken for tests:   0.583 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      120190000 bytes
HTML transferred:       120000000 bytes
Requests per second:    17151.72 [#/sec] (mean)
Time per request:       5.830 [ms] (mean)
Time per request:       0.058 [ms] (mean, across all concurrent requests)
Transfer rate:          201314.93 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.1      0       1
Processing:     1    6   0.4      6       7
Waiting:        0    6   0.4      6       7
Total:          2    6   0.3      6       7

Percentage of the requests served within a certain time (ms)
  50%      6  66%      6  75%      6  80%      6  90%      6
  95%      6  98%      6  99%      6 100%      7 (longest request)

@mzdaniel
Copy link
Contributor Author

mzdaniel commented Dec 31, 2016

PR updated now that micropython/micropython#1550 (comment) and specifically micropython/micropython@093a8f5 has landed. It includes a simple integration unittest to help us with testing. The test runs under linux, though not yet on esp8266.

micropython test_echo.py 
test_client_server ... ok
----------------------------------------------------------------------
Ran 1 tests in 0.010s

OK (failures=0, errors=0, skipped=0)

Here is the latest run of boom-heavy using ea26971 and
MicroPython v1.8.6-272-g093a8f5-dirty on 2016-12-31; linux version

admin@xubuntu:/data/micropython/micropython-lib/uasyncio/benchmark$ ./test-boom-heavy.sh 
Server Software: Unknown
Running GET http://127.0.0.1:8081
    Host: localhost:8081
Running 1000 queries - concurrency 30

-------- Results --------
Successful calls        1000
Total time              109.3656 s  
Average                 2.9684 s  
Fastest                 0.2987 s  
Slowest                 5.7786 s  
Amplitude               5.4798 s  
Standard deviation      0.746515
RPS                     9
BSI                     :(

Tests on esp8266 with PR

esp8266 is not super stable but it works. ab barely completed 50 single
queries with esp8266 using test_http_server_light.py (as main.py) through
wifi (no serial link attached). Most times, ab aborts after ~15-25 packets.
It also was able to complete 8 queries with 2 simultaneous connections.

Interestingly, if instead test_http_server_light.py is run as a paste to the
terminal, its way more stable pushing 250 single queries. Also, found the
following messages being repeated on the esp8266 when the connection get lost
sometimes.

E:M 48
no buf for probe, ie len 0
E:M 48
no buf for probe, ie len 0
scandone
state: 0 -> 2 (b0)            
state: 2 -> 0 (2)
reconnect      
E:M 48         
no buf for probe, ie len 0
E:M 48
no buf for probe, ie len 0
E:M 48

Standalone

admin@xubuntu:/data/micropython/micropython-lib/uasyncio/benchmark$ ab -n50 -c1 http://192.168.43.163:8080/
This is ApacheBench, Version 2.3 <$Revision: 1706008 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 192.168.43.163 (be patient).....done


Server Software:        
Server Hostname:        192.168.43.163
Server Port:            8080

Document Path:          /
Document Length:        8 bytes

Concurrency Level:      1
Time taken for tests:   8.621 seconds
Complete requests:      50
Failed requests:        0
Total transferred:      1350 bytes
HTML transferred:       400 bytes
Requests per second:    5.80 [#/sec] (mean)
Time per request:       172.427 [ms] (mean)
Time per request:       172.427 [ms] (mean, across all concurrent requests)
Transfer rate:          0.15 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        6   46 138.3     11     954
Processing:    46  127  49.6    114     272
Waiting:       45  127  49.6    114     272
Total:         56  172 156.5    126    1153

Percentage of the requests served within a certain time (ms)
  50%    126  66%    145  75%    158  80%    204  90%    262
  95%    283  98%   1153  99%   1153 100%   1153 (longest request)



$ ab -n8 -c2 http://192.168.43.163:8080/

Server Software:        
Server Hostname:        192.168.43.163
Server Port:            8080

Document Path:          /
Document Length:        8 bytes

Concurrency Level:      2
Time taken for tests:   1.051 seconds
Complete requests:      8
Failed requests:        0
Total transferred:      216 bytes
HTML transferred:       64 bytes
Requests per second:    7.61 [#/sec] (mean)
Time per request:       262.685 [ms] (mean)
Time per request:       131.343 [ms] (mean, across all concurrent requests)
Transfer rate:          0.20 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:       12   57  69.1     31     213
Processing:   106  180  60.5    204     254
Waiting:      106  180  60.5    204     254
Total:        126  237  93.5    227     424

Percentage of the requests served within a certain time (ms)
  50%    227  66%    263  75%    284  80%    284  90%    424
  95%    424  98%    424  99%    424 100%    424 (longest request)

With terminal

admin@xubuntu:/data/micropython/micropython-lib/uasyncio/benchmark$ ab -n250 -c1 http://192.168.43.163:8080/
This is ApacheBench, Version 2.3 <$Revision: 1706008 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 192.168.43.163 (be patient)
Completed 100 requests
Completed 200 requests
Finished 250 requests


Server Software:        
Server Hostname:        192.168.43.163
Server Port:            8080

Document Path:          /
Document Length:        8 bytes

Concurrency Level:      1
Time taken for tests:   47.745 seconds
Complete requests:      250
Failed requests:        0
Total transferred:      6750 bytes
HTML transferred:       2000 bytes
Requests per second:    5.24 [#/sec] (mean)
Time per request:       190.979 [ms] (mean)
Time per request:       190.979 [ms] (mean, across all concurrent requests)
Transfer rate:          0.14 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        6   43  82.1     20    1126
Processing:    44  148  81.8    129     772
Waiting:       44  148  81.8    129     772
Total:         58  191 113.8    166    1264

Percentage of the requests served within a certain time (ms)
  50%    166  66%    198  75%    218  80%    244  90%    307
  95%    360  98%    471  99%    557 100%   1264 (longest request)

@@ -63,9 +64,9 @@ def wait(self, delay):
# Remove "if res" workaround after
# https://github.com/micropython/micropython/issues/2716 fixed.
if res:
for fd, ev in res:
cb = self.objmap[fd]
if DEBUG and __debug__:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When fixing conflict, you dropped upstream change.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for catching it! Didn't see it in the diffs somehow.

@pfalcon
Copy link
Contributor

pfalcon commented Jan 1, 2017

Happy New Year!

@mzdaniel : Thanks for running the tests and further updates. While this would an interim solution, I guess it's good idea to merge this patch after few more minor changes. Besides inline comments, please squash together "Add minimum esp8266 support" and "Improve socket handling using changes from unix/moduselect" (but leave the test a separate commit). Regarding the test, thanks for adding it, there's definitely a need for more tests (I have few pseudo-tests locally, not suitable for committing.) One issue though is that micropython-lib follows pattern where tests are located within a module's dir, not in tests/ subdir. So, unless you have a good argument for what would be an inconsistency, please move it up. Thanks!

@pfalcon
Copy link
Contributor

pfalcon commented Jan 1, 2017

I may need more time to respond to your test results in detail, but as a quick hint, benchmark (load) tests are intended to be run on unix, esp8266 simply doesn't have enough memory to sustain so many connections (there's a chance to address that (somewhat) by violating TCP/IP protocol, and that's definitely on TODO, but of course not a priority ;-) ).

@mzdaniel
Copy link
Contributor Author

mzdaniel commented Jan 2, 2017

Happy New Year too, Paul!

@mzdaniel : Thanks for running the tests and further updates. While this would an interim solution, I guess it's good idea to merge this patch after few more minor changes. Besides inline comments, please squash together "Add minimum esp8266 support" and "Improve socket handling using changes from unix/moduselect" (but leave the test a separate commit).

My pleasure. It's done.

Regarding the test, thanks for adding it, there's definitely a need for more tests (I have few pseudo-tests locally, not suitable for committing.) One issue though is that micropython-lib follows pattern where tests are located within a module's dir, not in tests/ subdir. So, unless you have a good argument for what would be an inconsistency, please move it up. Thanks!

IMHO, aside of what seems a convention (to put tests in its own directory) in other important python projects that feels cleaner to me (in the sense of visual overload in the root dir of the module) and more convenient for test automation, I found the following:

admin@xubuntu:/data/micropython/micropython-lib/uasyncio$ ls
benchmark     README.test  test_call_soon.py  test_http_client.py  tests
metadata.txt  setup.py     test_echo.py       test_http_server.py  uasyncio
admin@xubuntu:/data/micropython/micropython-lib/uasyncio$ micropython test_echo.py 
Traceback (most recent call last):
  File "test_echo.py", line 1, in <module>
  File "/data/micropython/micropython-lib/uasyncio/uasyncio/__init__.py", line 4, in <module>
ImportError: no module named 'uasyncio.core'

instead if it is in tests:

admin@xubuntu:/data/micropython/micropython-lib/uasyncio/tests$ micropython test_echo.py 
test_client_server ... ok
Ran 1 tests

I introduced test_echo.py here as I felt is important and if agreed, decide details and what to do. Of course, local conventions and your preference have priority, so it is your call.

@pfalcon
Copy link
Contributor

pfalcon commented Jan 4, 2017

ImportError: no module named 'uasyncio.core'

Right, indeed, in my local copy I have a symlink to make that work.

instead if it is in tests:

Yes, but only if you installed modules first. If you made changes in micropython-lib, but didn't install them, you may be testing old versions. So, this solution has drawbacks too.

Anyway, as there're some "tests" (which aren't really tests, but that's another issue) in base dir already (https://github.com/micropython/micropython-lib/tree/master/uasyncio), and you've made this change in the patch, let it be in the base dir for now.

And I perfectly know about tests/ convention, and that works well for more or less big, standalone projects. But micropython-lib is different, it's myriad of small modules, and usually it's just a module .py. Then having test_*.py side by side with it allows to test always-fresh module without bothering with python path. Of course, it doesn't work as convinient and as clean with packages consisting of multiple submodules, like uasyncio or umqtt.

@pfalcon pfalcon merged commit 4bc5ab9 into micropython:master Jan 4, 2017
@pfalcon
Copy link
Contributor

pfalcon commented Jan 4, 2017

Merged, thanks! (I don't retest thoroughly now, assuming you did that.)

@fadushin
Copy link

fadushin commented Jan 4, 2017

Thank you all!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants