为什么indexingIterator.next()使用动态调度? [英] Why is indexingIterator.next() using dynamic dispatch?

查看:77
本文介绍了为什么indexingIterator.next()使用动态调度?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

但是,如果仅使用 range for-in ,则时间分析器将显示协议见证人.

为什么 indexingIterator.next()使用动态方法,而不在 School 中使用?我以为即使 struct 都符合 protocol ,如果 struct 类型的变量使用 protocol 的方法,则该方法将是静态方法.如果我错了,你能告诉我哪里错了吗?

⬇️学校代码

  struct SchoolIterator:IteratorProtocol {私立var学校列表:学校var idx = 0init(_学校:学校){self.schoolList =学校}更改func next()->细绳?{延迟{idx + = 1}守卫schoolList.count-1> = idx否则{return nil}返回schoolList [idx]}}struct School:序列{fileprivate var list = Array(重复:"school",计数:100000)var count:Int {return list.count}下标(_ idx:Int)->细绳?{守卫idx< = count-1否则{return nil}返回列表[idx]}func makeIterator()->SchoolIterator {返回SchoolIterator(个体)}}var school = School()在学校里上学{打印(学校)} 

解决方案

您的for循环翻译为:

 各种学校= School()var iterator = school.makeIterator()而让school = iterator.next(){打印(学校)} 

请注意,这里没有什么协议. schools 的类型为 School iterator 的类型为 SchoolIterator ,所有 next 确实(例如访问 schoolList.count schoolList 的下标)也处理结构.关键在于,编译器可以准确地找出您的意思,因为它的(编译时)类型是结构.无需查找见证表.

将其与例如

进行比较

  func f< S:序列>(_ s:S){对于s {...}/*var迭代器:S.Iterator = s.makeIterator()而让东西= iterator.next(){...}*/}f(学校())f(1 ..< 100) 

编译器将如何将调用分派给 iterator.next()?我特意添加了类型注释,以使发生的事情更清楚-这次,编译器不知道您指的是哪个 next .是 IndexingIterator.next()吗?还是 SchoolIterator.next()?还是 SomeOtherIterator.next()?请记住,我可以使用任何 Sequence 来调用 f !这就是为什么它需要在运行时查找 S.Iterator 的实际类型的见证表的原因-无法找出哪个 next 打电话.

至于为什么 for 0中的i..<100 使用动态分派,乍一看,似乎所有结构都如此:

 让范围:Range< Int>= 0 ..< 100var iterator:IndexingIterator< Range< Int>>= range.makeIterator()而让我= iterator.next(){...} 

但是, iterator.next 实际上会执行 https://medium.com/@venki0119/method-dispatch-in-swift-effects-of-it-on-performance-b5f120e497d3

Why is for-in slower than while in swift debugging mode? I wrote this. Thanks for people who answer to me, I could have learned Seqeunce and IteratorProtocol.

So I implemented custom type ( School below code ) which conformed Sequence. And I checked Xcode-time profile.

But I can't find anything protocol witness

But If only use range and for-in , time profiler show protocol witness.

why is indexingIterator.next() using dynamic method but not in School ? I thought that even struct conformed protocol, if variable in struct type use method of protocol, this method will be static method. If I am wrong, Could you please tell me what is wrong?

⬇️School code

struct SchoolIterator: IteratorProtocol {
    
    private var schoolList: School
    var idx = 0
    init(_ school: School) {
        self.schoolList = school
    }
    
    mutating  func next() -> String? {
        defer { idx += 1 }
        guard schoolList.count-1 >= idx
            else { return nil }
        
        return schoolList[idx]
    }
}

struct School: Sequence {
    fileprivate var list = Array(repeating: "school", count: 100000)
    var count: Int { return list.count }
    
    subscript(_ idx: Int ) -> String? {
        guard idx <= count-1
            else { return nil }
        return list[idx]
    }
    func makeIterator() -> SchoolIterator {
        return SchoolIterator(self)
    }
}
var schools = School()
for school in schools {
    print(school)
}


解决方案

Your for loop translates to:

var schools = School()
var iterator = schools.makeIterator()
while let school = iterator.next() {
    print(school)
}

Notice how nothing here is a protocol. schools is of type School, iterator is of type SchoolIterator, everything that next does (like accessing schoolList.count, or the subscript of schoolList) deals with structs too. The key point is that the compiler can figure out exactly which member you mean, because its (compile-time) type is a struct. There is no need to look up witness tables.

Compare that to, e.g.

func f<S: Sequence>(_ s: S) {
    for thing in s {
        ...
    }
/*
    var iterator: S.Iterator = s.makeIterator()
    while let thing = iterator.next() {
        ...
    }
*/
}

f(School())
f(1..<100)

How would the compiler dispatch the calls to iterator.next()? I've deliberately added the type annotation to make it clear what's happening - this time, the compiler doesn't know which next you mean. Is it IndexingIterator.next()? Or SchoolIterator.next()? Or SomeOtherIterator.next()? Keep in mind that I can call f with any kind of Sequence! That's why it needs to look up the witness table of the actual type of S.Iterator at runtime - it is impossible to figure out which next to call.

As for why for i in 0..<100 uses dynamic dispatch, well, on first sight, there seems to be all structs:

let range: Range<Int> = 0..<100
var iterator: IndexingIterator<Range<Int>> = range.makeIterator()
while let i = iterator.next() {
    ...
}

However, iterator.next actually does something like this:

public mutating func next() -> Elements.Element? { if _position == _elements.endIndex { return nil } let element = _elements[_position] _elements.formIndex(after: &_position) return element }

_elements is defined like this:

public struct IndexingIterator<Elements: Collection> {
  
  internal let _elements: Elements

_elements could be any kind of Collection, so again, we don't know which member _elements[_position] or _elements.formIndex refers to at compile time. Is it Array.formIndex? Or Set.formIndex? We only know at runtime, when we know what Elements is.

Recommended reading: https://medium.com/@venki0119/method-dispatch-in-swift-effects-of-it-on-performance-b5f120e497d3

这篇关于为什么indexingIterator.next()使用动态调度?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