OSDN Git Service

block: optimise bvec_iter_advance()
authorPavel Begunkov <asml.silence@gmail.com>
Sat, 30 Nov 2019 20:23:52 +0000 (23:23 +0300)
committerJens Axboe <axboe@kernel.dk>
Tue, 3 Dec 2019 14:36:38 +0000 (07:36 -0700)
bvec_iter_advance() is quite popular, but compilers fail to do proper
alias analysis and optimise it good enough. The assembly is checked
for gcc 9.2, x86-64.

- remove @iter->bi_size from min(...), as it's always less than @bytes.
Modify at the beginning and forget about it.

- the compiler isn't able to collapse memory dependencies and remove
writes in the loop. Help it by explicitely using local vars.

Signed-off-by: Arvind Sankar <nivedita@alum.mit.edu>
Signed-off-by: Pavel Begunkov <asml.silence@gmail.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
include/linux/bvec.h

index a032f01..679a422 100644 (file)
@@ -87,26 +87,24 @@ struct bvec_iter_all {
 static inline bool bvec_iter_advance(const struct bio_vec *bv,
                struct bvec_iter *iter, unsigned bytes)
 {
+       unsigned int idx = iter->bi_idx;
+
        if (WARN_ONCE(bytes > iter->bi_size,
                     "Attempted to advance past end of bvec iter\n")) {
                iter->bi_size = 0;
                return false;
        }
 
-       while (bytes) {
-               const struct bio_vec *cur = bv + iter->bi_idx;
-               unsigned len = min3(bytes, iter->bi_size,
-                                   cur->bv_len - iter->bi_bvec_done);
-
-               bytes -= len;
-               iter->bi_size -= len;
-               iter->bi_bvec_done += len;
+       iter->bi_size -= bytes;
+       bytes += iter->bi_bvec_done;
 
-               if (iter->bi_bvec_done == cur->bv_len) {
-                       iter->bi_bvec_done = 0;
-                       iter->bi_idx++;
-               }
+       while (bytes && bytes >= bv[idx].bv_len) {
+               bytes -= bv[idx].bv_len;
+               idx++;
        }
+
+       iter->bi_idx = idx;
+       iter->bi_bvec_done = bytes;
        return true;
 }