swift - str = str + "abc"比 str = "abc"+ str 慢?

标签 swift string multithreading grand-central-dispatch

你信吗? 我有一个这样的循环(请原谅任何错误,我不得不大量编辑大量信息和变量名称,相信我它有效)。

...旧示例已删除,请参见下面的代码...

如果我将那些中间的 str = "Blah\(odat.count)"+ str 类型行更改为 str = str + "Blah\(odat.count)" UI 停止了,我得到了色轮。 NSTextField 确实到达了第一个 self.display.string... 但随后卡住了。

我是一个多线程新手,所以请随时纠正我的方法。希望我想要的很清楚。

我不得不承认工作版本也有点卡顿,但从未真正卡住。 典型值为 n = 70,var3 = 7。

编辑:

这是一个完整的示例。只需链接 TextView 、进度条和按钮。尝试在主要功能之间切换。

//
//  Controllers.swift
//
//

import Cocoa

class MainController: NSObject {

    @IBOutlet var display: NSTextView!
    @IBOutlet weak var prog: NSProgressIndicator!

    @IBAction func go1(sender: AnyObject) {
        theRoutine(70)
    }

    @IBAction func go2(sender: AnyObject) {
        theRoutine(50)
    }

    class SomeClass {
        var x: Int
        var y: Int
        var p: Double

        init?(size: Int, pro: Double) {
            x = size
            y = size
            p = pro
        }
    }

    func theRoutine(n: Int) {
        prog.hidden = false
        prog.doubleValue = 0
        prog.maxValue = 7 * 40
        let priority = DISPATCH_QUEUE_PRIORITY_HIGH
        dispatch_async(dispatch_get_global_queue(priority, 0)) {
            self.theFunc(n, var1: 0.06, var2: 0.06, var3: 7)
            self.theFunc(n, var1: 0.1*log(Double(n))/Double(n), var2: 0.3*log(Double(n))/Double(n), var3: 7)
            dispatch_async(dispatch_get_main_queue()) {
                self.prog.hidden = true
                self.appOut("done!")
            }
        }
    }

    //This doesn't
//  func theFunc(n: Int, var1: Double, var2: Double, var3: Int) {
//      var m: AnEnum
//      var gra: SomeClass
//      var p = var1
//      for _ in 0...(var3 - 1) {
//          var str  = "blah \(p)\n"
//          for _ in 1...20 {
//              gra = SomeClass(size: n, pro: p)!
//              m = self.doSomething(gra)
//              switch m {
//              case .First(let dat):
//                  str = str + "Blah:\n\(self.arrayF(dat, transform: {"blah\($0)blah\($1)=blah"}))" + "\n\n" + str
//              case .Second(let odat):
//                  str = str + "Blah\(odat.count) blah\(self.arrayF(odat, transform: {"bl\($1)"}))" + "\n\n" + str
//              }
//              dispatch_async(dispatch_get_main_queue()) {
//                  self.prog.incrementBy(1)
//              }
//          }
//          dispatch_async(dispatch_get_main_queue()) {
//              // update some UI
//              self.display.string = str + "\n" + (self.display.string ?? "")
//          }
//          p += var2
//      }
//  }

    //This works
    func theFunc(n: Int, var1: Double, var2: Double, var3: Int) {
        var m: AnEnum
        var gra: SomeClass
        var p = var1
        for _ in 0...(var3 - 1) {
            var str  = "blah \(p)\n"
            for _ in 1...20 {
                gra = SomeClass(size: n, pro: p)!
                m = self.doSomething(gra)
                switch m {
                case .First(let dat):
                    str = "Blah:\n\(self.arrayF(dat, transform: {"blah\($0)blah\($1)=blah"}))" + "\n\n" + str
                case .Second(let odat):
                    str = "Blah\(odat.count) blah\(self.arrayF(odat, transform: {"bl\($1)"}))" + "\n\n" + str
                }
                dispatch_async(dispatch_get_main_queue()) {
                    self.prog.incrementBy(1)
                }
            }
            dispatch_async(dispatch_get_main_queue()) {
                // update some UI
                self.display.string = str + "\n" + (self.display.string ?? "")
            }
            p += var2
        }
    }

    func doSomething(G: SomeClass) -> AnEnum {
        usleep(30000)
        if drand48() <= G.p {
            return AnEnum.First([0, 0])
        } else {
            return AnEnum.Second([1, 1, 1])
        }
    }

    enum AnEnum {
        case First([Int])
        case Second([Int])
    }

    func appOut(out: String?) {
        if out != nil {
            display.string = out! + "\n\n" + (display.string ?? "")
        }
    }

    func arrayF(array: [Int], transform: (index: Int, value: Int) -> String) -> String {
        let arr = Array(0...(array.count - 1))
        return "[\(arr.map{transform(index: $0, value: array[$0])}.joinWithSeparator(", "))]"
    }
}

最佳答案

因为除了

之外,你并没有真正问其他问题

Can you believe it?

我会告诉你我当然可以,但说真的,在很多情况下,前置某些东西可能比追加慢/快。以链表为例。如果您没有持有对列表最后一个元素的引用,则 Prepend 是 O(1) 并且 Append 是 O(N)。

我将要点放在一起,只是对那个特定问题进行计时,并且在 5-6 次运行中它似乎没有显着差异,但是在我的机器上 prepend 仍然慢 10%。

有趣的是,对于您的情况,您基本上有 4 种在 Swift 中连接字符串的方法:

  • 添加到累加器 str = newstr + str
  • 追加到累加器 str = str + newstr
  • 追加变异str.append(newstr)
  • 使用好旧的数组作为字符串缓冲区并一次加入所有内容 a = []; a.追加(x); str = a.joined(分隔符: "")

在我的机器上,它们似乎几乎同时偏离,典型的时间如下:

prepend
real    0m0.082s
user    0m0.060s
sys 0m0.018s

append
real    0m0.070s
user    0m0.049s
sys 0m0.018s

append mutate
real    0m0.075s
user    0m0.054s
sys 0m0.019s

join
real    0m0.086s
user    0m0.064s
sys 0m0.020s

追加最快。

您可以在我的要点中看到所有四种情况的代码 https://gist.github.com/ojosdegris/df72a94327d12a67fe65e5989f9dcc53

如果您查看 Github 上的 Swift 源代码,您会看到:

@effects(readonly)
@_semantics("string.concat")
public static func + (lhs: String, rhs: String) -> String {
 if lhs.isEmpty {
  return rhs
 }
 var lhs = lhs
 lhs._core.append(rhs._core)
 return lhs
}

所以当累加器字符串增长时可能发生的事情是复制它的成本更高。

关于swift - str = str + "abc"比 str = "abc"+ str 慢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39376005/

相关文章:

c# - Asp.net -- C# 中的多线程

JSONSerialisation API 不同的 url 不工作

swift - 如何将 MONActivityView 方法转换为 Swift

swift - 展开可选值时为零

ios - 引号在数组中丢失

javascript - 计算数组中出现次数并获取最高值的简单方法(词袋)

java - 当两个条件对象分配给锁定对象时发生死锁

ios - 谁的 View 不在窗口层次结构中? swift

mysql - 如何转义 "<"?

java - 将值放在 ConcurrentHashMap 中是否是原子的?