This is by no means an academic benchmark, but I was doing some testing with 
RPC methods and decided to come up with a benchmark to get some average 
times for the time it takes to make several types of RPC calls, and if one 
protocol has a performance advantage over another. I tested JSON-RPC and 
XML-RPC. I also tested using sessions, and using SSL to see how those things 
affect performance. I will show all the code used, along with a description 
of the testing environment later, but right now, here are the results:

#######################################
#####        HTTP with Sessions
#######################################


ADD TWO NUMBERS
==================================
JSON-RPC: 47.74 ms
XML-RPC: 46.54 ms
Result: XML won by: 1.20 ms


CONCATENATE TWO STRINGS
==================================
JSON-RPC: 45.09 ms
XML-RPC: 44.66 ms
Result: XML won by: 0.43 ms


HANDLE BOOLEAN VALUE
==================================
JSON-RPC: 45.35 ms
XML-RPC: 45.43 ms
Result: JSON won by: 0.08 ms


HANDLE DATETIME VALUE
==================================
JSON-RPC: 46.13 ms
XML-RPC: 45.52 ms
Result: XML won by: 0.61 ms


HANDLE COMPLEX OBJECTS
==================================
JSON-RPC: 51.63 ms
XML-RPC: 51.19 ms
Result: XML won by: 0.44 ms


FINAL RESULTS
==================================
JSON-RPC Average Time: 47.19 ms
JSON-RPC Total Time Taken: 8731.09 ms
XML-RPC Average Time: 46.67 ms
XML-RPC Total Time Taken: 8682.93 ms
Result: XML won by: 0.52 ms


#######################################
#####        HTTP without Sessions
#######################################

This was done by placing "session.forget(response)" in the call() method.


ADD TWO NUMBERS
==================================
JSON-RPC: 49.10 ms
XML-RPC: 50.45 ms
Result: JSON won by: 1.35 ms


CONCATENATE TWO STRINGS
==================================
JSON-RPC: 45.81 ms
XML-RPC: 49.13 ms
Result: JSON won by: 3.32 ms


HANDLE BOOLEAN VALUE
==================================
JSON-RPC: 45.50 ms
XML-RPC: 49.44 ms
Result: JSON won by: 3.94 ms


HANDLE DATETIME VALUE
==================================
JSON-RPC: 45.97 ms
XML-RPC: 49.08 ms
Result: JSON won by: 3.11 ms


HANDLE COMPLEX OBJECTS
==================================
JSON-RPC: 51.25 ms
XML-RPC: 54.90 ms
Result: JSON won by: 3.65 ms


FINAL RESULTS
==================================
JSON-RPC Average Time: 47.53 ms
JSON-RPC Total Time Taken: 8767.23 ms
XML-RPC Average Time: 50.60 ms
XML-RPC Total Time Taken: 9076.67 ms
Result: JSON won by: 3.07 ms


As you can see, there wasn't much of a change. In fact, turning off sessions 
added about 400 ms to the testing time. Now let's try with SSL:


#######################################
#####           SSL with Sessions
#######################################

I ran this test several times because of the HUGE difference between the 
protocols when used over SSL. I think this may be more of a problem with the 
JSON client or server rather than the protocol itself. You'll see what I 
mean:

ADD TWO NUMBERS
==================================
JSON-RPC: 335.97 ms
XML-RPC: 60.19 ms
Result: XML won by: 275.78 ms


CONCATENATE TWO STRINGS
==================================
JSON-RPC: 335.83 ms
XML-RPC: 43.74 ms
Result: XML won by: 292.09 ms


HANDLE BOOLEAN VALUE
==================================
JSON-RPC: 336.12 ms
XML-RPC: 43.93 ms
Result: XML won by: 292.20 ms


HANDLE DATETIME VALUE
==================================
JSON-RPC: 334.30 ms
XML-RPC: 45.14 ms
Result: XML won by: 289.17 ms


