1#!/usr/bin/env python 2 3# 4# Licensed to the Apache Software Foundation (ASF) under one 5# or more contributor license agreements. See the NOTICE file 6# distributed with this work for additional information 7# regarding copyright ownership. The ASF licenses this file 8# to you under the Apache License, Version 2.0 (the 9# "License"); you may not use this file except in compliance 10# with the License. You may obtain a copy of the License at 11# 12# http://www.apache.org/licenses/LICENSE-2.0 13# 14# Unless required by applicable law or agreed to in writing, 15# software distributed under the License is distributed on an 16# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 17# KIND, either express or implied. See the License for the 18# specific language governing permissions and limitations 19# under the License. 20# 21 22r""" 23PYTHONPATH=./gen-py:../../lib/py/build/lib... ./FastbinaryTest.py 24""" 25 26# TODO(dreiss): Test error cases. Check for memory leaks. 27 28from __future__ import print_function 29 30import math 31import os 32import sys 33import timeit 34 35from copy import deepcopy 36from pprint import pprint 37 38from thrift.transport import TTransport 39from thrift.protocol.TBinaryProtocol import TBinaryProtocol, TBinaryProtocolAccelerated 40from thrift.protocol.TCompactProtocol import TCompactProtocol, TCompactProtocolAccelerated 41 42from DebugProtoTest import Srv 43from DebugProtoTest.ttypes import Backwards, Bonk, Empty, HolyMoley, OneOfEach, RandomStuff, Wrapper 44 45 46class TDevNullTransport(TTransport.TTransportBase): 47 def __init__(self): 48 pass 49 50 def isOpen(self): 51 return True 52 53 54ooe1 = OneOfEach() 55ooe1.im_true = True 56ooe1.im_false = False 57ooe1.a_bite = 0xd6 58ooe1.integer16 = 27000 59ooe1.integer32 = 1 << 24 60ooe1.integer64 = 6000 * 1000 * 1000 61ooe1.double_precision = math.pi 62ooe1.some_characters = "Debug THIS!" 63ooe1.zomg_unicode = u"\xd7\n\a\t" 64 65ooe2 = OneOfEach() 66ooe2.integer16 = 16 67ooe2.integer32 = 32 68ooe2.integer64 = 64 69ooe2.double_precision = (math.sqrt(5) + 1) / 2 70ooe2.some_characters = ":R (me going \"rrrr\")" 71ooe2.zomg_unicode = u"\xd3\x80\xe2\x85\xae\xce\x9d\x20"\ 72 u"\xd0\x9d\xce\xbf\xe2\x85\xbf\xd0\xbe"\ 73 u"\xc9\xa1\xd0\xb3\xd0\xb0\xcf\x81\xe2\x84\x8e"\ 74 u"\x20\xce\x91\x74\x74\xce\xb1\xe2\x85\xbd\xce\xba"\ 75 u"\xc7\x83\xe2\x80\xbc" 76 77if sys.version_info[0] == 2 and os.environ.get('THRIFT_TEST_PY_NO_UTF8STRINGS'): 78 ooe1.zomg_unicode = ooe1.zomg_unicode.encode('utf8') 79 ooe2.zomg_unicode = ooe2.zomg_unicode.encode('utf8') 80 81hm = HolyMoley(**{"big": [], "contain": set(), "bonks": {}}) 82hm.big.append(ooe1) 83hm.big.append(ooe2) 84hm.big[0].a_bite = 0x22 85hm.big[1].a_bite = 0x22 86 87hm.contain.add(("and a one", "and a two")) 88hm.contain.add(("then a one, two", "three!", "FOUR!")) 89if sys.version_info[0] == 2 and os.environ.get('THRIFT_TEST_PY_NO_UTF8STRINGS'): 90 hm.contain.add((u"\xd7\n\a\t".encode('utf8'),)) 91else: 92 hm.contain.add((u"\xd7\n\a\t",)) 93hm.contain.add(()) 94 95hm.bonks["nothing"] = [] 96hm.bonks["something"] = [ 97 Bonk(**{"type": 1, "message": "Wait."}), 98 Bonk(**{"type": 2, "message": "What?"}), 99] 100hm.bonks["poe"] = [ 101 Bonk(**{"type": 3, "message": "quoth"}), 102 Bonk(**{"type": 4, "message": "the raven"}), 103 Bonk(**{"type": 5, "message": "nevermore"}), 104] 105 106rs = RandomStuff() 107rs.a = 1 108rs.b = 2 109rs.c = 3 110rs.myintlist = list(range(20)) 111rs.maps = {1: Wrapper(**{"foo": Empty()}), 2: Wrapper(**{"foo": Empty()})} 112rs.bigint = 124523452435 113rs.triple = 3.14 114 115# make sure this splits two buffers in a buffered protocol 116rshuge = RandomStuff() 117rshuge.myintlist = list(range(10000)) 118 119my_zero = Srv.Janky_result(**{"success": 5}) 120 121 122class Test(object): 123 def __init__(self, fast, slow): 124 self._fast = fast 125 self._slow = slow 126 127 def _check_write(self, o): 128 trans_fast = TTransport.TMemoryBuffer() 129 trans_slow = TTransport.TMemoryBuffer() 130 prot_fast = self._fast(trans_fast, fallback=False) 131 prot_slow = self._slow(trans_slow) 132 133 o.write(prot_fast) 134 o.write(prot_slow) 135 ORIG = trans_slow.getvalue() 136 MINE = trans_fast.getvalue() 137 if ORIG != MINE: 138 print("actual : %s\nexpected: %s" % (repr(MINE), repr(ORIG))) 139 raise Exception('write value mismatch') 140 141 def _check_read(self, o): 142 prot = self._slow(TTransport.TMemoryBuffer()) 143 o.write(prot) 144 145 slow_version_binary = prot.trans.getvalue() 146 147 prot = self._fast( 148 TTransport.TMemoryBuffer(slow_version_binary), fallback=False) 149 c = o.__class__() 150 c.read(prot) 151 if c != o: 152 print("actual : ") 153 pprint(repr(c)) 154 print("expected: ") 155 pprint(repr(o)) 156 raise Exception('read value mismatch') 157 158 prot = self._fast( 159 TTransport.TBufferedTransport( 160 TTransport.TMemoryBuffer(slow_version_binary)), fallback=False) 161 c = o.__class__() 162 c.read(prot) 163 if c != o: 164 print("actual : ") 165 pprint(repr(c)) 166 print("expected: ") 167 pprint(repr(o)) 168 raise Exception('read value mismatch') 169 170 def do_test(self): 171 self._check_write(HolyMoley()) 172 self._check_read(HolyMoley()) 173 174 self._check_write(hm) 175 no_set = deepcopy(hm) 176 no_set.contain = set() 177 self._check_read(no_set) 178 self._check_read(hm) 179 180 self._check_write(rs) 181 self._check_read(rs) 182 183 self._check_write(rshuge) 184 self._check_read(rshuge) 185 186 self._check_write(my_zero) 187 self._check_read(my_zero) 188 189 self._check_read(Backwards(**{"first_tag2": 4, "second_tag1": 2})) 190 191 # One case where the serialized form changes, but only superficially. 192 o = Backwards(**{"first_tag2": 4, "second_tag1": 2}) 193 trans_fast = TTransport.TMemoryBuffer() 194 trans_slow = TTransport.TMemoryBuffer() 195 prot_fast = self._fast(trans_fast, fallback=False) 196 prot_slow = self._slow(trans_slow) 197 198 o.write(prot_fast) 199 o.write(prot_slow) 200 ORIG = trans_slow.getvalue() 201 MINE = trans_fast.getvalue() 202 assert id(ORIG) != id(MINE) 203 204 prot = self._fast(TTransport.TMemoryBuffer(), fallback=False) 205 o.write(prot) 206 prot = self._slow( 207 TTransport.TMemoryBuffer(prot.trans.getvalue())) 208 c = o.__class__() 209 c.read(prot) 210 if c != o: 211 print("copy: ") 212 pprint(repr(c)) 213 print("orig: ") 214 pprint(repr(o)) 215 216 217def do_test(fast, slow): 218 Test(fast, slow).do_test() 219 220 221def do_benchmark(protocol, iters=5000, skip_slow=False): 222 setup = """ 223from __main__ import hm, rs, TDevNullTransport 224from thrift.protocol.{0} import {0}{1} 225trans = TDevNullTransport() 226prot = {0}{1}(trans{2}) 227""" 228 229 setup_fast = setup.format(protocol, 'Accelerated', ', fallback=False') 230 if not skip_slow: 231 setup_slow = setup.format(protocol, '', '') 232 233 print("Starting Benchmarks") 234 235 if not skip_slow: 236 print("HolyMoley Standard = %f" % 237 timeit.Timer('hm.write(prot)', setup_slow).timeit(number=iters)) 238 239 print("HolyMoley Acceler. = %f" % 240 timeit.Timer('hm.write(prot)', setup_fast).timeit(number=iters)) 241 242 if not skip_slow: 243 print("FastStruct Standard = %f" % 244 timeit.Timer('rs.write(prot)', setup_slow).timeit(number=iters)) 245 246 print("FastStruct Acceler. = %f" % 247 timeit.Timer('rs.write(prot)', setup_fast).timeit(number=iters)) 248 249 250if __name__ == '__main__': 251 print('Testing TBinaryAccelerated') 252 do_test(TBinaryProtocolAccelerated, TBinaryProtocol) 253 do_benchmark('TBinaryProtocol') 254 print('Testing TCompactAccelerated') 255 do_test(TCompactProtocolAccelerated, TCompactProtocol) 256 do_benchmark('TCompactProtocol') 257