将两个string转换为一个布尔值数组的快速方法是什么?

我有一个很长的string(有时超过1000个字符),我想转换为一个布尔值数组。 而且它需要非常快速地做很多次。

let input: String = "001" let output: [Bool] = [false, false, true] 

我天真的尝试是这样的:

 input.characters.map { $0 == "1" } 

但是这比我想要的要慢很多。 我的分析表明, map是经济放缓的地方,但我不知道我能做多less简单。

我觉得在没有Swift / ObjC的开销的情况下,这样做会很快。 在C中,我认为这是一个简单for循环,其中一个字节的内存与一个常量进行比较,但我不知道我应该看什么函数或语法。

有没有办法做得更快?

更新:

我也试过了

 output = [] for char in input.characters { output.append(char == "1") } 

它快了大约15%。 我希望比这更多。

这是更快的:

 // Algorithm 'A' let input = "0101010110010101010" var output = Array<Bool>(count: input.characters.count, repeatedValue: false) for (index, char) in input.characters.enumerate() where char == "1" { output[index] = true } 

更新:在input = "010101011010101001000100000011010101010101010101"

0.0741 / 0.0087,这种方法比作者的8.46倍更快。 更大的数据相关性更积极。

此外,使用nulTerminatedUTF8速度稍微增加,但并不总是速度高于algorithmA

 // Algorithm 'B' let input = "10101010101011111110101000010100101001010101" var output = Array<Bool>(count: input.nulTerminatedUTF8.count, repeatedValue: false) for (index, code) in input.nulTerminatedUTF8.enumerate() where code == 49 { output[index] = true } 

在结果图中出现,input长度为2196 ,其中第一个和最后一个0..1,A – 秒,B – 第三个点。 A :0.311秒, B :0.304秒

算法比较图

 import Foundation let input:String = "010101011001010101001010101100101010100101010110010101010101011001010101001010101100101010100101010101011001010101001010101100101010100101010" var start = clock() var output = Array<Bool>(count: input.nulTerminatedUTF8.count, repeatedValue: false) var index = 0 for val in input.nulTerminatedUTF8 { if val != 49 { output[index] = true } index+=1 } var diff = clock() - start; var msec = diff * 1000 / UInt(CLOCKS_PER_SEC); print("Time taken \(Double(msec)/1000.0) seconds \(msec%1000) milliseconds"); 

这应该是非常快的。 试试看。 对于010101011010101001000100000011010101010101010101它需要0.039秒。

我想这是尽可能快的:

 let targ = Character("1") let input: String = "001" // your real string goes here let inputchars = Array(input.characters) var output:[Bool] = Array.init(count: inputchars.count, repeatedValue: false) inputchars.withUnsafeBufferPointer { inputbuf in output.withUnsafeMutableBufferPointer { outputbuf in var ptr1 = inputbuf.baseAddress var ptr2 = outputbuf.baseAddress for _ in 0..<inputbuf.count { ptr2.memory = ptr1.memory == targ ptr1 = ptr1.successor() ptr2 = ptr2.successor() } } } // output now contains the result 

原因在于,由于使用了缓冲区指针,我们只需循环访问连续的内存,就像循环访问C数组一样,通过增加其指针。 因此,一旦我们经过了最初的设置,这应该和C中一样快

编辑在实际testing中,OP的原始方法和这一个之间的时差是两者之间的差异

 13.3660290241241 

 0.219357967376709 

这是一个相当戏剧性的加速。 然而,我急于补充,我已经排除了时间testing的初始设置。 这一行:

 let inputchars = Array(input.characters) 

…特别贵。

这应该比enumerate() where char == "1"快一点enumerate() where char == "1"版本(对于500_000交替的0.557s和来自diampiax的1.159salgorithm'A'的零)

 let input = inputStr.utf8 let n = input.count var output = [Bool](count: n, repeatedValue: false) let one = UInt8(49) // 1 for (idx, char) in input.enumerate() { if char == one { output[idx] = true } } 

但是它的可读性也很差; -p

编辑: 两个版本都比地图变种慢,也许你忘了编译优化?

还有一步应该加快速度。 使用reserveCapacity将在循环开始之前调整数组的大小,而不是在循环运行时尝试这样做。

 var output = [Bool]() output.reserveCapacity(input.characters.count) for char in input.characters { output.append(char == "1") } 

使用withCString(_:)来检索一个原始的UnsafePointer<Int8> 。 迭代,比较49(ASCII值为"1" )。

怎么样更实用的风格? 这不是最快的(47毫秒),今天肯定…

 import Cocoa let start = clock() let bools = [Bool](([Character] ("010101011001010101001010101100101010100101010110010101010101011001010101001010101100101010100101010101011001010101001010101100101010100101010".characters)).map({$0 == "1"})) let msec = (clock() - start) * 1000 / UInt(CLOCKS_PER_SEC); print("Time taken \(Double(msec)/1000.0) seconds \(msec%1000) milliseconds"); 

我需要进行一些testing,但是我认为包括原始地图在内的许多方法的一个问题是,他们需要迭代string来计算字符数,然后第二次实际处理字符数。

你有没有尝试过:

 let output = [Bool](input.characters.lazy.map { $0 == "1" }) 

这可能只能做一次迭代。

另一个可以加快速度的方法是避免使用string,而是使用适当编码的字符数组(特别是如果是更大的固定长度单位(如UTF16或ASCII),那么长度查找就是O(1)而不是O(n),迭代也可能更快

顺便说一句,在优化器启用的情况下总是testing性能,从来没有在游乐场中testing性能,因为性能特征是完全不同的,有时是100倍。