HANDLE COMPLEX OBJECTS
==================================
JSON-RPC: 337.24 ms
XML-RPC: 50.18 ms
Result: XML won by: 287.06 ms


FINAL RESULTS
==================================
JSON-RPC Average Time: 335.90 ms
JSON-RPC Total Time Taken: 37605.90 ms
XML-RPC Average Time: 48.64 ms
XML-RPC Total Time Taken: 8878.73 ms
Result: XML won by: 287.26 ms


#######################################
#####           SSL without Sessions
#######################################

Again using "session.forget(response)" here. And again, using this line 
seems to slow things down a bit:


ADD TWO NUMBERS
==================================
JSON-RPC: 341.59 ms
XML-RPC: 61.99 ms
Result: XML won by: 279.60 ms


CONCATENATE TWO STRINGS
==================================
JSON-RPC: 338.62 ms
XML-RPC: 45.59 ms
Result: XML won by: 293.03 ms


HANDLE BOOLEAN VALUE
==================================
JSON-RPC: 333.35 ms
XML-RPC: 46.06 ms
Result: XML won by: 287.28 ms


HANDLE DATETIME VALUE
==================================
JSON-RPC: 332.38 ms
XML-RPC: 45.22 ms
Result: XML won by: 287.16 ms


HANDLE COMPLEX OBJECTS
==================================
JSON-RPC: 336.60 ms
XML-RPC: 51.47 ms
Result: XML won by: 285.13 ms


FINAL RESULTS
==================================
JSON-RPC Average Time: 336.51 ms
JSON-RPC Total Time Taken: 37665.40 ms
XML-RPC Average Time: 50.07 ms
XML-RPC Total Time Taken: 9023.97 ms
Result: XML won by: 286.44 ms




As you can see, JSON-RPC over SSL is a real problem. In fact, while I was 
playing around with it, I noticed the 300 ms delay and this was the reason I 
decided to do the performance testing in the first place. Now, I have web2py 
running from a Ubuntu 10.04 virtual machine (using bridged networking) and 
running the tests from the host machine (Mac OS X 10.7.1) over the network. 
As for the code used to perform this test, I have attached the controller 
and the test script, since they are too large to paste.

I just thought this might be of interest as not only a general performance 
test of web2py, but also an RPC performance test, which hasn't gotten as 
much attention as web testing.
# -*- coding: utf-8 -*-
# this file is released under public domain and you can use without limitations
from gluon import *
import datetime

#########################################################################
## This is a samples controller
## - index is the default action of any application
## - user is required for authentication and authorization
## - download is for downloading files uploaded in the db (does streaming)
## - call exposes all registered services (none by default)
#########################################################################

def index():
    """
    example action using the internationalization operator T and flash
    rendered by views/default/index.html or views/generic.html
    """
    return dict(message=T('Hello World'))
    
@service.xmlrpc
@service.jsonrpc
def add(num1, num2):
    """
    Adds two numbers together.
    
    @param num1:        First number
    @param num2:        Second number
    
    @return: The sum of the two numbers.
        
    >>> add(2, 2)
    4
    
    >>> add(2.5, 2.5)
    5.0
    """
    session.forget(response)
    return num1 + num2

@service.xmlrpc
@service.jsonrpc
def concat(str1, str2, str3=''):
    """
    Concatenates two or three strings.
    
    @param str1:        First string
    @param str2:        Second string
    @param str3:        (Optional) Third string
    
    @return: The concatenated string.

    >>> concat('hello', ' world')
    'hello world'
    
    >>> concat('hello', ' world', ' !!!')
    'hello world !!!'
    """
    return str1 + str2 + str3

