[interfacekit] Re: BBlockCache bug
- From: "Jeremy Rand" <jrand@xxxxxxxx>
- To: interfacekit@xxxxxxxxxxxxx
- Date: Sat, 06 Sep 2003 23:52:48 EDT (-0400)
So, I have the tests done now. You can find them here:
http://www.magma.ca/~jrand/BBlockCacheTests.zip
I don't have CVS access so if someone can volunteer to check this in, I
would appreciate it.
However, there is a minor issue. Marcus has checked in a linked list
implementation which looks very good and much better than the one which
was there before. But, there is a deviation in his implementation from
Be's.
Imagine this scenario with a 4 block cache. The blocks on the cache
are A, B, C, D. If a caller gets five blocks from the cache, the
following is returned: A, B, C, D, E. The first four come from the
cache and the fifth (E) is allocated dynamically to satisfy the caller.
What happens if the caller then repeatedly Save()'s E and then performs
a Get() again over
and over again?
In Be's implementation, when E is Save()'ed, E is placed on the list of
free blocks in the cache. Even though E was not in the cache
initially, since it is the right size it isn't freed and just put in
the cache for a caller to get later. So, if E is Save()'ed and gotten
again repeatedly, Be's implementation does this:
- put E on the cache
- take E off the cache and pass back to the caller
- put E on the cache
- etc.
This means that Be intends that the cache will hold up to 4 blocks in
this case but the total number of blocks on the cache plus those handed
out through the Get() method may exceed 4 perhaps by a great deal.
But in Marcus' implementation, if you Get() 5 elements from a 4 element
cache and then return one, he will not start putting elements back into
the cache right away. When the 5th element is gotten from the cache,
it tracks that there is an "excess block" oustanding. While excess
blocks are outstanding, any attempt to return one of them to the cache
will actually result in them being freed. So, with Marcus'
implementation, the following would occur if E is Save()'ed and gotten
again repeatedly:
- E is freed
- a new block (possibly the same as E) is allocated
- the block is freed again
- etc.
Marcus' implementation guarantees that there will not be any blocks in
the cache if there are 4 or more blocks returned by Get() which are
still outstanding. Be's implementation will be faster in this case by
avoiding the calls to the memory allocator. Plus, my tests detect this
difference in behaviour and will fail on Marcus' current
implementation.
In the zip file, I have two updated implementations of BBlockCache:
- src/kits/support/BBlockCache.new and headers/os/support/
BBlockCache.new: This is a minor modification of Marcus' implementation
based on linked lists to match Be's behaviour when returning blocks to
an empty cache.
- src/kits/support/BBlockCache.jer and headers/os/support/
BBlockCache.jer: This is an implementation which I expect is 99.9% the
same as Be's. Its Get() and Save() methods are O(1) and the cache is
implemented as an array of (void *)'s.
Both implementations pass the tests with very similar performance.
Either is fine and I try to be as "egoless" as possible about these
things. Truth be known, Marcus' implementation does have some great
debugging features like setting and checking magic numbers in it which
we should probably hold on to.
So, I am looking for someone to decide what should get checked in and
then do it for me. Thanks!
> What one can do to optimize Save() is not to keep track of the used
> blocks.
> That way you simply check, whether the chunk of memory passed to
> Save() has
> the right size and, if so, add it to your stack of free blocks. If
> not, or if
> the cache has reached the maximal number of free blocks, the memory
> would
> simply be freed. That way both Get() and Save() are O(1).
>
> CU, Ingo
--
Jeremy Rand
jrand@xxxxxxxx
Other related posts: