All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Alexandre Courbot" <acourbot@nvidia.com>
To: "Alexandre Courbot" <acourbot@nvidia.com>,
	"Boqun Feng" <boqun.feng@gmail.com>
Cc: "Danilo Krummrich" <dakr@kernel.org>,
	"Miguel Ojeda" <ojeda@kernel.org>,
	"Alex Gaynor" <alex.gaynor@gmail.com>,
	"Gary Guo" <gary@garyguo.net>,
	"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
	"Benno Lossin" <benno.lossin@proton.me>,
	"Andreas Hindborg" <a.hindborg@kernel.org>,
	"Alice Ryhl" <aliceryhl@google.com>,
	"Trevor Gross" <tmgross@umich.edu>,
	"Joel Fernandes" <joelagnelf@nvidia.com>,
	"John Hubbard" <jhubbard@nvidia.com>,
	rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH v2] rust: alloc: implement `extend` for `Vec`
Date: Sun, 06 Apr 2025 22:05:39 +0900	[thread overview]
Message-ID: <D8ZKTDE05HBM.2S9OI9WDVEV6U@nvidia.com> (raw)
In-Reply-To: <D8ZKOIHKPBCN.OIHEWU3TUE1W@nvidia.com>

On Sun Apr 6, 2025 at 9:59 PM JST, Alexandre Courbot wrote:
> Hi Boqun, thanks for the review!
>
> On Sun Apr 6, 2025 at 4:44 AM JST, Boqun Feng wrote:
>> Hi Alexandre,
>>
>> Thanks for the patch.
>>
>> On Sat, Apr 05, 2025 at 10:51:41PM +0900, Alexandre Courbot wrote:
>>> KVec currently has `extend_with` and `extend_from_slice` methods, but no
>>> way extend a vector from a regular iterator as provided by the `Extend`
>>> trait.
>>> 
>>> Due to the need to provide the GFP flags, `Extend` cannot be implemented
>>> directly, so simply define a homonymous method that takes an extra
>>> `flags` argument.
>>> 
>>> The aforementioned `extend_with` and `extend_from_slice` can then be
>>> reimplemented as direct invocations of this new method - maybe they can
>>> eventually be removed.
>>> 
>>> Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
>>> ---
>>> I was a bit surprised to find no equivalent of the `Extend` trait for
>>> KVec, and while I anticipate to be told the reason for this, I also
>>> didn't hit any hard wall trying to come with my own implementation so
>>> here it is.
>>> 
>>> I expect the new `extend_with` and `extend_from_slice` to be optimized
>>> into something close to their previous implementations, but am not sure
>>> how I can simply verify that this is the case - any hint would be
>>> appreciated!
>>> ---
>>> Changes in v2:
>>> - Changed the diff algorithm to histogram for a more readable patch.
>>> ---
>>>  rust/kernel/alloc/kvec.rs | 89 +++++++++++++++++++++++++++++------------------
>>>  1 file changed, 56 insertions(+), 33 deletions(-)
>>> 
>>> diff --git a/rust/kernel/alloc/kvec.rs b/rust/kernel/alloc/kvec.rs
>>> index ae9d072741cedbb34bed0be0c20cc75472aa53be..e78cb5ee575ce01e44283f8b4905689fb1e96165 100644
>>> --- a/rust/kernel/alloc/kvec.rs
>>> +++ b/rust/kernel/alloc/kvec.rs
>>> @@ -454,30 +454,64 @@ pub fn reserve(&mut self, additional: usize, flags: Flags) -> Result<(), AllocEr
>>>      }
>>>  }
>>>  
>>> +impl<T, A: Allocator> Vec<T, A> {
>>> +    /// Extends the vector by the elements of `iter`.
>>> +    ///
>>> +    /// This uses [`Iterator::size_hint`] to optimize reallocation of memory, but will work even
>>> +    /// with imprecise implementations - albeit with potentially more memory reallocations.
>>> +    ///
>>> +    /// In the kernel most iterators are expected to have a precise `size_hint` implementation, so
>>> +    /// this should nicely optimize out in most cases.
>>> +    pub fn extend<I>(&mut self, iter: I, flags: Flags) -> Result<(), AllocError>
>>> +    where
>>> +        I: IntoIterator<Item = T>,
>>> +    {
>>> +        let mut iter = iter.into_iter();
>>> +
>>> +        loop {
>>> +            let low_bound = match iter.size_hint() {
>>> +                // No more items expected, we can return.
>>> +                (0, Some(0)) => break,
>>> +                // Possibly more items but not certain, tentatively add one.
>>> +                (0, _) => 1,
>>> +                // More items pending, reserve space for the lower bound.
>>> +                (low_bound, _) => low_bound,
>>> +            };
>>> +
>>> +            self.reserve(low_bound, flags)?;
>>> +
>>
>> I want to point out this might cause a behavior change, previously
>> extend_with() and extend_with_slice() do a "all-or-nothing" extension
>> depending on memory allocation, i.e. if there is enough memory for all
>> the new items, do the extension, otherwise do nothing. Your changes here
>> make it that extension can fail in-between due to AllocError, that is,
>> only part of the `iter` is added. Of course, in practice, both
>> slice::Iter and iter::Take will just return the number of all the items
>> as the low_bound of .size_hint(), but it's not guaranteed.
>
> That's a very valid point, and one of the reasons why I would like to
> see how the code is actually optimized in `extend_with` and
> `extend_with_slice`. While the method is designed to handle
> imprecise/incorrect implementations of `size_hint`, the expectation is
> that outside of very unusual uses the code should be able to be
> optimized into a single-allocation, non-loop pass.
>
> We could probably enforce that if we had access to `TrustedLen` or
> defined our own equivalent trait for the kernel. The set of iterators
> that could be passed as arguments would be more limited, but for the
> same reason as above I don't think that would be a big limitation.
>
>> I don't see a direct correct-or-wrong answer for what behavior is
>> desired, but if we are moving to a new behavior, we need to make sure
>> updating the document of the extend*() function. Plus if failing
>> in-between, should we return the `iter` so that users can continue do
>> something about the `iter`?
>
> I have updated the documentation with more details about the sub-optimal
> and error cases. I am not sure what use a caller would have from the
> remaining items - and after all, the currently existing methods also
> don't return the iterator upon failure. If we want to preserve the
> current behavior, we can always reduce the size of the vector to its
> pre-call value on the error path.

Err of course they don't have any iterator to return so that last point
is mostly moot, except maybe for `extend_with` which doesn't return the
passed value, which the caller hasn't necessarily kept a clone of.

  reply	other threads:[~2025-04-06 13:05 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-04-05 13:51 [PATCH v2] rust: alloc: implement `extend` for `Vec` Alexandre Courbot
2025-04-05 19:44 ` Boqun Feng
2025-04-06 12:59   ` Alexandre Courbot
2025-04-06 13:05     ` Alexandre Courbot [this message]
2025-04-06 11:59 ` kernel test robot

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=D8ZKTDE05HBM.2S9OI9WDVEV6U@nvidia.com \
    --to=acourbot@nvidia.com \
    --cc=a.hindborg@kernel.org \
    --cc=alex.gaynor@gmail.com \
    --cc=aliceryhl@google.com \
    --cc=benno.lossin@proton.me \
    --cc=bjorn3_gh@protonmail.com \
    --cc=boqun.feng@gmail.com \
    --cc=dakr@kernel.org \
    --cc=gary@garyguo.net \
    --cc=jhubbard@nvidia.com \
    --cc=joelagnelf@nvidia.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=ojeda@kernel.org \
    --cc=rust-for-linux@vger.kernel.org \
    --cc=tmgross@umich.edu \
    /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.