Kivy on Android, part 2

Hi guys,

Look like people are following my blog and waiting for android version of Kivy.
We have a launcher that you can already use. Check the :

Maybe during the next release, or a little bit after, i’ll release a software to create an Android package of a Kivy application. The code is already on launchpad, but it’s still a work in progress. As soon as i have finished, i’ll publish it on kivy-dev mailing list. If you didn’t subscribe yet, do it now ! :)

More to come by the end of that week… !

Kivy (next PyMT) on Android, step 1 done !

Tonight is a wonderful night.

I know that i didn’t announce Kivy officially yet, but i’ll do it in another blog post very soon. You just need to know that Kivy is the next PyMT version. From 2 years ago with thomas, we have regulary doubts and reflections about using Python for PyMT. And i’ve started to look more at the future, and i was deeply convince that for our sake, we must be able to run on a Webbrowser. The goal is simple: same code for every platform, at least what we use every day: Linux / Windows / Macosx / Android / iOS.

Android and iOS are new OS, and we was thinking that except running in webbrowser, we will be never able to run on it. And we have started to target a futur with fewer dependencies, OpenGL ES 2.0 compatible, and so on. This vision have been named Kivy. Theses last days, i’ve removed numpy and pyopengl dependencies. Pygame is the only library required for running an application with widgets. (minimal don’t mean full featured).

And i’ve started to look at the android platform, since Tom from Renpy library have deliver a pygame subset for android. He just made an awesome work. My part was just to understand how it work, and get Kivy compilation done.

For now, here is what i’ve got :

Ok, but what i got exactly ?

  • Python/Pygame running from renpytom project
  • Failed attempt to use numpy on android
  • Kivy adapation for android (opengl debug mode, removing numpy and pyopengl, link on opengl es 2.0…)
  • Pygame change to create OpenGL ES 2.0
  • Various patch on the build system

And here is my step 2 :

  • Send to upstream all the patch on the build system
  • Resolve symbol conflict when 2 compiled module have the same name (kivy.event and pygame.event… nice naming.)
  • Add a way of detecting Android platform from python
  • Add multitouch support to pygame and/or kivy
  • Add android sleep/wakeup in kivy
  • Write documentation about how to compile a kivy application on android

For now, sleep time ! Enjoy.

Receiving SMS in python, for cheap.

From few month now, i was using my android phone to transmit new SMS to my computer using Android Notifier. All the new SMS was sent over Wifi/UDP. Now i was searching a standalone and cheap alternative. Recently, Hack a Day have published an article: Cheap and easy SMS via GMS for your MCU. And they talk about something very interesting: a USB GSM modem for only $25.

To be able to use this modem, i need also a SIM card. You have lot of possibilities to explore, but the one i’ve take is a prepaid card on Bouygues, for only 9.90€. You have only 5 minutes for dialing.. but you don’t care. What you must care about is the availability of the phone number. On this case, the number is still available 6 month. 9.90€ for having a 6 month number, that’s totally enough for only receiving SMS.

The GSM modem is internally a BenQ M23 (Specifications). You can use a simple serial command line to talk with :

$ cu -l /dev/ttyUSB0
Connected.
AT
OK
AT+CGMI
BenQ

OK
AT+CGMM
M32

OK
AT+CGMR
SW ver: 1.80
HW ver: 1.00
FS ver: 1.00
Build Date: 2004/6/25
Build Time: 18:40:37

OK

After exploring some documentation and specifications, what i found out:

  • Messaging system can be read in 2 form, text and PDU
  • Text mode is not ok for our French encoding. All specials characters are stripped.

Exemple of a session in text mode (AT+CMGF=1) :

AT+CMGF=1
OK
AT+CMGL="ALL"
+CMGL: 1,"REC UNREAD","336XXXXXXXXX",,"11/01/07,12:15:53+04",145,21
Voici les rsultats. 

OK

The correct word must be “résultats”, not “rsultats”. When switching to PDU mode (AT+CMGF=0), we obtain :

AT+CMGF=0
OK
AT+CMGL=4
+CMGL: 1,1,,38
07913306093076F0040B913386654329F100001110702151354015D6777A9C06B1CB7390BC30AFB3E961FADC0502

OK

The big hexadecimal string is the PDU. For decoding this string, we can use the Python Messaging library, very simple to use :

from messaging.sms import SmsDeliver
print SmsDeliver(
    '07913306093076F0040B913386654329F10000111070'
    '2151354015D6777A9C06B1CB7390BC30AFB3E961FADC0502').text
