[英]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 {}
。
但是如果你想使用像continue
或await
這樣的控制流結構,你需要一個for
循環。
我在使用一次的迭代器前面鏈接一個常量,我是否為此支付 O(n) 成本?
什么 n 的 O(n)?
在我的腦海中,一旦在運行時轉化為 if,這表明它確實如此。
好的代碼Once
實際上只是Option
的迭代器的包裝器take
它所做的就是調用自身。
mem::replace(self, None)
所以if at runtime
once
,條件在迭代協議中。
chain
中有條件,因為它需要從第一個運算符切換到第二個運算符,所以那里變得有點復雜。
我怎么知道?
如果你使用 godbolt 可能會更容易,因為它的裝配視圖比操場好得多。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.