PageRenderTime 29ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/cmemcache-0.94-orig/cachecmp.py

https://github.com/eklitzke/cmemcache-yelp
Python | 443 lines | 381 code | 26 blank | 36 comment | 11 complexity | 01fc1d1cf9e7041e926f85656bd690f3 MD5 | raw file
Possible License(s): GPL-2.0, JSON
  1. #!/usr/bin/env python
  2. #
  3. # $Id: cachecmp.py 410 2007-08-25 04:42:22Z gijsbert $
  4. #
  5. """
  6. Test some caching systems of simple string data. Interesting to see what is best to use as a session storage/cache for web apps. Stores: python dict, sql, memcache(d).
  7. """
  8. import random
  9. import threading
  10. import time
  11. import antiorm
  12. __version__ = "$Revision: 410 $"
  13. __author__ = "$Author: gijsbert $"
  14. #-----------------------------------------------------------------------------------------
  15. #
  16. def createv( n, i, size=1 ):
  17. """
  18. Create string value from base 'n' and integer 'i'. Size can be used to make it bigger.
  19. """
  20. return (n + '%04d' % i) * size
  21. #-----------------------------------------------------------------------------------------
  22. #
  23. class store(object):
  24. """
  25. Store base class.
  26. """
  27. def copy( self ):
  28. """
  29. Create a copy of the store for multi-thread usage. Default assumes store is
  30. thread safe and returns self.
  31. """
  32. return self
  33. def teardown( self, nv ):
  34. """
  35. Tear down the store, ie remove added keys, etc.
  36. """
  37. #-----------------------------------------------------------------------------------------
  38. #
  39. class nativestore(store):
  40. """
  41. use the python dictionary
  42. """
  43. name = 'native'
  44. def setup( self, nv ):
  45. self.nv = nv.copy()
  46. def get( self, name ):
  47. return self.nv[name]
  48. def set( self, name, value ):
  49. self.nv[name] = value
  50. #-----------------------------------------------------------------------------------------
  51. #
  52. class nativestorecopy(store):
  53. """
  54. use the python dictionary, but copy the data
  55. """
  56. name = 'nativecopy'
  57. def setup( self, nv ):
  58. self.nv = nv.copy()
  59. def get( self, name ):
  60. return self.nv[name][:]
  61. def set( self, name, value ):
  62. self.nv[name] = value[:]
  63. #-----------------------------------------------------------------------------------------
  64. #
  65. class sqlstor(store):
  66. """
  67. sql base line.
  68. """
  69. name = 'sql'
  70. table_name = "cachetest"
  71. def setupDB( self ):
  72. import psycopg2
  73. self.dbapi = psycopg2
  74. self.conn = psycopg2.connect(database='test',
  75. user=opts.user,
  76. password=opts.password)
  77. the_engine = antiorm.MormConnectionEngine(self.conn)
  78. class NameValueTable(antiorm.MormTable):
  79. table = self.table_name
  80. engine = the_engine
  81. converters = {
  82. 'name': antiorm.MormConvString(),
  83. 'value': antiorm.MormConvString()
  84. }
  85. self.NameValueTable = NameValueTable
  86. def setup( self, nv ):
  87. self.setupDB()
  88. curs = self.conn.cursor()
  89. try:
  90. curs.execute( "DROP TABLE %s" % (self.table_name,) )
  91. except self.dbapi.ProgrammingError:
  92. # table probably does not exist
  93. pass
  94. self.conn.commit()
  95. curs.execute( """
  96. CREATE TABLE %s (
  97. name text primary key,
  98. value text
  99. );
  100. """ % (self.table_name,) )
  101. for n, v in nv.iteritems():
  102. self.NameValueTable.insert(name=n, value=v)
  103. self.conn.commit()
  104. def get( self, name ):
  105. res = self.NameValueTable.select('WHERE name = %s', (name,), cols=('value',))
  106. assert(len(res) == 1)
  107. return res.next().value
  108. def set( self, name, value ):
  109. self.NameValueTable.update('WHERE name=%s', (name,), value=value)
  110. #-----------------------------------------------------------------------------------------
  111. #
  112. class memcachedstor(store):
  113. """
  114. Use memcached as storage.
  115. """
  116. name = 'memcached'
  117. def __init__( self ):
  118. import memcache
  119. servers = ["127.0.0.1:11211"]
  120. self.mc = memcache.Client(servers, debug=0)
  121. self.mc.flush_all()
  122. def setup( self, nv ):
  123. for n, v in nv.iteritems():
  124. self.mc.set(n, v)
  125. def teardown( self, nv ):
  126. for n in nv.iterkeys():
  127. self.mc.delete(n)
  128. def get( self, name ):
  129. return self.mc.get(name)
  130. def set( self, name, value ):
  131. return self.mc.set(name, value)
  132. def copy( self ):
  133. """
  134. memcache object not thread safe, return a new one for each thread.
  135. """
  136. # there is no state, so just return a fresh instance
  137. return memcachedstor()
  138. #-----------------------------------------------------------------------------------------
  139. #
  140. class cmemcachedstor(store):
  141. """
  142. Use c-interface to memcached as storage.
  143. """
  144. name = 'cmemcached'
  145. def __init__( self ):
  146. import cmemcache
  147. servers = ["127.0.0.1:11211"]
  148. self.mc = cmemcache.Client(servers, debug=0)
  149. # fixme: self.mc.flush_all()
  150. def setup( self, nv ):
  151. for n, v in nv.iteritems():
  152. self.mc.set(n, v)
  153. def get( self, name ):
  154. return self.mc.get(name)
  155. def set( self, name, value ):
  156. return self.mc.set(name, value)
  157. def copy( self ):
  158. """
  159. memcache object not thread safe, return a new one for each thread.
  160. """
  161. # there is no state, so just return a fresh instance
  162. return cmemcachedstor()
  163. #-----------------------------------------------------------------------------------------
  164. #
  165. class poshstor(store):
  166. """
  167. Use posh as storage.
  168. """
  169. name = 'posh'
  170. def setup( self, nv ):
  171. import posh
  172. self.nv = nv.copy()
  173. self.snv = posh.share(self.nv)
  174. def get( self, name ):
  175. return self.snv[name]
  176. #-----------------------------------------------------------------------------------------
  177. #
  178. class test(object):
  179. """
  180. Test base class.
  181. """
  182. def __init__( self, opts ):
  183. self.opts = opts
  184. #-----------------------------------------------------------------------------------------
  185. #
  186. class seqtest(test):
  187. """
  188. Just get all names.
  189. """
  190. name = 'seq'
  191. def run( self, store, nv ):
  192. for n,v in nv.iteritems():
  193. value = store.get(n)
  194. if value != v:
  195. print n, value, v, self.name, store.name
  196. assert(value == v)
  197. #-----------------------------------------------------------------------------------------
  198. #
  199. class seqnotesttest(test):
  200. """
  201. Just get all names.
  202. """
  203. name = 'seqnotest'
  204. def run( self, store, nv ):
  205. get = store.get
  206. for n in nv.iterkeys():
  207. get(n)
  208. #-----------------------------------------------------------------------------------------
  209. #
  210. class seqrndwrttest(test):
  211. """
  212. Go through all the values and set or get depending on a random function.
  213. """
  214. name = 'seqrndwrt'
  215. def run( self, store, nv ):
  216. for n,v in nv.iteritems():
  217. if random.random() < opts.writeratio:
  218. # just set same value, I don't think that is being optimised in the store
  219. store.set(n, v)
  220. else:
  221. value = store.get(n)
  222. if value != v:
  223. print n, value, v, self.name, store.name
  224. assert(value == v)
  225. #-----------------------------------------------------------------------------------------
  226. #
  227. class rndtest(test):
  228. """
  229. Just get names in random order.
  230. """
  231. name = 'rnd'
  232. def run( self, store, nv ):
  233. l = len(nv)
  234. # make it a sequence, faster
  235. nvs = zip(nv.iterkeys(), nv.itervalues())
  236. # for i in xrange(1, l):
  237. for i in nv.iteritems():
  238. n, v = random.choice(nvs)
  239. value = store.get(n)
  240. assert(value == v)
  241. #-----------------------------------------------------------------------------------------
  242. #
  243. class threadtest(test):
  244. """
  245. Run another test in a thread.
  246. """
  247. def __init__( self, opts, t ):
  248. test.__init__(self, opts)
  249. self.t = t
  250. self.name = 'thread-' + self.t.name
  251. def run( self, store, nv ):
  252. class TestThread(threading.Thread):
  253. def __init__( self, t, store, nv ):
  254. threading.Thread.__init__(self)
  255. self.t = t
  256. self.store = store
  257. self.nv = nv
  258. def run( self ):
  259. self.t.run( self.store, self.nv )
  260. threads = []
  261. for i in xrange(self.opts.threads):
  262. t = TestThread(self.t, store.copy(), nv)
  263. threads.append(t)
  264. for t in threads:
  265. t.start()
  266. for t in threads:
  267. t.join()
  268. #-------------------------------------------------------------------------------
  269. #
  270. def main():
  271. import optparse
  272. parser = optparse.OptionParser(__doc__.strip())
  273. # on laptops with cpu speed control numpairs should be high enough that creating the
  274. # name,value pairs brings the cpu to full speed.
  275. parser.add_option('-n', '--numpairs', action='store', type='int',
  276. default=10000,
  277. help="Number of name,value pairs to test with." )
  278. parser.add_option('-k', '--namemult', action='store', type='int',
  279. default=4,
  280. help="Name/key size multiplier." )
  281. parser.add_option('-v', '--valuemult', action='store', type='int',
  282. default=400,
  283. help="Value size multiplier." )
  284. # NOTE: when the seq test fails on the value == v check, that means that there was too
  285. # little space in memcached to store all name,value pairs. Restart, and if it still
  286. # fails restart with more memory.
  287. parser.add_option('-t', '--threads', action='store', type='int',
  288. default=10,
  289. help="Number of threads to run." )
  290. parser.add_option('-w', '--writeratio', action='store', type='float',
  291. default=0.25,
  292. help="Ratio of write:read actions." )
  293. parser.add_option('-u', '--user', action='store',
  294. default='gijsbert',
  295. help="DB user")
  296. parser.add_option('-p', '--password', action='store',
  297. default='hAAn',
  298. help="DB password")
  299. global opts
  300. opts, args = parser.parse_args()
  301. assert(opts.numpairs > 0)
  302. print 'Setting up %d name,value pairs' % opts.numpairs
  303. nv = {}
  304. nsz, vsz = 0, 0
  305. for i in range(1, opts.numpairs):
  306. # name = createn('name', i)
  307. name = createv('name', i, opts.namemult)
  308. nsz += len(name)
  309. value = createv('value', i, opts.valuemult)
  310. vsz += len(value)
  311. nv[name] = value
  312. # stors = [ nativestore(), sqlstor(), memcachedstor(), poshstor() ]
  313. stors = [ nativestore(),
  314. nativestorecopy(),
  315. # sqlstor(),
  316. memcachedstor(),
  317. cmemcachedstor() ]
  318. for s in stors:
  319. print 'Initializing', s.name
  320. s.setup(nv)
  321. tests = [ seqtest(opts),
  322. seqnotesttest(opts),
  323. seqrndwrttest(opts),
  324. rndtest(opts)
  325. #,
  326. # threaded test does not tell us anything new:
  327. # threadtest(opts, seqtest(opts))
  328. ]
  329. stats = {}
  330. for t in tests:
  331. lstats = {}
  332. for s in stors:
  333. print 'Doing %d iterations of %s with %s' % (opts.numpairs, t.name, s.name)
  334. # reset random so they all do the same thing
  335. random.seed(12)
  336. t0 = time.time()
  337. t.run(s, nv)
  338. t1 = time.time()
  339. lstats[s] = t1-t0
  340. print 'time elapsed ', t1-t0
  341. print 'per get ', (t1-t0)/opts.numpairs
  342. stats[t] = lstats
  343. for s in stors:
  344. print 'Finalizing', s.name
  345. s.teardown(nv)
  346. print
  347. print 'Name/Value size %d/%d total %d bytes' % \
  348. ((vsz/opts.numpairs), (nsz/opts.numpairs), vsz)
  349. print
  350. print '%14s %s' % ('', ' '.join('%14s' %s.name for s in stors))
  351. for t in tests:
  352. print '%14s' % (t.name,),
  353. for s in stors:
  354. print '%14.8f' % (stats[t][s],),
  355. print
  356. print
  357. print '%14s %s' % ('', ' '.join('%14s' %s.name for s in stors))
  358. for t in tests:
  359. print '%14s' % (t.name,),
  360. for s in stors:
  361. print '%14.8f' % (stats[t][s]/stats[tests[0]][stors[0]],),
  362. print
  363. if __name__ == '__main__':
  364. main()