为什么 Swift 迭代器比数组构建慢?

2024-01-03

这有点与这个问题 https://stackoverflow.com/questions/40669193/explain-swift-iterators/40672317#40672317,其中假设使用生成器(迭代器)遍历嵌套数组对于迭代元素来说是最佳选择,只要您不需要存储结果,而如果您只想存储结果,则使用重复数组串联是最好的展平阵列。

然而,我决定做一些测试,并实现这个函数(将数组展平)[Any]包含任一Ints or [Int]s) 在惰性和存储形式中,事实证明存储形式更快,即使仅用于迭代元素!这意味着不知何故,迭代生成器比两者都花费更多的时间建造内存中的一个新数组,以及then迭代that.

令人难以置信的是,它甚至比普通计算机慢 5-70% 左右。python实施相同的计划,投入越少,情况就越糟。 Swift 是用-O flag.

这是三个测试用例 1. 小输入,混合; 2.输入量大,[Int]占主导地位,3.大投入,Int主导的:

Swift

let array1: [Any] = [Array(1...100), Array(101...105), 106, 
                     Array(107...111), 112, 113, 114, Array(115...125)]
let array2: [Any] = Array(repeating: Array(1...5), count: 2000)
let array3: [Any] = Array(repeating: 31, count: 10000)

Python

A1 = [list(range(1, 101)), list(range(101, 106)), 106, 
      list(range(107, 112)), 112, 113, 114, list(range(115, 126))]
A2 = list(range(1, 6)) * 2000
A3 = [31] * 10000

生成器和数组生成器:

Swift

func chain(_ segments: [Any]) -> AnyIterator<Int>{
    var i = 0
    var j = 0
    return AnyIterator<Int> {
        while i < segments.count {
            switch segments[i] {
                case let e as Int:
                    i += 1
                    return e
                case let E as [Int]:
                    if j < E.count {
                        let val = E[j]
                        j += 1
                        return val
                    }
                    j = 0
                    i += 1

                default:
                    return nil
            }
        }
        return nil
    }
}


func flatten_array(_ segments: [Any]) -> [Int] {
    var result = [Int]()
    for segment in segments {
        switch segment {
            case let segment as Int:
                result.append(segment)
            case let segment as [Int]:
                result.append(contentsOf: segment)
            default:
                break
        }
    }
    return result
}

Python

def chain(L):
    for i in L:
        if type(i) is int:
            yield i
        elif type(i) is list:
            yield from i


def flatten_list(L):
    result = []
    for i in L:
        if type(i) is int:
            result.append(i)
        elif type(i) is list:
            result.extend(i)
    return result

基准测试结果(第一个测试用例 100000 次循环,其他测试用例 1000 次):

Swift

test case 1 (small mixed input)
    Filling an array                         : 0.068221092224121094 s
    Filling an array, and looping through it : 0.074559926986694336 s
    Looping through a generator              : 1.5902719497680664   s *
    Materializing the generator to an array  : 1.759943962097168    s *

test case 2 (large input, [Int] s)
    Filling an array                         : 0.20634698867797852  s
    Filling an array, and looping through it : 0.21031379699707031  s
    Looping through a generator              : 1.3505551815032959   s *
    Materializing the generator to an array  : 1.4733860492706299   s *

test case 3 (large input, Int s)
    Filling an array                         : 0.27392101287841797  s
    Filling an array, and looping through it : 0.27670192718505859  s
    Looping through a generator              : 0.85304021835327148  s
    Materializing the generator to an array  : 1.0027849674224854   s *

Python

test case 1 (small mixed input)
    Filling an array                         : 0.1622014045715332   s
    Filling an array, and looping through it : 0.4312894344329834   s
    Looping through a generator              : 0.6839139461517334   s
    Materializing the generator to an array  : 0.5300459861755371   s

test case 2 (large input, [int] s)
    Filling an array                         : 1.029205083847046    s
    Filling an array, and looping through it : 1.2195289134979248   s
    Looping through a generator              : 1.0876803398132324   s
    Materializing the generator to an array  : 0.8958714008331299   s

test case 3 (large input, int s)
    Filling an array                         : 1.0181667804718018   s
    Filling an array, and looping through it : 1.244570255279541    s
    Looping through a generator              : 1.1220412254333496   s
    Materializing the generator to an array  : 0.9486079216003418   s

显然,Swift 非常非常擅长构建数组。但为什么它的生成器如此慢,在某些情况下甚至比 Python 还慢? (标有*表中。)使用极大的输入(> 100,000,000 个元素,几乎使 Swift 崩溃)进行测试表明,即使在极限情况下,生成器在最好的情况下也比数组填充慢至少 3.25 倍。

