From: Paolo Bonzini <pbonzini@redhat.com>
To: Eric Blake <eblake@redhat.com>
Cc: kwolf@redhat.com, jcody@redhat.com, qemu-devel@nongnu.org,
stefanha@linux.vnet.ibm.com
Subject: Re: [Qemu-devel] [PATCH 37/47] add hierarchical bitmap data type and test cases
Date: Mon, 30 Jul 2012 15:39:16 +0200 [thread overview]
Message-ID: <50168E84.7050101@redhat.com> (raw)
In-Reply-To: <5013E89C.7080903@redhat.com>
Il 28/07/2012 15:26, Eric Blake ha scritto:
> On 07/24/2012 05:04 AM, Paolo Bonzini wrote:
>> HBitmaps provide an array of bits. The bits are stored as usual in an
>> array of unsigned longs, but HBitmap is also optimized to provide fast
>> iteration over set bits; going from one bit to the next is O(logB n)
>> worst case, with B = sizeof(long) * CHAR_BIT: the result is low enough
>> that the number of levels is in fact fixed.
>>
>
>> +++ b/hbitmap.c
>> + * So it is easy to move up simply by shifting the index right by
>> + * log2(BITS_PER_LONG) bits. To move down, you shift the index left
>> + * similarly, and add the word index within the group. Iteration uses
>> + * ffs (find first set bit) to find the next word to examine; this
>> + * operation can be done in constant time in most current architectures.
>
> Technically, ffs and friends can be done in constant time in ALL
> architectures. There are some well-known bit-twiddling algorithms for
> computing it in straightline C code with no conditionals (and therefore
> in constant time, just with a larger constant than on platforms that
> lack the builtin single instruction).
Technically, those are O(log2 BITS_PER_LONG). :)
>> +
>> +struct HBitmap {
>> + /* Number of total bits in the bottom level. */
>> + uint64_t size;
>> +
>> + /* Number of set bits in the bottom level. */
>> + uint64_t count;
>> +
>> + /* A scaling factor. When setting or resetting bits, the bitmap will
>> + * scale bit numbers right by this amount of bits. When iterating,
>> + * the bitmap will scale bit numbers left by this amonut of bits.
>
> s/amonut/amount/
>
>> + * Example of operations in a size-16, granularity-1 HBitmap:
>> + *
>> + * initial state 00000000
>> + * set(start=0, count=9) 11111000 (iter: 0, 2, 4, 6, 8)
>> + * reset(start=1, count=3) 00111000 (iter: 4, 6, 8)
>> + * set(start=9, count=2) 00111100 (iter: 4, 6, 8, 10)
>> + * reset(start=5, count=5) 00000000
>
> Thanks. That helps me understand tremendously over the previous version
> of this patch. Basically, you are stating that rather than storing 16
> bits, you compress and only store information on 8 pages, and each
> operation on a range of bits first rounds the bits to determine which
> page they land in then affect the entire page; while iteration only
> visits the first bit of each page. A granularity of 1 means pages are
> 1<<1 or every 2 bit numbers.
Yes.
> Typical values of granularity will
> probably then be 0 (every bit is important) or 12 (operating on 4k
> pages, where touching any byte in the page is sufficient to track that
> entire page as affected in the bitmap).
In our case, bit indices are sector numbers, so a common value of the
granularity will be for example 7=16-9 (for a 64k-coarse dirty bitmap).
>> + */
>> + int granularity;
>> +
>> + /* A number of progressively less coarse bitmaps (i.e. level 0 is the
>> + * coarsest). Each bit in level N represents a word in level N+1 that
>> + * has a set bit, except the last level where each bit represents the
>> + * actual bitmap.
>> + */
>> + unsigned long *levels[HBITMAP_LEVELS];
>
> That is, even allocating a 1-bit bitmap will still allocate
> HBITMAP_LEVELS arrays, rather than trying to dynamically optimize and
> reduce the number of levels necessary to hold the requested size. Fair
> enough.
Also because the HBitmapIter would become even more complex than it
already is.
>> +
>> +static int hbi_count_towards(HBitmapIter *hbi, uint64_t last)
>> +{
>> + uint64_t next = hbi_next_internal(hbi);
>> + int n;
>> +
>> + /* Take it easy with the last few bits. */
>> + if (next >= (last & -BITS_PER_LONG)) {
>> + return (next > last ? 0 : 1);
>
> You could write this as:
> return next <= last;
> but probably not worth the obfuscation.
You probably guessed why I wrote it that way. It's at the top of the
function, and such a return may give the wrong idea that the function
returns a boolean.
>> +void hbitmap_iter_init(HBitmapIter *hbi, HBitmap *hb, uint64_t first)
>> +{
>> + int i, bit;
>> + size_t pos;
>> +
>> + hbi->hb = hb;
>> + pos = first;
>> + for (i = HBITMAP_LEVELS; --i >= 0; ) {
>> + bit = pos & (BITS_PER_LONG - 1);
>> + pos >>= BITS_PER_LEVEL;
>> +
>> + /* Drop bits representing items before first. */
>> + hbi->cur[i] = hb->levels[i][pos] & ~((1UL << bit) - 1);
>> +
>> + /* We have already added level i+1, so the lowest set bit has
>> + * been processed. Clear it.
>> + */
>> + if (i != HBITMAP_LEVELS - 1) {
>> + hbi->cur[i] &= ~(1UL << bit);
>> + }
>> + }
>> +
>> + hbi->pos = first >> BITS_PER_LEVEL;
>> + hbi->granularity = hb->granularity;
>
> Do we really need hbi->granularity, or can the code get by with
> hbi->hb->granularity?
It's probably prematurely optimized---this way the fast path does not
need to access hb at all. But it's also a little bit helpful in gdb,
and it is practically free, so I'd rather leave it.
>> +HBitmap *hbitmap_alloc(uint64_t size, int granularity)
>> +{
>> + HBitmap *hb = g_malloc0(sizeof(struct HBitmap));
>> + int i;
>> +
>> + assert(granularity >= 0 && granularity < 64);
>
> Shouldn't this be granularity < BITS_PER_LONG?
Yep, thanks.
>> +++ b/hbitmap.h
>
>> +#define BITS_PER_LEVEL (BITS_PER_LONG == 32 ? 5 : 6)
>> +
>> +/* For 32-bit, the largest that fits in a 4 GiB address space.
>> + * For 64-bit, the number of sectors in 1 PiB. Good luck, in
>> + * either case... :)
>> + */
>> +#define HBITMAP_LOG_MAX_SIZE (BITS_PER_LONG == 32 ? 34 : 41)
>> +
>> +/* Leave an extra bit for a sentinel. */
>> +#define HBITMAP_LEVELS ((HBITMAP_LOG_MAX_SIZE / BITS_PER_LEVEL) + 1)
>
> Interesting that this picks 7 levels for both 32-bit and 64-bit long
> (hmm, that's why you capped HBITMAP_LOG_MAX_SIZE to the number of
> sectors in 1 PiB, rather than covering the entire address space :)
>
>> +
>> +struct HBitmapIter {
>> + HBitmap *hb;
>> + size_t pos;
>> + int granularity;
>
> Again, do you really need granularity here?
>
>> + unsigned long cur[HBITMAP_LEVELS];
>> +};
>> +
>
> I did a much closer read of the code this time around, and I'm happy
> that your implementation looks sound by inspection as well as has an
> accompanying testsuite.
Yeah, no way to be confident about this without a testsuite.
Thanks for the review!
Paolo
>> +++ b/tests/test-hbitmap.c
>
>> +static void test_hbitmap_iter_granularity(TestHBitmapData *data,
>> + const void *unused)
>> +{
>> + HBitmapIter hbi;
>> +
>> + /* Note that hbitmap_test_check has to be invoked manually in this test. */
>> + hbitmap_test_init(data, 131072 << 7, 7);
>> + hbitmap_iter_init(&hbi, data->hb, 0);
>> + g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0);
>> + hbitmap_test_set(data, ((L2 + L1 + 1) << 7) + 8, 8);
>> + hbitmap_iter_init(&hbi, data->hb, 0);
>> + g_assert_cmpint(hbitmap_iter_next(&hbi), ==, (L2 + L1 + 1) << 7);
>
> Misleading comment, since you didn't call hbitmap_test_check.
>
next prev parent reply other threads:[~2012-07-30 13:39 UTC|newest]
Thread overview: 136+ messages / expand[flat|nested] mbox.gz Atom feed top
2012-07-24 11:03 [Qemu-devel] [PATCH 00/47] Block job improvements for 1.2 Paolo Bonzini
2012-07-24 11:03 ` [Qemu-devel] [PATCH 01/47] qapi: generalize documentation of streaming commands Paolo Bonzini
2012-07-24 11:03 ` [Qemu-devel] [PATCH 02/47] qerror/block: introduce QERR_BLOCK_JOB_NOT_ACTIVE Paolo Bonzini
2012-07-26 15:26 ` Kevin Wolf
2012-07-26 15:41 ` Paolo Bonzini
2012-07-26 16:49 ` Luiz Capitulino
2012-07-26 16:59 ` Paolo Bonzini
2012-07-26 17:02 ` Luiz Capitulino
2012-07-24 11:03 ` [Qemu-devel] [PATCH 03/47] block: move job APIs to separate files Paolo Bonzini
2012-07-26 15:50 ` Kevin Wolf
2012-07-24 11:03 ` [Qemu-devel] [PATCH 04/47] block: add block_job_query Paolo Bonzini
2012-07-30 14:47 ` Kevin Wolf
2012-07-30 15:05 ` Paolo Bonzini
2012-07-31 8:47 ` Kevin Wolf
2012-07-31 8:50 ` Paolo Bonzini
2012-08-02 19:28 ` Jeff Cody
2012-07-24 11:03 ` [Qemu-devel] [PATCH 05/47] block: add support for job pause/resume Paolo Bonzini
2012-07-24 11:03 ` [Qemu-devel] [PATCH 06/47] qmp: add block-job-pause and block-job-resume Paolo Bonzini
2012-08-01 7:42 ` Kevin Wolf
2012-07-24 11:03 ` [Qemu-devel] [PATCH 07/47] qemu-iotests: add test for pausing a streaming operation Paolo Bonzini
2012-07-24 11:03 ` [Qemu-devel] [PATCH 08/47] block: rename block_job_complete to block_job_completed Paolo Bonzini
2012-07-24 11:03 ` [Qemu-devel] [PATCH 09/47] block: rename BlockErrorAction, BlockQMPEventAction Paolo Bonzini
2012-07-24 11:03 ` [Qemu-devel] [PATCH 10/47] block: move BlockdevOnError declaration to QAPI Paolo Bonzini
2012-07-24 11:03 ` [Qemu-devel] [PATCH 11/47] block: reorganize io error code Paolo Bonzini
2012-08-01 9:30 ` Kevin Wolf
2012-08-01 9:46 ` Paolo Bonzini
2012-07-24 11:03 ` [Qemu-devel] [PATCH 12/47] block: sort BlockDeviceIoStatus errors by severity Paolo Bonzini
2012-08-01 9:44 ` Paolo Bonzini
2012-08-01 9:44 ` Kevin Wolf
2012-07-24 11:03 ` [Qemu-devel] [PATCH 13/47] block: introduce block job error Paolo Bonzini
2012-07-25 17:40 ` Eric Blake
2012-08-01 10:14 ` Kevin Wolf
2012-08-01 11:17 ` Paolo Bonzini
2012-08-01 11:49 ` Kevin Wolf
2012-08-01 12:09 ` Paolo Bonzini
2012-08-01 12:23 ` Kevin Wolf
2012-08-01 12:30 ` Paolo Bonzini
2012-08-01 13:09 ` Kevin Wolf
2012-08-01 13:21 ` Paolo Bonzini
2012-08-01 14:01 ` Kevin Wolf
2012-08-01 14:34 ` Paolo Bonzini
2012-08-01 14:59 ` Kevin Wolf
2012-08-01 15:15 ` Paolo Bonzini
2012-08-06 9:29 ` Kevin Wolf
2012-08-06 9:44 ` Paolo Bonzini
2012-08-06 10:45 ` Kevin Wolf
2012-08-06 10:58 ` Paolo Bonzini
2012-07-24 11:03 ` [Qemu-devel] [PATCH 14/47] stream: add on-error argument Paolo Bonzini
2012-07-31 18:40 ` Eric Blake
2012-08-01 10:29 ` Kevin Wolf
2012-08-01 11:11 ` Paolo Bonzini
2012-08-01 11:45 ` Kevin Wolf
2012-07-24 11:03 ` [Qemu-devel] [PATCH 15/47] blkdebug: process all set_state rules in the old state Paolo Bonzini
2012-07-24 20:06 ` Blue Swirl
2012-07-24 11:03 ` [Qemu-devel] [PATCH 16/47] qemu-iotests: map underscore to dash in QMP argument names Paolo Bonzini
2012-07-24 11:03 ` [Qemu-devel] [PATCH 17/47] qemu-iotests: add tests for streaming error handling Paolo Bonzini
2012-08-01 10:43 ` Kevin Wolf
2012-08-01 11:09 ` Paolo Bonzini
2012-07-24 11:03 ` [Qemu-devel] [PATCH 18/47] block: live snapshot documentation tweaks Paolo Bonzini
2012-07-24 11:03 ` [Qemu-devel] [PATCH 19/47] block: add bdrv_query_info Paolo Bonzini
2012-09-11 13:07 ` Kevin Wolf
2012-09-11 13:12 ` Paolo Bonzini
2012-07-24 11:03 ` [Qemu-devel] [PATCH 20/47] block: add bdrv_query_stats Paolo Bonzini
2012-07-24 11:03 ` [Qemu-devel] [PATCH 21/47] block: add bdrv_ensure_backing_file Paolo Bonzini
2012-09-11 13:32 ` Kevin Wolf
2012-09-11 13:46 ` Paolo Bonzini
2012-09-11 13:58 ` Kevin Wolf
2012-09-11 14:10 ` Paolo Bonzini
2012-09-11 15:38 ` Kevin Wolf
2012-07-24 11:04 ` [Qemu-devel] [PATCH 22/47] block: make device optional in BlockInfo Paolo Bonzini
2012-09-11 13:38 ` Kevin Wolf
2012-09-11 13:49 ` Paolo Bonzini
2012-09-11 14:02 ` Kevin Wolf
2012-09-11 14:14 ` Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 23/47] block: add target info to QMP query-blockjobs command Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 24/47] block: introduce new dirty bitmap functionality Paolo Bonzini
2012-09-11 14:57 ` Kevin Wolf
2012-09-11 16:17 ` Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 25/47] block: add block-job-complete Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 26/47] block: introduce BLOCK_JOB_READY event Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 27/47] block: introduce mirror job Paolo Bonzini
2012-07-25 23:02 ` Eric Blake
2012-09-13 12:54 ` Kevin Wolf
2012-09-13 14:07 ` Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 28/47] qmp: add drive-mirror command Paolo Bonzini
2012-07-26 23:42 ` Eric Blake
2012-07-27 7:04 ` Paolo Bonzini
2012-07-31 9:26 ` Kevin Wolf
2012-07-31 9:33 ` Paolo Bonzini
2012-07-31 9:46 ` Kevin Wolf
2012-07-31 10:02 ` Paolo Bonzini
2012-07-31 10:25 ` Kevin Wolf
2012-07-31 10:51 ` Paolo Bonzini
2012-07-31 11:13 ` Kevin Wolf
2012-07-31 11:25 ` Paolo Bonzini
2012-07-31 12:17 ` Kevin Wolf
2012-07-31 12:52 ` Paolo Bonzini
2012-09-13 13:15 ` Kevin Wolf
2012-09-13 13:24 ` Paolo Bonzini
2012-09-13 13:26 ` Kevin Wolf
2012-09-13 13:38 ` Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 29/47] mirror: support querying target file Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 30/47] mirror: implement completion Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 31/47] qemu-iotests: add mirroring test case Paolo Bonzini
2012-07-26 23:46 ` Eric Blake
2012-07-27 7:04 ` Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 32/47] block: forward bdrv_iostatus_reset to block job Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 33/47] mirror: add support for on-source-error/on-target-error Paolo Bonzini
2012-07-27 15:26 ` Eric Blake
2012-07-30 13:29 ` Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 34/47] qmp: add pull_event function Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 35/47] qemu-iotests: add testcases for mirroring on-source-error/on-target-error Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 36/47] host-utils: add ffsl and flsl Paolo Bonzini
2012-07-27 16:05 ` Eric Blake
2012-07-30 13:30 ` Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 37/47] add hierarchical bitmap data type and test cases Paolo Bonzini
2012-07-28 13:26 ` Eric Blake
2012-07-30 13:39 ` Paolo Bonzini [this message]
2012-07-30 14:18 ` Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 38/47] block: implement dirty bitmap using HBitmap Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 39/47] block: make round_to_clusters public Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 40/47] mirror: perform COW if the cluster size is bigger than the granularity Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 41/47] block: return count of dirty sectors, not chunks Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 42/47] block: allow customizing the granularity of the dirty bitmap Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 43/47] mirror: allow customizing the granularity Paolo Bonzini
2012-07-28 13:43 ` Eric Blake
2012-07-30 13:40 ` Paolo Bonzini
2012-07-30 13:53 ` Eric Blake
2012-07-30 14:03 ` Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 44/47] mirror: switch mirror_iteration to AIO Paolo Bonzini
2012-07-28 13:46 ` Eric Blake
2012-07-30 13:41 ` Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 45/47] mirror: add buf-size argument to drive-mirror Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 46/47] mirror: support more than one in-flight AIO operation Paolo Bonzini
2012-07-24 11:04 ` [Qemu-devel] [PATCH 47/47] mirror: support arbitrarily-sized iterations Paolo Bonzini
2012-07-28 13:51 ` [Qemu-devel] [PATCH 00/47] Block job improvements for 1.2 Eric Blake
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=50168E84.7050101@redhat.com \
--to=pbonzini@redhat.com \
--cc=eblake@redhat.com \
--cc=jcody@redhat.com \
--cc=kwolf@redhat.com \
--cc=qemu-devel@nongnu.org \
--cc=stefanha@linux.vnet.ibm.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.