optimise bvec/bio iteration
cover optimise bvec/bio iteration
This adds simpler versions of bvec_iter_advance() and bio_advance_iter() (i.e. *_single()), that are faster but work with the restriction that @bytes shouldn't be more than available in the current bvec segment.
That covers most of bvec/bio iteration/foreach, that are massively inlined, and thus also nicely shrinks binary.
Others non core-block users might be updated on case by case basis (if applicable) after the change is merged.
Pavel Begunkov (2):
block: optimise for_each_bvec() advance
bio: optimise bvec iteration
block/bio.c | 4 ++-- include/linux/bio.h | 17 +++++++++++++++-- include/linux/bvec.h | 20 +++++++++++++++----- 3 files changed, 32 insertions(+), 9 deletions(-)
Signature · 1 line
2.24.0
PATCH 1/2 block: optimise for_each_bvec() advance
Because of how for_each_bvec() works it never advances across multiple entries at a time, so bvec_iter_advance() is an overkill. Add specialised bvec_iter_advance_single() that is faster. It also handles zero-len bvecs, so can kill bvec_iter_skip_zero_bvec().
text data bss dec hex filename
before:
23977 805 0 24782 60ce lib/iov_iter.o
before, bvec_iter_advance() w/o WARN_ONCE()
22886 600 0 23486 5bbe ./lib/iov_iter.o
after:
21862 600 0 22462 57be lib/iov_iter.o
--- include/linux/bvec.h | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-)
include/linux/bvec.h +15 -5
@@ -121,18 +121,28 @@ static inline bool bvec_iter_advance(const struct bio_vec *bv,return true; } -static inline void bvec_iter_skip_zero_bvec (struct bvec_iter *iter) +/* +* A simpler version of bvec_iter_advance(), @bytes should not span +* across multiple bvec entries, i.e. bytes <= bv[i->bi_idx].bv_len +*/ +static inline void bvec_iter_advance_single (const struct bio_vec *bv, +struct bvec_iter *iter,unsigned int bytes) { - iter->bi_bvec_done =0 ; - iter->bi_idx++; +unsigned int done = iter->bi_bvec_done + bytes; + +if (done == bv[iter->bi_idx].bv_len) { + done =0 ; + iter->bi_idx++; + } + iter->bi_bvec_done = done; + iter->bi_size -= bytes; }#define for_each_bvec (bvl, bio_vec, iter, start) \ for (iter = (start); \ (iter).bi_size && \ ((bvl = bvec_iter_bvec((bio_vec), (iter))), 1); \ - (bvl).bv_len ? (void)bvec_iter_advance((bio_vec), &(iter), \ - (bvl).bv_len) : bvec_iter_skip_zero_bvec(&(iter))) + bvec_iter_advance_single((bio_vec), &(iter), (bvl).bv_len))/* for iterating one bio from start to end */ #define BVEC_ITER_ALL_INIT (struct bvec_iter) \
Signature · 1 line
2.24.0
Re: [PATCH 1/2] block: optimise for_each_bvec() advance
Quoted 13 lines · Pavel Begunkov
Because of how
for_each_bvec()works it never advances across multiple entries at a time, sobvec_iter_advance()is an overkill. Add specialisedbvec_iter_advance_single()that is faster. It also handles zero-len bvecs, so can killbvec_iter_skip_zero_bvec().text data bss dec hex filename
before:
23977 805 0 24782 60celib/iov_iter.o
before,bvec_iter_advance()w/oWARN_ONCE()
22886 600 0 23486 5bbe./lib/iov_iter.o
after:
21862 600 0 22462 57belib/iov_iter.o
Quoted 2 lines
Reviewed-by Ming Lei <[email protected]>Signed-off-by Pavel Begunkov <[email protected]>
Looks good,
PATCH 2/2 bio: optimise bvec iteration
In reply to Pavel Begunkov__bio_for_each_bvec(), __bio_for_each_segment() and bio_copy_data_iter() fall under conditions of bvec_iter_advance_single(), which is a faster and slimmer version of bvec_iter_advance(). Add bio_advance_iter_single() and convert them.
--- block/bio.c | 4 ++-- include/linux/bio.h | 17 +++++++++++++++-- 2 files changed, 17 insertions(+), 4 deletions(-)
block/bio.c +2 -2
@@ -1212,8 +1212,8 @@ void bio_copy_data_iter(struct bio *dst, struct bvec_iter *dst_iter,flush_dcache_page (dst_bv.bv_page); -bio_advance_iter (src, src_iter, bytes); -bio_advance_iter (dst, dst_iter, bytes); +bio_advance_iter_single (src, src_iter, bytes); +bio_advance_iter_single (dst, dst_iter, bytes); } }EXPORT_SYMBOL (bio_copy_data_iter);
include/linux/bio.h +15 -2
@@ -148,11 +148,24 @@ static inline void bio_advance_iter(const struct bio *bio,/* TODO: It is reasonable to complete bio with error here. */ } +/* @bytes should be less or equal to bvec[i->bi_idx].bv_len */ +static inline void bio_advance_iter_single (const struct bio *bio, +struct bvec_iter *iter, +unsigned int bytes) +{ + iter->bi_sector += bytes >>9 ; + +if (bio_no_advance_iter (bio)) + iter->bi_size -= bytes; +else +bvec_iter_advance_single (bio->bi_io_vec, iter, bytes); +} +#define __bio_for_each_segment (bvl, bio, iter, start) \ for (iter = (start); \ (iter).bi_size && \ ((bvl = bio_iter_iovec((bio), (iter))), 1); \ - bio_advance_iter((bio), &(iter), (bvl).bv_len)) + bio_advance_iter_single((bio), &(iter), (bvl).bv_len))#define bio_for_each_segment (bvl, bio, iter) \ __bio_for_each_segment(bvl, bio, iter, (bio)->bi_iter)
@@ -161,7 +174,7 @@ static inline void bio_advance_iter(const struct bio *bio,for (iter = (start); \ (iter).bi_size && \ ((bvl =mp_bvec_iter_bvec ((bio)->bi_io_vec, (iter))),1 ); \ -bio_advance_iter ((bio), &(iter), (bvl).bv_len)) +bio_advance_iter_single ((bio), &(iter), (bvl).bv_len))/* iterate over multi-page bvec */ #define bio_for_each_bvec(bvl, bio, iter) \
Signature · 1 line
2.24.0
Re: [PATCH 2/2] bio: optimise bvec iteration
Quoted 4 lines · Pavel Begunkov
__bio_for_each_bvec(),__bio_for_each_segment()andbio_copy_data_iter()fall under conditions ofbvec_iter_advance_single(), which is a faster and slimmer version ofbvec_iter_advance(). Addbio_advance_iter_single()and convert them.
Are you sure about bio_advance_iter()? That API looks like it might not always be limited to a single segment, and might at least need a WARN_ON_ONCE to make sure it is not abused.
Re: [PATCH 2/2] bio: optimise bvec iteration
Quoted 7 lines · Christoph Hellwig
Quoted 4 lines · Pavel Begunkov
__bio_for_each_bvec(),__bio_for_each_segment()andbio_copy_data_iter()fall under conditions ofbvec_iter_advance_single(), which is a faster and slimmer version ofbvec_iter_advance(). Addbio_advance_iter_single()and convert them.Are you sure about
bio_advance_iter()? That API looks like it might
Both those listed bio_for_each*() pass bvl.bv_len, which is truncated to current segment by bio_iter_iovec() (i.e. bvec_iter_bvec) or mp_bvec_iter_bvec().
And just to note that I didn't change bio_advance_iter() but added a new function.
There is always space for stupid mistakes, but I'm sure. What makes you to think opposite? I may have missed it.
Quoted 2 lines
not always be limited to a single segment, and might at least need a
WARN_ON_ONCEto make sure it is not abused.
I thought twice about converting other places as you commented before, and it looks saner to not do that exactly for that reason. I prefer to leave *_single() versions for rare but high impact cases like for_each()s.
And as it's contained I decided to not add overhead on WARN_ONCE(), e.g. with inlining and w/o string dedup it grows .data section much.
Signature · 1 line
Pavel Begunkov
Re: [PATCH 2/2] bio: optimise bvec iteration
Quoted 13 lines · Pavel Begunkov
Quoted 7 lines · Christoph Hellwig
Quoted 4 lines · Pavel Begunkov
__bio_for_each_bvec(),__bio_for_each_segment()andbio_copy_data_iter()fall under conditions ofbvec_iter_advance_single(), which is a faster and slimmer version ofbvec_iter_advance(). Addbio_advance_iter_single()and convert them.Are you sure about
bio_advance_iter()? That API looks like it mightBoth those listed bio_for_each*() pass bvl.bv_len, which is truncated to current segment by
bio_iter_iovec()(i.e.bvec_iter_bvec) ormp_bvec_iter_bvec().
Quoted 5 lines
And just to note that I didn't change
bio_advance_iter()but added a new function.There is always space for stupid mistakes, but I'm sure. What makes you to think opposite? I may have missed it.
Christoph, any doubts left?
Quoted 10 lines
Quoted 2 lines
not always be limited to a single segment, and might at least need a
WARN_ON_ONCEto make sure it is not abused.I thought twice about converting other places as you commented before, and it looks saner to not do that exactly for that reason. I prefer to leave
*_single()versions for rare but high impact cases like for_each()s.
And as it's contained I decided to not add overhead onWARN_ONCE(), e.g. with inlining and w/o string dedup it grows .data section much.
Signature · 1 line
Pavel Begunkov
Re: [PATCH 2/2] bio: optimise bvec iteration
In reply to Pavel BegunkovQuoted 6 lines · Pavel Begunkov
__bio_for_each_bvec(),__bio_for_each_segment()andbio_copy_data_iter()fall under conditions ofbvec_iter_advance_single(), which is a faster and slimmer version ofbvec_iter_advance(). Addbio_advance_iter_single()and convert them.Signed-off-by Pavel Begunkov <[email protected]>
Looks good,
Quoted 9 lines · Pavel Begunkov
This adds simpler versions of
bvec_iter_advance()andbio_advance_iter()(i.e.*_single()), that are faster but work with the restriction that @bytes shouldn't be more than available in the current bvec segment.That covers most of bvec/bio iteration/foreach, that are massively inlined, and thus also nicely shrinks binary.
Others non core-block users might be updated on case by case basis (if applicable) after the change is merged.
Applied, thanks.
Signature · 1 line
Jens Axboe