@service.xmlrpc
@service.jsonrpc
def complex(obj):
    """
    Accepts a simple or complex object and returns a complex object. This
    tests the performance of a larger payload.
    
    @param obj:         Any object
    
    @return: A complex object (list of dictionaries)
    
    """
    return [
        {'id': 1, 'name': 'Peter Griffin', 'weight': 350, 'birthdate': datetime.datetime(1965, 3, 21), 'children': [
            {'id': 3, 'name': 'Chris Griffin', 'weight': 275, 'birthdate': datetime.datetime(1987, 2, 3), 'children': []},
            {'id': 4, 'name': 'Meg Griffin', 'weight': 800, 'birthdate': datetime.datetime(1985, 6, 9), 'children': []},
            {'id': 5, 'name': 'Stewie Griffin', 'weight': 30, 'birthdate': datetime.datetime(2010, 6, 5), 'children': []},
        ]},
        {'id': 2, 'name': 'Louis Griffin', 'weight': 350, 'birthdate': datetime.datetime(1964, 9, 8), 'children': [
            {'id': 3, 'name': 'Chris Griffin', 'weight': 275, 'birthdate': datetime.datetime(1987, 2, 3), 'children': []},
            {'id': 4, 'name': 'Meg Griffin', 'weight': 800, 'birthdate': datetime.datetime(1985, 6, 9), 'children': []},
            {'id': 5, 'name': 'Stewie Griffin', 'weight': 30, 'birthdate': datetime.datetime(2010, 6, 5), 'children': []},
        ]},
        {'id': 6, 'name': 'Brian Griffin', 'weight': 40, 'birthdate': datetime.datetime(2002, 11, 14), 'children': []},
        {'id': 7, 'name': 'Quagmire', 'weight': 40, 'birthdate': datetime.datetime(1971, 12, 4), 'children': []}
    ]

@service.xmlrpc
@service.jsonrpc
def fail():
    """
    This method is designed to fail and raise an exception.
    
    @raise: Will raise an exception ZeroDivisionError.
    @return: Will always raise an exception instead of returning.
    """
    
    return 1/0

@service.xmlrpc
@service.jsonrpc
def boolean():
    """
    This method always return True in order to test the bool data type.
    
    @return: always True
    """
    
    return True

@service.xmlrpc
@service.jsonrpc
def now():
    """
    Returns the server's current date and time.
    
    @return: datetime
    """
    return datetime.datetime.now()
    
def call():
    """
    exposes services. for example:
    http://..../[app]/default/call/jsonrpc
    decorate with @services.jsonrpc the functions to expose
    supports xml, json, xmlrpc, jsonrpc, amfrpc, rss, csv
    """
    session.forget(response)
    return service()
import time, datetime
from gluon.contrib.simplejsonrpc import ServerProxy as JS
from xmlrpclib import ServerProxy as XM
j = JS('https://172.16.100.111:8000/rpc_test/default/call/jsonrpc')
x = XM('https://172.16.100.111:8000/rpc_test/default/call/xmlrpc')

