On Mon, Dec 4, 2017 at 11:28 AM, Qu Wenruo <quwenruo.bt...@gmx.com> wrote:
>
>
> On 2017年12月04日 19:13, Filipe Manana wrote:
>> On Mon, Dec 4, 2017 at 7:19 AM, Qu Wenruo <quwenruo.bt...@gmx.com> wrote:
>>>
>>>
>>> On 2017年12月04日 15:02, robbieko wrote:
>>>> From: Robbie Ko <robbi...@synology.com>
>>>>
>>>> The commands generated by send contain the following step:
>>>> 1. mkfile o1851-19-0
>>>> 2. rename o1851-19-0 -> alsa-driver/alsa-kernel/isa/es1688/es1688.c
>>>> 3. set_xattr alsa-driver/alsa-kernel/isa/es1688/es1688.c - name=user.xattr 
>>>> data_len=4 data=test
>>>> 4. write alsa-driver/alsa-kernel/isa/es1688/es1688.c - offset=0, len=10458
>>>> 5. truncate alsa-driver/alsa-kernel/isa/es1688/es1688.c size=10458
>>>> 6. chown alsa-driver/alsa-kernel/isa/es1688/es1688.c - uid=1024, gid=100
>>>> 7. chmod alsa-driver/alsa-kernel/isa/es1688/es1688.c - mode=0644
>>>> 8. utimes alsa-driver/alsa-kernel/isa/es1688/es1688.c
>>>>
>>>> After writing file content, it will truncate file to the correct size.
>>>> Btrfs truncate will flush last page if size does not align to sectorsize,
>>>> and this will cause receive process to wait until flush finishes.
>>>> In order to avoid waiting flushing data.This patch changes the order so
>>>> that truncate command is sent before write command.
>>>
>>> Personally speaking, it's better to optimize the receive side.
>>>
>>> For example, at receive side, if we already know that the file size is
>>> not changed at all, then just skip the truncate command.
>>>
>>> In your send dump, step 5 is not needed at all, and can be skipped to
>>> speed up the receive procedure.
>>
>> Better yet, is to not send the truncate commands at all, reduces the
>> stream size and saves all receiver implementations
>> from having such logic (which further requires stat calls to figure
>> out the current file size).
>
> Another one is to optimize btrfs file truncate to avoid the unnecessary
> page writeback.
>
> But at least, doing optimization in receive side is easier and less
> bug-prone.

Not really, for this case at least.
>
> If we could do the same in both send and receive side, then I prefer
> optimization in receive side.

Can't agree much with that, for the reasons mentioned before.

