【问题标题】:How to reduce execution time of the while-loop after substitution of the ++ operator in swift?如何在swift中替换++运算符后减少while循环的执行时间?
【发布时间】:2016-07-11 09:21:39
【问题描述】:

新的 Xcode (7.3) 警告增量运算符 ++ 将在下一个 swift3 中删除。我有 ++ 的 while 循环(见下文)。删除增量运算符 ++ 后,此循环比使用 ++ 时慢三倍。更改此 while 循环的更好方法是什么?

之前

 var first = string1.startIndex
 var last = first.advancedBy(n, limit: string1.endIndex)
 var myarray = [String]()

 while last != string1.endIndex
 {
     myarray.append(string1[first++...last++])
 } 

之后

 var first = string1.startIndex
 var last = first.advancedBy(n, limit: string1.endIndex)
 var myarray = [String]()

 while last != string1.endIndex
 {
    myarray.append(string1[first...last])

    first = first.advancedBy(1) // or first = first.successor()
    last = first.advancedBy(n, limit: string1.endIndex) // or  last = last.successor()

 }

【问题讨论】:

  • 当您使用last = last.successor() 而不是last = first.advancedBy(n, limit: string1.endIndex) 时,您确定会看到性能下降吗?对于较大的 n 值,前者应该更快。
  • @Ole Begemann 是的,我已经通过使用 - let date_start = NSDate() - 执行代码 - print("(-date_start.timeIntervalSinceNow)") 对其进行了测试。

标签: swift while-loop increment


【解决方案1】:

使用预分配和随机访问分配,而不是重复地将元素附加到数组中

添加first = ...last = ... 行不会像重复附加 元素(并因此重复扩展您的数组)到尚未预分配的数组那样影响性能.如果您初始化数组 myarray 并使用随机访问 (myarray[i]) 分配滑动窗口字符串值,而不是使用 .append(...),您应该能够减少开销。

var first = string1.startIndex
var last = first.advancedBy(n, limit: string1.endIndex)
var myarray = [String](count: string1.characters.count-n,
                       repeatedValue: "")

for i in 0..<myarray.count {
    myarray[i] = string1[first...last]

    first = first.advancedBy(1)
    last = first.advancedBy(n, limit: string1.endIndex)
}

基准测试

我已使用以下示例 string1n 将上述修改后的解决方案与您自己的解决方案进行对比:

var string1 = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce pretium diam at arcu suscipit, sed molestie mi rhoncus. Donec neque sapien, luctus ultricies ex sed, tristique auctor sem. Praesent varius ullamcorper nisi a commodo. Mauris eu rutrum enim, sit amet hendrerit velit. Curabitur iaculis neque ut tellus pretium, at convallis odio posuere. Integer ultricies diam ex, sit amet faucibus odio ultrices eu. Morbi tincidunt felis tellus, eget gravida est suscipit quis. Nam nunc ipsum, molestie sit amet neque at, aliquam maximus lacus. Sed bibendum massa a lorem eleifend, non eleifend felis commodo. Vivamus eget sem luctus, aliquam dolor nec, finibus diam. Morbi id justo vehicula, eleifend arcu nec, hendrerit neque. Donec ex ante, fringilla id rhoncus nec, porta id tortor. Donec dapibus nisl nibh, non posuere lacus egestas non. Duis massa quam, elementum sit amet volutpat eget, tincidunt sodales urna. Donec at congue ligula, a sollicitudin tortor."
string1 = string1+string1+string1 // 2880 characters
let n = 4

基准测试结果(使用this QuartzCore-based function 测量)

  • 带有for i in ...循环的预初始化数组解决方案:2.5s
  • .append(..) 带有while 循环的解决方案,如您的问题所示:32.5s

如果执行时间很重要,显然您会受益于预先初始化数组并使用随机访问分配而不是重复使用.append(..)


最后,您可以问自己是否真的需要显式存储滑动窗口中的所有子字符串,或者是否足以存储与它们对应的范围。如果n 很大,上面的myarray 将包含大量重复字符/重叠子字符串。在这种情况下,您最好只保存一个Range&lt;String.CharacterView.Index&gt; 元素数组,它可以很容易地用于即时提取string1 的相关子字符串。例如:

var first = string1.startIndex
var last = first.advancedBy(n, limit: string1.endIndex)
var rangeArr = [Range<String.CharacterView.Index>](
        count: string1.characters.count-n,
        repeatedValue: first...last)

for i in 0..<rangeArr.count {
    rangeArr[i] = first...last

    first = first.advancedBy(1)
    last = first.advancedBy(n, limit: string1.endIndex)
}

【讨论】:

  • 太好了,这对我来说是非常有用的建议!我已经开始编写生物信息学代码,以处理大序列。
  • @VYT 很高兴为您提供帮助。我将添加关于实际显式存储所有子字符串的附加说明。
  • 存储范围而不是字符串是另一个好主意,它特别适用于全基因组分析。能学到好东西真是太好了。谢谢!
  • @VYT 最后我会提到,奇怪的是,字符串的范围实际上比字符串本身更大(80 字节)(24 字节),(参见sizeof(String)sizeof(Range&lt;String.CharacterView.Index&gt;)) ,因此取决于内存是否也有限,也许实际存储字符串是更喜欢的(即使存储范围可能感觉“更整洁”)。祝你的项目好运!
  • @VYT 最后,题外话。这个问题,但w.r.t.您对基因组的评论:如果此问答涵盖了存储基因组序列,那么使用字符串(每个字符串 24 字节)可能是多余的:因为基因组只能接受字母 A, C, G, T,您实际上可以将任何这样的 4 个字母组合存储在一个 ( unsigned) byte:字母组成一个base-4,“4个位置”的base-4正好是256,即一个字节可表示的不同状态的个数(2^8)。你可以使用字典。将这些 0->255 状态重新转换为 corr。字母组合。然而,再一次,有点跑题了:)
【解决方案2】:

将增量从 first++ 更改为 first += 1 - 这是关于已弃用版本的原则

但是如果进行深度搜索,你会发现increment over String.CharacterView.Index 是实现了自定义后缀运算符,其中接受泛型与_Incrementable 协议,其中声明了successor 方法用于返回下一个链值。 为什么++ 比直接successor 更快——谁知道呢,显然一些优化正在开发中,并且在 Swift 3 中会有更好的速度。

【讨论】:

  • 如果我使用 first += 1 而不是 first.advancedBy(1) 它不起作用,因为 += 没有应用于索引。
  • 嗯,所以,只是等待改进的时间......也许是也许不是,不是一个好方法
【解决方案3】:

有什么原因不能将增量放在单独的行上?

while last != string1.endIndex
 {
     first += 1
     last += 1
     myarray.append(string1[first...last])
 } 

【讨论】:

  • 第一个 += 1 和最后一个 += 1 不适用于索引
【解决方案4】:

为什么不使用for循环而不是while,然后直接使用索引访问。

var myarray = [String]()
for var i in string1.startIndex ... string1.endIndex {
    myarray.append(string1[i])
}

希望对您有所帮助。

【讨论】:

  • 忘记for var i; i&lt;j; j++,改用for .. in
  • 哈哈。从一个已弃用的解决方案转移到另一个已弃用的解决方案:)
  • 是的,它应该是一个 for in 循环来保持快速。将编辑我的代码
猜你喜欢
  • 2021-08-15
  • 1970-01-01
  • 1970-01-01
  • 2018-02-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-31
  • 2019-10-07
相关资源
最近更新 更多