如果这确实是该语言固有的,那么它会产生一些有趣的含义。例如,常识(无论如何对我作为一个Python程序员来说)都会认为,如果我们试图合成一个不可变的对象(如字符串),我们应该首先将源提供给生成函数来展开它,然后手工关闭输出到joined()适用于单个浅层序列的方法。相反,看起来最有效的策略是通过数组进行序列化;将源展开为中间数组,然后从该数组构造输出。

构建一个全新的数组然后迭代它是否比原始数组上的惰性迭代更快?为什么?

(可能相关的javascript问题 https://stackoverflow.com/questions/32983296/why-is-using-a-generator-function-slower-than-filling-and-iterating-an-array-in)

Edit

这是测试代码:

Swift

func time(test_array: [Any], cycles: Int = 1000000) -> (array_iterate: Double, 
                                                        array_store  : Double, 
                                                        generate_iterate: Double, 
                                                        generate_store: Double) {
    func start() -> Double { return Date().timeIntervalSince1970 }
    func lap(_ t0: Double) -> Double {
        return Date().timeIntervalSince1970 - t0
    }
    var t0 = start()

    for _ in 0..<cycles {
        for e in flatten_array(test_array) { e + 1 }
    }
    let ΔE1 = lap(t0)

    t0 = start()
    for _ in 0..<cycles {
        let array: [Int] = flatten_array(test_array)
    }
    let ΔE2 = lap(t0)

    t0 = start()
    for _ in 0..<cycles {
        let G = chain(test_array)
        while let g = G.next() { g + 1 }
    }
    let ΔG1 = lap(t0)

    t0 = start()
    for _ in 0..<cycles {
        let array: [Int] = Array(chain(test_array))
    }
    let ΔG2 = lap(t0)

    return (ΔE1, ΔE2, ΔG1, ΔG2)
}

print(time(test_array: array1, cycles: 100000))
print(time(test_array: array2, cycles: 1000))
print(time(test_array: array3, cycles: 1000))

Python

def time_f(test_array, cycles = 1000000):
    lap = lambda t0: time() - t0
    t0 = time()

    for _ in range(cycles):
        for e in flatten_list(test_array):
            e + 1

    ΔE1 = lap(t0)

    t0 = time()
    for _ in range(cycles):
        array = flatten_list(test_array)

    ΔE2 = lap(t0)

    t0 = time()
    for _ in range(cycles):
        for g in chain(test_array):
            g + 1

    ΔG1 = lap(t0)

    t0 = time()
    for _ in range(cycles):
        array = list(chain(test_array))

    ΔG2 = lap(t0)

    return ΔE1, ΔE2, ΔG1, ΔG2

print(time_f(A1, cycles=100000))
print(time_f(A3, cycles=1000))
print(time_f(A2, cycles=1000))

你问“为什么它的 [Swift] 生成器这么慢,在某些情况下甚至比 Python 的还慢?”

我的回答是,我认为它们并不像您的结果所显示的那么慢。特别是,我将尝试证明循环遍历迭代器应该比为所有测试用例构建数组更快。

在早期的工作中(请参阅相关博客文章http://lemire.me/blog/2016/09/22/swift-versus-java-the-bitset-performance-test/ http://lemire.me/blog/2016/09/22/swift-versus-java-the-bitset-performance-test/),我发现在处理位集类时,Swift 迭代器的速度大约是 Java 中迭代器的一半。这不太好,但 Java 在这方面非常高效。与此同时,Go 的表现更糟。我向您提出,Swift 迭代器的效率可能不理想,但它们的效率可能是原始 C 代码的两倍之内。性能差距可能与 Swift 中的函数内联不足有关。

我发现您正在使用AnyIterator。我建议推导一个struct from IteratorProtocol相反,这样做的好处是确保不必进行任何动态调度。这是一个相对有效的可能性:

public struct FastFlattenIterator: IteratorProtocol {
   let segments: [Any]
    var i = 0 // top-level index
    var j = 0 // second-level index
    var jmax = 0 // essentially, this is currentarray.count, but we buffer it
    var currentarray : [Int]! // quick reference to an int array to be flatten

   init(_ segments: [Any]) {
       self.segments = segments
   }

   public mutating func next() -> Int? {
     if j > 0 { // we handle the case where we iterate within an array separately
       let val = currentarray[j]
       j += 1
       if j == jmax {
         j = 0
         i += 1
       }
       return val
     }
     while i < segments.count {
        switch segments[i] {
          case let e as Int: // found an integer value
            i += 1
            return e
          case let E as [Int]: // first encounter with an array
            jmax = E.count
            currentarray = E
            if jmax > 0 {
              j = 1
              return E[0]
            }
            i += 1
          default:
            return nil
        }
     }
     return nil
   }
}

通过这堂课,我得到了以下数字。对于每个测试用例,前四种方法取自代码示例,而后两种(快速迭代器)是使用新结构构建的。请注意,“循环快速迭代器”始终是最快的。

test case 1 (small mixed input)
Filling an array                         : 0.0073099999999999997 ms
Filling an array, and looping through it : 0.0069870000000000002 ms
Looping through a generator              : 0.18385799999999999   ms 
Materializing the generator to an array  : 0.18745700000000001   ms 
Looping through a fast iterator          : 0.005372              ms 
Materializing the fast iterator          : 0.015883999999999999  ms

test case 2 (large input, [Int] s)
Filling an array                         : 2.125931            ms
Filling an array, and looping through it : 2.1169820000000001  ms
Looping through a generator              : 15.064767           ms 
Materializing the generator to an array  : 15.45152            ms 
Looping through a fast iterator          : 1.572919            ms
Materializing the fast iterator          : 1.964912            ms 

test case 3 (large input, Int s)
Filling an array                         : 2.9140269999999999  ms
Filling an array, and looping through it : 2.9064290000000002  ms
Looping through a generator              : 9.8297640000000008  ms
Materializing the generator to an array  : 9.8297640000000008  ms 
Looping through a fast iterator          : 1.978038            ms 
Materializing the fast iterator          : 2.2565339999999998  ms 

您可以在 GitHub 上找到我的完整代码示例:https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog/tree/master/extra/swift/iterators https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog/tree/master/extra/swift/iterators

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么 Swift 迭代器比数组构建慢? 的相关文章

  • 将构造函数传递给 Array.map?

    我怎样才能做这样的事情 var a 1 2 3 4 a map Date constructor 此代码在 Google V8 上引发错误 SyntaxError Unexpected number 我也尝试过 a map Date con
  • 如何在 swift 中以编程方式使用坐标打开地图应用程序?

    我想在地图应用程序中打开纬度和经度 我尝试了这段代码HERE https stackoverflow com questions 12504294 programmatically open maps app in ios 6 func g
  • SwiftUI - 从 NSObject 继承的 ObservableObject 在 iOS 13 中不会更新

    我知道 这是 无法在 iOS XX 中工作 问题之一 但我完全陷入困境 所以我有一个ObservableObject继承自的类NSObject 因为我需要听委托方法UISearchResultsUpdating class SearchBa
  • 弱变量中间为零

    弱变量什么时候变为零 weak var backgroundNode SKSpriteNode texture SKTexture image initialBackgroundImage backgroundNode position C
  • 优化 CSS 交付 - Google 的建议

    谷歌建议在 head 中使用非常重要的 CSS 内联 并在内部使用其他 CSS
  • Swift 中的 quitFirstResponder

    我怎样才能用Apple的新语言实现它 Objective C 代码 void touchesBegan NSSet touches withEvent UIEvent event for UIView view in self view s
  • 使用 python 生成器高效创建 scipy.lil_matrix

    我有一个生成单一维度的生成器numpy arrays 的长度相同 我想要一个包含该数据的稀疏矩阵 行的生成顺序与我希望它们出现在最终矩阵中的顺序相同 csr矩阵优于lil矩阵 但我认为后者在我描述的场景中更容易构建 假设row gen是一个
  • 使用 VBA 通过简单命令从非连续范围的并集获取值到数组中(无循环)

    我有以下任务 表面上很简单 使用 VBA 将电子表格上多个列的值复制到二维数组中 为了让生活更有趣 这些柱子并不相邻 但它们的长度都相同 显然 可以通过依次循环每个元素来做到这一点 但这看起来非常不优雅 我希望有一个更紧凑的解决方案 但我很
  • C# Byte[] 转 BCD 和 BCD 转 INT

    我有一个由 CashRegister Machine 创建的 Hex 文件 我必须读入这个文件 文件使用下面详述的格式 它就像套接字数据包 代码数据 2字节PLU 代码数据 7 字节单价数据 5字节数量数据 5字节数据总量 5字节PLU 名
  • 优化数据可视化 Web 应用程序的性能

    我正在重写 3 年前编写的数据可视化网络工具 从那时起 浏览器的 JavaScript 引擎变得更快 所以我正在考虑将部分工作从服务器转移到客户端 在页面上 数据在表格和地图 或图表 中可视化 它使用相同的数据 但以不同的方式 因此准备显示
  • Swift:无法为“[UIViewController]”类型的值添加下标?

    我试图弄清楚如何在 Xcode 7 iOS9 上的 Swift 中解决此问题 并且我也遇到此错误 无法为 UIViewController 类型的值添加下标 索引类型为 Int 任何建议表示赞赏 谢谢 My code func indexP
  • jQuery - 提高处理 XML 时的选择器性能

    我正在处理一个 XML 文件 当使用 XPath 样式选择器选择节点时 该文件的性能非常慢 这是运行特别慢的部分代码 for i 0 i
  • 在 C/C++ 中获得正模数的最快方法

    通常在我的内部循环中 我需要以 环绕 方式索引数组 因此 例如 如果数组大小为 100 并且我的代码要求元素 2 则应该给它元素 98 高级语言 例如 Python 可以简单地使用my array index array size 但由于某
  • 如何在 Swift Playgrounds 中获得弹出对话框

    我想知道如何在 Swift 中弹出一个对话框游乐场 是的 必须在 Playgrounds 中 我尝试了以下代码 直接来自 AppleDevs 站点 然而 无论我如何尝试 self tag always抛出错误 谁能帮我这个 import U
  • 在 C# 中按元素相乘数组具有意想不到的性能

    我想找到按元素相乘两个数组的最佳方法 这是更广泛项目的一部分 其中性能而不是唯一的考虑因素 我今天开始用 C Linqpad 编写一些函数 因此它还没有以任何方式进行优化 下面代码的输出如下 Environment ProcessorCou
  • NumPy 和 SciPy - .todense() 和 .toarray() 之间的区别

    我想知道使用是否有什么区别 优点 缺点 toarray vs todense 在稀疏 NumPy 数组上 例如 import scipy as sp import numpy as np sparse m sp sparse bsr mat
  • 如何在 Swift 中创建 UIAlertView?

    我一直在努力在 Swift 中创建 UIAlertView 但由于某种原因我无法得到正确的语句 因为我收到此错误 找不到接受提供的 init 重载 论点 我是这样写的 let button2Alert UIAlertView UIAlert
  • 在多个数组中搜索字符串,然后设置 var - jQuery

    我正在寻找基于字符串存在于哪个数组中设置一个变量 例如 var primary red blue yellow var secondary orange purple green 然后检查 purple 并返回它在 secondary 数组
  • 矩阵到数组 C#

    这将是转换方阵的最有效方法 例如 1 2 3 4 5 6 7 8 9 into 1 2 3 4 5 6 7 8 9 in c 我在做 int array2D new int 1 2 3 4 5 6 7 8 9 int array1D new
  • iOS 13 检查 CLLocationManager 的临时授权状态

    根据 WWDC 视频 https developer apple com videos play wwdc2019 705 https developer apple com videos play wwdc2019 705 当你要求 Al

随机推荐

  • php 的 mysql_real_escape_string() 的等效 JavaScript 代码是什么?

    等效的 javascript 代码是什么mysql real escape string 基于PHP 文档 http php net manual en function mysql real escape string php该方法的作用
  • 如何在 felm() 函数之后绘制交互的边际效应

    我基于具有一堆单位固定效应的 巨大 面板数据进行了回归 所以我使用了包 lfe 中的函数 felm 此外 我在回归中有两个连续变量的交互项 但是 当绘制 x 对 y 的边际效应如何随 x2 变化时 felm 生成的对象似乎通常与大多数绘图函
  • 为什么我使用 context().method() 违反了状态图断言?

    我已经为一个项目开发了一些概念代码 我很快就会从事该项目 该项目适合于状态机设计 我认为 boost statechart 会做得很好 然而 当我尝试使用 context 时 我遇到了障碍 这是一个示例 我很乐意提供更多代码 但我认为这是相
  • 如何“安全”地使用 window.history.pushState

    我想使用window history pushState 支持浏览器的功能 不幸的是我在 Firefox 上遇到错误 类型错误 history pushState 不是函数 如何才能避免这种情况呢 虽然我没有在 JavaScript 中测试
  • Swift 1.2 中的可变@autoclosure ?

    现在 autoclosure是参数声明的一部分而不是类型 如何声明函数采用可变数量的自动闭包 Before public func coalesce
  • matplotlib show() 不能工作两次

    我有一个奇怪的问题 与 matplotlib 有关 如果我运行这个程序 我可以多次打开和关闭同一个图形 import numpy from pylab import figure show X numpy random rand 100 1
  • 高流量网站的 Facebook 身份验证:空访问令牌、空 /me

    目前 我们有一个在 Facebook 选项卡上运行的应用程序 该应用程序收到了大量流量 每隔几秒钟就有人注册 而且大多数都成功了 但是我遇到了以下问题 根本没有收到访问令牌 空响应 没有错误 或者如果收到 则对 me 的 API 调用失败
  • 检测 iOS UIDevice 方向

    我需要检测设备何时处于纵向 以便我可以发出特殊的动画 但我不希望我的视图自动旋转 当设备旋转为纵向时 如何覆盖自动旋转的视图 我的应用程序只需要以横向显示它的视图 但如果我希望能够检测到纵向旋转 我似乎也需要支持纵向 尝试在应用程序加载或视
  • 将 JSONB 转换为缩小(无空格)字符串

    如果我转换一个文本值 例如 a b 到 JSONB 然后返回到文本空格 之间添加 和 psql gt select a b jsonb text text a b 1 row 如何将文本转换为 jsonb 以便我可以使用 jsonb 函数
  • C#中如何检查字符串的最后一个字符?

    我想在 C 中找到字符串的最后一个字符 然后将其放入if陈述 然后 如果最后一个字符等于 A B 或 C 则应执行某个操作 C 中如何获取字符串的最后一个字符 Use the EndsWith 字符串方法 if string EndsWit
  • PHP:“即时”向电子邮件添加附件?

    我刚刚让PHP的邮件功能在我的测试环境中正常工作 我有一个输出许多字符串的 PHP 应用程序 将这些字符串转换为附件真是太好了 TXT 文件 在电子邮件中 无需先将它们存储在磁盘上并重新读回 这在 PHP 中可能吗 是的 这是可能的 您只需
  • 包含任何内容 ([_]) 和任何内容 (_) 的列表有什么区别

    我试图完成以下任务 如果我有两个列表 L1 和 L2 我希望结果 R 是 L1 中 L2 的 减法 Example L1 1 2 3 L2 2 3 4 5 R 1 我能够做到这一点 但我不知道两者之间有什么区别 and 如果我这样做 dif
  • 在 R 中的点阵图例图中包含线和点

    大家好 我正在处理格子图 一切正常 但我在图例方面遇到了一些麻烦 我在用xyplot 而且效果非常棒 我的数据框是NM I add dput 最后部分的版本 AMes A2009 A2010 A2011 A2012 A2013 A2014
  • 开关参数和powershell.exe -File参数

    据微软称 在极少数情况下 您可能需要为开关参数提供布尔值 要为 File 参数值中的开关参数提供布尔值 请将参数名称和值括在大括号中 如下所示 File Get Script ps1 All False 我有一个简单的脚本 CmdletBi
  • 如何使用 dask 和特定 AWS 配置文件从 s3 读取镶木地板文件

    如何使用 s3 读取 parquet 文件dask以及特定的 AWS 配置文件 存储在凭证文件中 达斯克用途s3fs它使用boto 这是我尝试过的 gt gt gt import os gt gt gt import s3fs gt gt
  • CakePHP - $hasMany 模型中的订单被忽略

    我有一个具有 hasMany 属性的模型 如果我只有以下内容 var hasMany OtherModel 在 OtherModel 扩展 AppModel 类中 我有以下内容 var order colour id DESC 该顺序被忽略
  • 一个域名有可能有多个对应的IP地址吗?

    例如 当我们连接到www example com 首先我们尝试连接到192 0 2 1 如果第一次尝试失败 那么我们会尝试192 0 2 222 是否可以 一个域名可以注册多个备份IP吗 这是循环 DNS 这是一个非常简单的负载平衡解决方案
  • 是否可以使用 BeautifulSoup 只获取没有类或 id 的标签?

    我有数千个 HTML 网站 我正在尝试过滤这些网站中的文本 我正在用漂亮的汤来做这个 get text 从这些网站给我提供了很多不必要的信息 因此我写了一个循环 l for line in text5 soup bs line html p
  • Excel 宏一次将一行连接到文件末尾

    我需要一个 Excel 宏来连接每行的七列数据 直到到达数据末尾 例如 如果我有这样的公式 A1 B1 C1 D1 E1 F1 G1 如何编写宏 以便它按这样的顺序递增到文件末尾的每一行 A1 B1 C1 D1 E1 F1 G1 A2 B2
  • 为什么 Swift 迭代器比数组构建慢?

    这有点与这个问题 https stackoverflow com questions 40669193 explain swift iterators 40672317 40672317 其中假设使用生成器 迭代器 遍历嵌套数组对于迭代元素