>
> Thanks,
> Qu
>
>>
>> I still have a local branch around with several optimizations (from
>> the time I was a volunteer), with one of them being precisely to avoid
>> unnecessary truncate commands, however I never ended up getting to the
>> point of doing benchmarks.
>> Pasted below and at https://www.friendpaste.com/1J1TuXf2wlU1125mX57k4r
>>
>>
>> commit 362290ea03b52e4ce4edb43453a51a3b1f1cd33f
>> Author: Filipe David Borba Manana <fdman...@gmail.com>
>> Date:   Sat Apr 5 22:34:45 2014 +0100
>>
>>     Btrfs: send, don't issue unnecessary file truncate commands
>>
>>     Every time a file is created or updated, send (either incremental
>> or not) always
>>     issues a file truncate command. In several commons scenarios this
>> truncate is not
>>     needed and yet it was still issued all the time. The unnecessary
>> cases where the
>>     truncate command is not needed are:
>>
>>     * We have a new file, and its last extent isn't a hole, so that
>> its last write
>>       command ends at the file's size;
>>
>>     * For an incremental send, we updated in place an existing file,
>> without changing
>>       its size;
>>
>>     * For an incremental send, we updated the file, increased its
>> size, and the last
>>       file extent item doesn't correspond to a hole. In this case the last 
>> write
>>       command against the file ends at the file's new size;
>>
>>     * For an incremental send, we didn't update the file's data at
>> all, we only changed
>>       its mode, group or access/update timestamps.
>>
>>     Therefore don't send truncate commands for these cases, reducing
>> the stream's size
>>     and saving time.
>>
>> diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
>> index fa378c7ebffd..46f52b4bbc21 100644
>> --- a/fs/btrfs/send.c
>> +++ b/fs/btrfs/send.c
>> @@ -120,6 +120,7 @@ struct send_ctx {
>>   u64 cur_inode_mode;
>>   u64 cur_inode_rdev;
>>   u64 cur_inode_last_extent;
>> + u64 cur_inode_max_write_end;
>>
>>   u64 send_progress;
>>   u64 total_data_size;
>> @@ -4494,6 +4495,8 @@ static int send_hole(struct send_ctx *sctx, u64 end)
>>   break;
>>   offset += len;
>>   }
>> + sctx->cur_inode_max_write_end = max(offset,
>> +    sctx->cur_inode_max_write_end);
>>  tlv_put_failure:
>>   fs_path_free(p);
>>   return ret;
>> @@ -4529,6 +4532,10 @@ static int send_write_or_clone(struct send_ctx *sctx,
>>   len = btrfs_file_extent_num_bytes(path->nodes[0], ei);
>>   }
>>
>> + if (offset >= sctx->cur_inode_size) {
>> + ret = 0;
>> + goto out;
>> + }
>>   if (offset + len > sctx->cur_inode_size)
>>   len = sctx->cur_inode_size - offset;
>>   if (len == 0) {
>> @@ -4560,6 +4567,8 @@ static int send_write_or_clone(struct send_ctx *sctx,
>>   }
>>   ret = 0;
>>   }
>> + sctx->cur_inode_max_write_end = max(offset + len,
>> +    sctx->cur_inode_max_write_end);
>>  out:
>>   return ret;
>>  }
>> @@ -4982,6 +4991,7 @@ static int finish_inode_if_needed(struct
>> send_ctx *sctx, int at_end)
>>   u64 right_gid;
>>   int need_chmod = 0;
>>   int need_chown = 0;
>> + int need_truncate = 1;
>>   int pending_move = 0;
>>   int refs_processed = 0;
>>
>> @@ -5022,9 +5032,13 @@ static int finish_inode_if_needed(struct
>> send_ctx *sctx, int at_end)
>>   need_chown = 1;
>>   if (!S_ISLNK(sctx->cur_inode_mode))
>>   need_chmod = 1;
>> + if (sctx->cur_inode_max_write_end == sctx->cur_inode_size)
>> + need_truncate = 0;
>>   } else {
>> + u64 old_size;
>> +
>>   ret = get_inode_info(sctx->parent_root, sctx->cur_ino,
>> - NULL, NULL, &right_mode, &right_uid,
>> + &old_size, NULL, &right_mode, &right_uid,
>>   &right_gid, NULL);
>>   if (ret < 0)
>>   goto out;
>> @@ -5033,6 +5047,13 @@ static int finish_inode_if_needed(struct
>> send_ctx *sctx, int at_end)
>>   need_chown = 1;
>>   if (!S_ISLNK(sctx->cur_inode_mode) && left_mode != right_mode)
>>   need_chmod = 1;
>> +
>> + if (old_size == sctx->cur_inode_size &&
>> +    sctx->cur_inode_max_write_end <= sctx->cur_inode_size)
>> + need_truncate = 0;
>> + else if (sctx->cur_inode_size > old_size &&
>> + sctx->cur_inode_max_write_end == sctx->cur_inode_size)
>> + need_truncate = 0;
>>   }
>>
>>  truncate_inode:
>> @@ -5052,10 +5073,13 @@ static int finish_inode_if_needed(struct
>> send_ctx *sctx, int at_end)
>>   goto out;
>>   }
>>   }
>> - ret = send_truncate(sctx, sctx->cur_ino, sctx->cur_inode_gen,
>> - sctx->cur_inode_size);
>> - if (ret < 0)
>> - goto out;
>> + if (need_truncate) {
>> + ret = send_truncate(sctx, sctx->cur_ino,
>> +    sctx->cur_inode_gen,
>> +    sctx->cur_inode_size);
>> + if (ret < 0)
>> + goto out;
>> + }
>>   }
>>
>>   if (need_chown) {
>> @@ -5110,6 +5134,7 @@ static int changed_inode(struct send_ctx *sctx,
>>   sctx->cur_ino = key->objectid;
>>   sctx->cur_inode_new_gen = 0;
>>   sctx->cur_inode_last_extent = (u64)-1;
>> + sctx->cur_inode_max_write_end = 0;
>>
>>   /*
>>   * Set send_progress to current inode. This will tell all get_cur_xxx
>>
>>>
>>> Thanks,
>>> Qu
>>>
>>>>
>>>> Overall performance improves by 102 percent when sending 790000 small 
>>>> files.
>>>> original: 32m45.311s
>>>> patch: 16m8.387s
>>>>
>>>> Signed-off-by: Robbie Ko <robbi...@synology.com>
>>>> ---
>>>>  fs/btrfs/send.c | 13 +++++++++----
>>>>  1 file changed, 9 insertions(+), 4 deletions(-)
>>>>
>>>> diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
>>>> index 20d3300..7ae2347 100644
>>>> --- a/fs/btrfs/send.c
>>>> +++ b/fs/btrfs/send.c
>>>> @@ -5857,10 +5857,6 @@ static int finish_inode_if_needed(struct send_ctx 
>>>> *sctx, int at_end)
>>>>                                       goto out;
>>>>                       }
>>>>               }
>>>> -             ret = send_truncate(sctx, sctx->cur_ino, sctx->cur_inode_gen,
>>>> -                             sctx->cur_inode_size);
>>>> -             if (ret < 0)
>>>> -                     goto out;
>>>>       }
>>>>
>>>>       if (need_chown) {
>>>> @@ -6044,6 +6040,15 @@ static int changed_inode(struct send_ctx *sctx,
>>>>                                       sctx->left_path->nodes[0], left_ii);
>>>>               }
>>>>       }
>>>> +     if (result == BTRFS_COMPARE_TREE_NEW ||
>>>> +         result == BTRFS_COMPARE_TREE_CHANGED) {
>>>> +             if (S_ISREG(sctx->cur_inode_mode)) {
>>>> +                     ret = send_truncate(sctx, sctx->cur_ino, 
>>>> sctx->cur_inode_gen,
>>>> +                                             sctx->cur_inode_size);
>>>> +                     if (ret < 0)
>>>> +                             goto out;
>>>> +             }
>>>> +     }
>>>>
>>>>  out:
>>>>       return ret;
>>>>
>>>
>>
>>
>>
>



-- 
Filipe David Manana,

“Whether you think you can, or you think you can't — you're right.”
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to