def test():
    def format_time(title, t):
        return '%s: %0.2f ms' % (title, t * 1000.0)
    
    def rpc(client):
        start_total = time.time()
        add_times = []
        concat_times = []
        bool_times = []
        date_times = []
        complex_times = []
            
        for i in xrange(20):
            start = time.time()
            client.add(i, i)
            stop = time.time()
            add_times.append(stop - start)
            
            start = time.time()
            client.concat('hello ', 'world')
            stop = time.time()
            concat_times.append(stop - start)
            
            start = time.time()
            client.boolean()
            stop = time.time()
            bool_times.append(stop - start)
            
            start = time.time()
            client.now()
            stop = time.time()
            date_times.append(stop - start)
            
            start = time.time()
            client.complex([
                {'id': 1, 'name': 'Peter Griffin', 'weight': 350, 'birthdate': str(datetime.datetime(1965, 3, 21)), 'children': [
                    {'id': 3, 'name': 'Chris Griffin', 'weight': 275, 'birthdate': str(datetime.datetime(1987, 2, 3)), 'children': []},
                    {'id': 4, 'name': 'Meg Griffin', 'weight': 800, 'birthdate': str(datetime.datetime(1985, 6, 9)), 'children': []},
                    {'id': 5, 'name': 'Stewie Griffin', 'weight': 30, 'birthdate': str(datetime.datetime(2010, 6, 5)), 'children': []},
                ]},
                {'id': 2, 'name': 'Louis Griffin', 'weight': 350, 'birthdate': str(datetime.datetime(1964, 9, 8)), 'children': [
                    {'id': 3, 'name': 'Chris Griffin', 'weight': 275, 'birthdate': str(datetime.datetime(1987, 2, 3)), 'children': []},
                    {'id': 4, 'name': 'Meg Griffin', 'weight': 800, 'birthdate': str(datetime.datetime(1985, 6, 9)), 'children': []},
                    {'id': 5, 'name': 'Stewie Griffin', 'weight': 30, 'birthdate': str(datetime.datetime(2010, 6, 5)), 'children': []},
                ]},
                {'id': 6, 'name': 'Brian Griffin', 'weight': 40, 'birthdate': str(datetime.datetime(2002, 11, 14)), 'children': []},
                {'id': 7, 'name': 'Quagmire', 'weight': 40, 'birthdate': str(datetime.datetime(1971, 12, 4)), 'children': []}
            ])
            stop = time.time()
            complex_times.append(stop - start)
            
            time.sleep(0.2)
        
        stop_total = time.time()
        time_total = stop_total - start_total
    
        def get_avg(l):
            total = 0.0
            for t in l:
                total += t
                
            return total / len(l)
        
        add_avg = get_avg(add_times)
        concat_avg = get_avg(concat_times)
        bool_avg = get_avg(bool_times)
        date_avg = get_avg(date_times)
        complex_avg = get_avg(complex_times)
        total_avg = get_avg([add_avg, concat_avg, bool_avg, date_avg, complex_avg])
        
        return (time_total, add_avg, concat_avg, bool_avg, date_avg, complex_avg, total_avg)
        
    json_total, json_add, json_concat, json_bool, json_date, json_complex, json_avg = rpc(j)
    xml_total, xml_add, xml_concat, xml_bool, xml_date, xml_complex, xml_avg = rpc(x)
    
    def compare_avg(json, xml):
        if xml < json:
            return format_time('Result: XML won by', json - xml)
        elif json < xml:
            return format_time('Result: JSON won by', xml - json)
        else:
            return 'Result: XML and JSON tied'
    
    print ''
    print ''
    print 'ADD TWO NUMBERS'
    print '=================================='
    print format_time('JSON-RPC', json_add)
    print format_time('XML-RPC', xml_add)
    print compare_avg(json_add, xml_add)
    print ''
    print ''
    print 'CONCATENATE TWO STRINGS'
    print '=================================='
    print format_time('JSON-RPC', json_concat)
    print format_time('XML-RPC', xml_concat)
    print compare_avg(json_concat, xml_concat)
    print ''
    print ''
    print 'HANDLE BOOLEAN VALUE'
    print '=================================='
    print format_time('JSON-RPC', json_bool)
    print format_time('XML-RPC', xml_bool)
    print compare_avg(json_bool, xml_bool)
    print ''
    print ''
    print 'HANDLE DATETIME VALUE'
    print '=================================='
    print format_time('JSON-RPC', json_date)
    print format_time('XML-RPC', xml_date)
    print compare_avg(json_date, xml_date)
    print ''
    print ''
    print 'HANDLE COMPLEX OBJECTS'
    print '=================================='
    print format_time('JSON-RPC', json_complex)
    print format_time('XML-RPC', xml_complex)
    print compare_avg(json_complex, xml_complex)
    print ''
    print ''
    print 'FINAL RESULTS'
    print '=================================='
    print format_time('JSON-RPC Average Time', json_avg)
    print format_time('JSON-RPC Total Time Taken', json_total)
    print format_time('XML-RPC Average Time', xml_avg)
    print format_time('XML-RPC Total Time Taken', xml_total)
    print compare_avg(json_avg, xml_avg)
    print ''
    print ''

test()

Reply via email to