[RFC] Reduce memory with Key and Keys (and improve performance)

John Arbash Meinel john at arbash-meinel.com
Fri Oct 2 23:11:07 BST 2009


-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Andrew Bennetts wrote:
> Robert Collins wrote:
>> On Thu, 2009-09-10 at 16:01 -0500, John Arbash Meinel wrote:
>>
>>> Given the 10%-ish memory reduction, and the fairly impressive speed
>>> improvement, I'm thinking it is probably worth investigating this further.
>> I'm very much in favour of investigating this further.
> 
> Me too!  These are extremely promising results.  And as Robert says further
> down, having real objects that we can add methods and attributes to is an added
> bonus.
> 
> -Andrew.

To give a little bit more update about where I'm at.
The new branch is at:
  lp:~jameinel/bzr/2.1-static-tuple

With a basic design that falls into 2 parts:
 1) A 'StaticTuple' type which for all intents and purposes is a tuple
    with some small variations

    a) It can only reference other StaticTuples or PyStrings
    b) It has a '.intern()' attribute which canonicalizes it (similar to
       intern() for strings)
    c) intern() also is designed not to create immortal objects (so when
       the last refcount is deleted, it looks if it needs to remove
       itself from the interned group.)

 2) A 'StaticTupleIntern' object, which is essentially a PySet, only it
    *doesn't* cache the hash value associated with a reference.

    a) This means that it uses 1/2 the memory of a PySet, and 1/3rd the
       memory of a PyDict (which is used by string's intern)
    b) It also exposes some a Get function which PySet doesn't, and its
       "Add" function returns the object at that location.
    c) Which generally means you only have to do 1 lookup to get the
       canonical form rather than one for "Is there one already" and
       another for "Now put this one there".
    d) It also isn't put into the python GC because Pyrex doesn't know
       how to handle my raw-pointer (it Pyrex/Cython doesn't handle
       arrays of objects without going to a List object.)
       On the plus side, I didn't *want* GC to walk this structure,
       because it has 100-500k pointers that don't need to be evaluated.


Results look promising, though it isn't where I'd like it to be in all
cases.

For "load all the parent maps across all versionedfiles" it is quite
good. For my launchpad branch I'm at:
  327MB, 53.4s for bzr.dev
  244MB, 26.5s for my branch

Time for 'bzr log -r -1 -n1' (load the whole graph but only display the
last revision) is pretty much unchanged at around 700ms.

Time for 'bzr log -n1 -v' is also mostly unchanged, and memory
consumption has actually gone up slightly.

Time for 'bzr branch bzr.dev standalone' is:
  407MB, 2m04s for bzr.dev
  332MB, 1m42s for my branch

So a 1.23:1 savings in memory and 1.22:1 savings in speed. And from what
I can tell, this should only increase with something like a Launchpad tree.

Right now it requires Cython to build, because there are a few
constructs that looked nice. It probably isn't a huge difference, so I
may backport to pyrex, but I figured while exploring the space, I'd keep
it easy on myself.

John
=:->
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (Cygwin)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iEYEARECAAYFAkrGensACgkQJdeBCYSNAAMjswCgmyJIghlYCuyxioBtMqlAsbMu
6ZYAoMcKnYH4iQZkC4ix+lvVchrVL3sn
=m3BF
-----END PGP SIGNATURE-----



More information about the bazaar mailing list