簡體   English   中英

std::iter::once 的成本是多少?

[英]What is the cost of std::iter::once?

如果我有這樣的代碼:

pub fn f(x: u16) -> impl Iterator<Item = u16> {
    std::iter::once(0).chain((x >> 10)..x)
}

我使用once在迭代器前面鏈接一個常量,當迭代器被消耗時我是否為它支付 O(n) 成本? 我無法從程序集中分辨出來(除了它肯定會在內存中設置一個額外的數據結構)。 我怎么知道?

如前所述, once()的成本是 O(1)。 您正在考慮的 O(n) 成本來自chain() ,而不是once() ,因為至少在優化之前, <Chain as Iterator>::next()必須在每一步決定是否從第一個迭代器或第二個迭代器。

但是,可以通過使用內部迭代來避免產生該成本——其控制流由迭代器本身負責的迭代,這樣就不需要重復調用next() 最類似for的方式是for_each() 如果查看這段代碼生成的程序集,您會發現在main2()中使用for_each()的版本要簡單得多:

pub fn f(x: u16) -> impl Iterator<Item = u16> {
    std::iter::once(0).chain((x >> 10)..x)
}

pub fn main1() -> u16 {
    let mut sum = 0;
    for item in f(3) {
        sum += std::hint::black_box(item);
    }
    sum
}

pub fn main2() -> u16 {    
    let mut sum = 0;
    f(3).for_each(|item| {
        sum += std::hint::black_box(item);
    });
    sum
}

black_box() ,我曾經阻止將循環優化為常量,預計將在 Rust 1.66.0 中保持穩定——目前,使用 nightly 構建。)

這有幫助的原因是for_each()是根據fold()實現的,並且Chain迭代器覆蓋了fold()的默認實現,以便它調用第一個迭代器自己的fold() ,然后調用第二個迭代器的。 每個項目都沒有條件。

您不必顯式使用for_each()fold() — 您應該期望任何可以從中受益的迭代器操作,如sum()collect() ,都會這樣做。 一般來說,如果您正在尋找最佳的迭代器性能並且涉及chain()之類的東西,請嘗試使用對迭代器的單個調用來表達您的操作,而不是next()或轉換為next()的東西,例如for item in iter {}

但是如果你想使用像continueawait這樣的控制流結構,你需要一個for循環。

我在使用一次的迭代器前面鏈接一個常量,我是否為此支付 O(n) 成本?

什么 n 的 O(n)?

在我的腦海中,一旦在運行時轉化為 if,這表明它確實如此。

好的代碼Once實際上只是Option的迭代器的包裝器take所做的就是調用自身。

take的是

mem::replace(self, None)

所以if at runtime once ,條件在迭代協議中。

chain中有條件,因為需要從第一個運算符切換到第二個運算符,所以那里變得有點復雜。

我怎么知道?

如果你使用 godbolt 可能會更容易,因為它的裝配視圖比操場好得多。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM