2016-09-07 16 views
3

あなたはそれを信じることができますか? 私はこのようなループを持っています(エラーを許して、たくさんの情報と変数名を書き換えなければなりませんでした。str = str + "abc" str = "abc" + strより遅いか?

...古い例...コード下記参照、

を編集し、私はstr = str + "Blah \(odat.count)"にそれらの中間のstr = "Blah \(odat.count)" + strタイプのラインを変更した場合、UIが停止して磨くと私はカラーホイールを取得します。 NSTextFieldは最初のself.display.string...になりますが、その後フリーズします。

私はマルチスレッドの初心者ですので、私の方法を修正してください。うまくいけば私は何を望むのか分かります。

作業バージョンもちょっと面倒ですが、決して実際にはフリーズすることはできません。ここ

完全実施例である: 典型的な値は、n = 70、VAR3 = 7

EDITです。テキストビュー、進行状況バー、およびボタンをリンクするだけです。主な機能を変更してみてください。

// 
// 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(", "))]" 
    } 
} 
+0

これらのコード行がバックグラウンドスレッド上にある場合、あなたが同時に 'str'に書き込み読み込みをしない限り、あなたのUIはフリーズしません。あなたの問題がどこにあるのかと思うので、 'str'が終了したことを知るまで、' self.display.string = str + "\ n" +(self.display.string ?? "")をディスパッチしないでください。これは、原子が目的のCで便利になった場所です。 – Knight0fDragon

+6

もちろん、それがうまくいくと信じています。しかし、あなたの問題を調査するために、*自己完結型の例が非常に役に立ちます。 –

+0

私には何かがありませんが、実際にtheFuncを呼び出すのは何ですか? – Knight0fDragon

答えて

3

あなたは本当にあなたがそれを信じることができる

以外の質問をされていないため?

私は確かに言うことができますが、真剣に何かをprependingすると、追加するよりも遅く/早くなる場合がたくさんあります。例えばリンクされたリストを取る。リストの最後の要素への参照を保持していない場合、PrependはO(1)、AppendはO(N)です。

私は、その特定の問題の回数だけまとめて、5-6回の実行ではそれほど大きな違いはないようですが、私のマシンではプリペンドはまだ10%遅くなっています。

  • 先頭に追加str = str + newstr
  • str.append(newstr)
  • 使用が良い追記変異させるアキュムレータにstr = newstr + str
  • 追記をアキュムレータに:あなたは基本的にあなたのケースについてスウィフトにおける連結文字列の4つの方法を持っている興味深いのは

    配列を文字列バッファとしてまとめて一度に結合するa = []; a.append(x); str = a.joined(separator: " ")

彼らはすべてに思える私のマシン上で

は、このような典型的なタイミングで、かなりの周りに同じ時間を逸脱:

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 

追加が最速であること。

あなたはGithubの上スウィフトのソースをチェックアウトする場合は、あなたがこの参照してくださいよ https://gist.github.com/ojosdegris/df72a94327d12a67fe65e5989f9dcc53

私の要旨にすべての4例のコードを見ることができます:

@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 
} 

だから何がおそらくアキュムレータとして起こっているの文字列が大きくなると、コピーするほうが高価になります。

+0

シンプルなループがこれを実証して以来、私は自分の実験をXCTestsで行った。私は、ループの大きさを数桁大きくすると、2つの違いがはるかに大きいことが分かりました。追記への切り替えはそれほど良くはなかった。 –

1

ヴィトーレの答えは正しいです。文字列のためのスウィフトのソースコード(stdlib/public/core/String.swift)を見ると、私たちが見ることができます:

をスウィフト内の文字列が値のセマンティクスを持っていますが、文字列がバッファにそのデータを格納するためのコピー・オン・ライト 戦略を使用しています。このバッファは、文字列の異なるコピーによって で共有できます。複数の文字列インスタンスが同じ バッファを使用している場合、文字列のデータは遅延時にのみ の突然変異時にコピーされます。したがって、突然変異操作の任意のシーケンスにおける最初のものは、時間および空間O(n)のコストOであり得る。

文字列の連続した記憶領域がいっぱいになると、新しいバッファが に割り当てられ、データを新しい記憶領域に移動する必要があります。文字列バッファーは、 指数関数的な成長方法を使用して、多くの追加操作で平均化されたときに文字列に定数 の時間演算を追加します。 WikipediaのCopy-on-writeパー

リソースが重複しますが変更されていない場合は、新しいリソースを作成する必要はありません。コピーと元の間でリソースを共有することができます。これを考慮して

str = str + "abc"を行う場合、コンパイラはstrの浅いコピーを実行し、その連続したメモリへ​​を追加しています。一方、str = "abc" + strは、もはや連続したメモリを使用していないので、独自のデータコピーを持つ独立したインスタンスを作成します。

+0

ありがとうございます。私は@Vittoreに正解を授与しました(ありがとうございました!)しかし、あなたは賞金を授与したので、ソースで重要なスウィフトの詳細を渡しました。 –

関連する問題