# output => u'Voici les r\xe9sultats. '

Here we are !

Next blog post: how to read MMS from Bouygues Telecom :)

Python: is X is better than Y ? Round 3, pure python VS cython

Round 3: pure python drawing VS cython

Benchmark code :

s = time.time()
for x in xrange(10000):
   drawRectangle(0, 0, 50, 50)
print 'result=', time.time() - s

Let’s take the Python version of simplified drawRectangle():

def drawRectangle(x, y, w, h):
   glBegin(GL_QUADS)
   glVertex2f(x, y)
   glVertex2f(x + w, y)
   glVertex2f(x + w, y + h)
   glVertex2f(x, y + h)
   glEnd()

I got the result on poor graphics card: average ~1.1268s

Let’s rewrite in Cython:

cdef extern from "GL/gl.h":
   ctypedef float         GLfloat
   ctypedef unsigned int  GLenum
   int GL_QUADS
   cdef void glBegin(GLenum mode)
   cdef void glEnd()
   cdef void glVertex2f(GLfloat x, GLfloat y)
def drawRectangle(float x, float y, float w, float h):
   glBegin(GL_QUADS)
   glVertex2f(x, y)
   glVertex2f(x + w, y)
   glVertex2f(x + w, y + h)
   glVertex2f(x, y + h)
   glEnd()

And the result : average ~0.0325s

PyMT Impact: rewriting graphx package :)

EDIT:
On a NVIDIA 9800 GT: with 1000000 (instead of 10000):

  • Python: 16.1
  • Python -O: 17.2
  • Python -OO: 16.9
  • Cython: 0.29

Very weird about -O / -OO…

Python: is X is better than Y ? Round 2, remove exception VS test in + remove

Round 2: catching remove exception VS test in + remove

Benchmark code :

from time import time
 
# exceptions vs list in
def bench_in_remove(count):
    for x in xrange(count):
        q = []
        for z in xrange(1000):
            q.append(z)
        for z in xrange(0, 500):
            if z in q:
                q.remove(z)
 
def bench_exception(count):
    for x in xrange(count):
        q = []
        for z in xrange(1000):
            q.append(z)
        for z in xrange(0, 500):
            try:
                q.remove(z)
            except ValueError:
                continue
 
def bench_remove(count):
    for x in xrange(count):
        q = []
        for z in xrange(1000):
            q.append(z)
        for z in xrange(0, 500):
            q.remove(z)
 
def run(f, c=10000):
    t = time()
    f(c)
    print '%-15s time=%.8f count=%d' % (f.func_name, time() - t, c)
 
run(bench_remove)
run(bench_in_remove)
run(bench_exception)

And the result :

bench_remove    time=4.20256305 count=10000
bench_in_remove time=4.49746203 count=10000
bench_exception time=4.33078504 count=10000

We can see a little improvement with testing exception instead of testing in.
We can also see the overhead due to the test before removing in list.

I’ve also tested with xrange(-500, 500) instead of (0, 500), to trigger invalid removal. Here is the result :

bench_in_remove time=12.42448401 count=1000
bench_exception time=14.26106405 count=1000

Triggering an exception cost much time than testing if value is in a list…

PyMT Impact: must check.

Python: is X is better than Y ? Round 1, deque vs list.

This week-end, i’ve spend some time about searching how to optimize PyMT code. And done some interesting benchmark. Next days, i’ll post some of them, in order to remember which is the better solution.

For the first round: let’s test Deque from collections package VS Python List !

Code:

from time import time
 
# collections VS list
def bench_deque(count):
    from collections import deque
    q = deque()
    for x in xrange(count):
        for z in xrange(1000):
            q.append(z)
        while True:
            try:
                q.pop()
            except:
                break
 
def bench_list(count):
    q = []
    for x in xrange(count):
        for z in xrange(1000):
            q.append(z)
        for y in xrange(len(q)):
            q.pop(0)
 
def run(f, c=100000):
    t = time()
    f(c)
    print '%-15s time=%.8f count=%d' % (f.func_name, time() - t, c)
 
run(bench_deque)
run(bench_list)

And the result :

bench_deque     time=35.61677003 count=100000
bench_list      time=78.20481586 count=100000

Impact for PyMT: wm_pen, wm_touch.

Python and HTTP Pipelining

I wanted to do HTTP Pipelining using urllib2. But, first of all, what is pipelining ?
HTTP pipelining is a technique in which multiple HTTP requests are written out to a single socket without waiting for the corresponding responses.

What is the benefit of pipelining ? Less network load, speedup processing !
I was searching a way to do it with urllib2… But solution are complicated, and not fit well to my needs.
But way, why stay on urllib2 ? Use httplib !

Reusing the connection :

First, reusing the same connection

import httplib
server = httplib.HTTPConnection('yourserver.com')
server.request('GET', '/index.html')
print 'RESPONSE1:', server.getresponse().read()
 
server.request('GET', '/index2.html')
print 'RESPONSE2:', server.getresponse().read()
 
server.request('GET', '/index3.html')
print 'RESPONSE3:', server.getresponse().read()

Second, try pipelining !

import httplib
server = httplib.HTTPConnection('yourserver.com')
server.request('GET', '/index.html')
res1 = server.getresponse()
server.request('GET', '/index2.html')
res2 = server.getresponse()
server.request('GET', '/index3.html')
res3 = server.getresponse()

print 'RESPONSE1:', res1.read()
print 'RESPONSE2:', res2.read()
print 'RESPONSE3:', res3.read()

Color swapping and Python

For PyMT, Sharath need BGR support into PyMT, while his graphic card don’t support GL_BGR.
Well, after adapting swap code from Pyglet sourcecode, he say: “< patali> it works but very slow”

The goal is to swap a string from ‘bgrbgrbgr’ to ‘rgbrgbrgb’. Just let’s do a rapid benchmark.

import sys
import re
import time
 
swap1_pattern = re.compile('(.)(.)(.)', re.DOTALL)
swap1_repl = r'\3\2\1'
def swap1(bytes):
    return swap1_pattern.sub(swap1_repl, bytes)
 
def swap2(bytes):
    out = ''
    for i in xrange(0, len(bytes), 3):
        b, g, r = bytes[i:i+3]
        out += r
        out += g
        out += b
    return out
 
def swap3(bytes):
    bytes = list(bytes)
    for i in xrange(0, len(bytes), 3):
        b, g, r = bytes[i:i+3]
        bytes[i:i+3] = r, g, b
    return ''.join(bytes)
 
def swap4(bytes):
    blues = bytes[0::3]
    greens = bytes[1::3]
    reds = bytes[2::3]
    return ''.join(''.join(x) for x in zip(reds, greens, blues))
 
def swap5(bytes):
    from array import array
    a = array('b', bytes)
    a[0::3], a[2::3] = a[2::3], a[0::3]
    return a.tostring()
 
def swap6(bytes):
    import numpy
    a = numpy.array(bytes, 'c')
    b = a[...,::-1]
    return b.tostring()
 
def swap7(bytes):
    a = list(bytes)
    a[0::3], a[2::3] = a[2::3], a[0::3]
    return ''.join(a)
 
 
def bench(func, bytes):
    sys.stderr.write('Bench %s: ' % str(func))
 
    start = time.time()
    for i in xrange(20):
        sys.stderr.write('.')
        ret = func(bytes)
        if ret[:3] != 'rgb' or ret[-3:] != 'rgb':
            sys.stderr.write('INVALID DATA, start=%s, end=%s' %
                            (ret[:3], ret[-3:]))
            break
 
    end = time.time() - start
    sys.stderr.write('| Finished in %.4f
' % end)
    return ret
 
 
bytes = 'bgr' * 256 * 256
bench(swap1, bytes)
bench(swap2, bytes)
bench(swap3, bytes)
bench(swap4, bytes)
bench(swap5, bytes)
bench(swap6, bytes)
bench(swap7, bytes)

Swap1() is actually the way of pyglet. swap2/swap3 is from me, and others is found on some threads in pygame mailing list.

So, who win ?

21:09 tito@ashaka ~ $ python benchmark.py
Bench <function swap1 at 0x7f1d154632a8>: ....................| Finished in 3.2713
Bench <function swap2 at 0x7f1d15463230>: ....................| Finished in 1.2860
Bench <function swap3 at 0x7f1d15470cf8>: ....................| Finished in 0.6535
Bench <function swap4 at 0x7f1d15470e60>: ....................| Finished in 0.6475
Bench <function swap5 at 0x7f1d15470ed8>: ....................| Finished in 0.0367
Bench <function swap6 at 0x7f1d15470f50>: ....................| Finished in 0.1959
Bench <function swap7 at 0x7f1d15473050>: ....................| Finished in 0.1482

The array solution is a lot faster than any other implementation ! And it’s python standard :) Let’s take this…

Edit: thanks for Milan to come with another awesome solution !

def swap0(bytes):
  return bytes[::-1]

Result is: 0.0059 !