Ver código fonte

Improve performance of MD5 and SHA by replacing generics with functions mostly.

Marcin Krzyżanowski 10 anos atrás
pai
commit
34919ba374

+ 28 - 2
CryptoSwift.xcodeproj/xcshareddata/xcbaselines/754BE45F19693E190098E6F3.xcbaseline/85434D17-7706-4DE9-AFA7-B1DD0BD9FA39.plist

@@ -21,7 +21,7 @@
 				<key>com.apple.XCTPerformanceMetric_WallClockTime</key>
 				<dict>
 					<key>baselineAverage</key>
-					<real>0.624</real>
+					<real>0.571</real>
 					<key>baselineIntegrationDisplayName</key>
 					<string>Local Baseline</string>
 				</dict>
@@ -31,7 +31,33 @@
 				<key>com.apple.XCTPerformanceMetric_WallClockTime</key>
 				<dict>
 					<key>baselineAverage</key>
-					<real>0.5895</real>
+					<real>0.535</real>
+					<key>baselineIntegrationDisplayName</key>
+					<string>Local Baseline</string>
+				</dict>
+			</dict>
+		</dict>
+		<key>ChaCha20Tests</key>
+		<dict>
+			<key>testChaCha20Performance()</key>
+			<dict>
+				<key>com.apple.XCTPerformanceMetric_WallClockTime</key>
+				<dict>
+					<key>baselineAverage</key>
+					<real>0.176</real>
+					<key>baselineIntegrationDisplayName</key>
+					<string>Local Baseline</string>
+				</dict>
+			</dict>
+		</dict>
+		<key>CryptoSwiftTests</key>
+		<dict>
+			<key>testMD5PerformanceSwift()</key>
+			<dict>
+				<key>com.apple.XCTPerformanceMetric_WallClockTime</key>
+				<dict>
+					<key>baselineAverage</key>
+					<real>0.087</real>
 					<key>baselineIntegrationDisplayName</key>
 					<string>Local Baseline</string>
 				</dict>

+ 2 - 2
CryptoSwift/AES.swift

@@ -125,7 +125,7 @@ final public class AES {
     private func encryptBlock(block:[UInt8]) -> [UInt8]? {
         let rounds = self.variant.Nr
         let rk = self.expandedKey
-        var b = block[block.startIndex..<block.endIndex].toUInt32Array()
+        var b = toUInt32Array(block[block.startIndex..<block.endIndex])
 
         var t = [UInt32](count: 4, repeatedValue: 0)
         
@@ -192,7 +192,7 @@ final public class AES {
     private func decryptBlock(block:[UInt8]) -> [UInt8]? {
         let rounds = self.variant.Nr
         let rk = expandedKeyInv
-        var b = block[block.startIndex..<block.endIndex].toUInt32Array()
+        var b = toUInt32Array(block[block.startIndex..<block.endIndex])
 
         var t = [UInt32](count: 4, repeatedValue: 0)
         

+ 8 - 7
CryptoSwift/ArraySlice<UInt8>+Bytes.swift

@@ -5,18 +5,17 @@
 //  Created by Marcin Krzyzanowski on 27/09/15.
 //  Copyright © 2015 Marcin Krzyzanowski. All rights reserved.
 //
+//  Disabled because performance is bad. The cost of generic approach is to big.
 
+/*
 extension ArraySlice where Element: _UInt8Type {
-    
+
     func toUInt32Array() -> Array<UInt32> {
         var result = Array<UInt32>()
-        result.reserveCapacity(self.count / sizeof(UInt32))
+        result.reserveCapacity(16)
+        
         for idx in self.startIndex.stride(to: self.endIndex, by: sizeof(UInt32)) {
-            var val:UInt32 = 0
-            val |= UInt32(self[idx.advancedBy(3)] as! UInt8) << 24
-            val |= UInt32(self[idx.advancedBy(2)] as! UInt8) << 16
-            val |= UInt32(self[idx.advancedBy(1)] as! UInt8) << 8
-            val |= UInt32(self[idx.advancedBy(0)] as! UInt8) << 0
+            let val:UInt32 = (UInt32(self[idx.advancedBy(3)] as! UInt8) << 24) | (UInt32(self[idx.advancedBy(2)] as! UInt8) << 16) | (UInt32(self[idx.advancedBy(1)] as! UInt8) << 8) | UInt32(self[idx] as! UInt8)
             result.append(val)
         }
         return result
@@ -24,6 +23,7 @@ extension ArraySlice where Element: _UInt8Type {
     
     func toUInt64Array() -> Array<UInt64> {
         var result = Array<UInt64>()
+        result.reserveCapacity(32)
         for idx in self.startIndex.stride(to: self.endIndex, by: sizeof(UInt64)) {
             var val:UInt64 = 0
             val |= UInt64(self[idx.advancedBy(7)] as! UInt8) << 56
@@ -39,3 +39,4 @@ extension ArraySlice where Element: _UInt8Type {
         return result
     }
 }
+*/

+ 2 - 1
CryptoSwift/MD5.swift

@@ -42,6 +42,7 @@ final class MD5 : HashProtocol  {
     
     func calculate() -> [UInt8] {
         var tmpMessage = prepare(64)
+        tmpMessage.reserveCapacity(tmpMessage.count + 4)
 
         // initialize hh with hash values
         var hh = h
@@ -55,7 +56,7 @@ final class MD5 : HashProtocol  {
         let chunkSizeBytes = 512 / 8 // 64
         for chunk in BytesSequence(chunkSize: chunkSizeBytes, data: tmpMessage) {
             // break chunk into sixteen 32-bit words M[j], 0 ≤ j ≤ 15
-            var M = chunk.toUInt32Array()
+            var M = toUInt32Array(chunk)
             assert(M.count == 16, "Invalid array")
             
             // Initialize hash value for this chunk:

+ 1 - 1
CryptoSwift/SHA1.swift

@@ -36,7 +36,7 @@ final class SHA1 : HashProtocol {
                 case 0...15:
                     let start = chunk.startIndex + (x * sizeofValue(M[x]))
                     let end = start + sizeofValue(M[x])
-                    let le = chunk[start..<end].toUInt32Array()[0]
+                    let le = toUInt32Array(chunk[start..<end])[0]
                     M[x] = le.bigEndian
                     break
                 default:

+ 4 - 2
CryptoSwift/SHA2.swift

@@ -6,6 +6,8 @@
 //  Copyright (c) 2014 Marcin Krzyzanowski. All rights reserved.
 //
 
+import simd
+
 final class SHA2 : HashProtocol {
     var size:Int { return variant.rawValue }
     let variant:SHA2.Variant
@@ -136,7 +138,7 @@ final class SHA2 : HashProtocol {
                 case 0...15:
                     let start = chunk.startIndex + (x * sizeofValue(M[x]))
                     let end = start + sizeofValue(M[x])
-                    let le = chunk[start..<end].toUInt32Array()[0]
+                    let le = toUInt32Array(chunk[start..<end])[0]
                     M[x] = le.bigEndian
                     break
                 default:
@@ -219,7 +221,7 @@ final class SHA2 : HashProtocol {
                 case 0...15:
                     let start = chunk.startIndex + (x * sizeofValue(M[x]))
                     let end = start + sizeofValue(M[x])
-                    let le = chunk[start..<end].toUInt64Array()[0]
+                    let le = toUInt64Array(chunk[start..<end])[0]
                     M[x] = le.bigEndian
                     break
                 default:

+ 29 - 0
CryptoSwift/Utils.swift

@@ -38,6 +38,35 @@ func reverseBytes(value: UInt32) -> UInt32 {
     return ((value & 0x000000FF) << 24) | ((value & 0x0000FF00) << 8) | ((value & 0x00FF0000) >> 8)  | ((value & 0xFF000000) >> 24);
 }
 
+func toUInt32Array(slice: ArraySlice<UInt8>) -> Array<UInt32> {
+    var result = Array<UInt32>()
+    result.reserveCapacity(16)
+    
+    for idx in slice.startIndex.stride(to: slice.endIndex, by: sizeof(UInt32)) {
+        let val:UInt32 = (UInt32(slice[idx.advancedBy(3)]) << 24) | (UInt32(slice[idx.advancedBy(2)]) << 16) | (UInt32(slice[idx.advancedBy(1)]) << 8) | UInt32(slice[idx])
+        result.append(val)
+    }
+    return result
+}
+
+func toUInt64Array(slice: ArraySlice<UInt8>) -> Array<UInt64> {
+    var result = Array<UInt64>()
+    result.reserveCapacity(32)
+    for idx in slice.startIndex.stride(to: slice.endIndex, by: sizeof(UInt64)) {
+        var val:UInt64 = 0
+        val |= UInt64(slice[idx.advancedBy(7)]) << 56
+        val |= UInt64(slice[idx.advancedBy(6)]) << 48
+        val |= UInt64(slice[idx.advancedBy(5)]) << 40
+        val |= UInt64(slice[idx.advancedBy(4)]) << 32
+        val |= UInt64(slice[idx.advancedBy(3)]) << 24
+        val |= UInt64(slice[idx.advancedBy(2)]) << 16
+        val |= UInt64(slice[idx.advancedBy(1)]) << 8
+        val |= UInt64(slice[idx.advancedBy(0)]) << 0
+        result.append(val)
+    }
+    return result
+}
+
 func xor(a: [UInt8], b:[UInt8]) -> [UInt8] {
     var xored = [UInt8](count: a.count, repeatedValue: 0)
     for i in 0..<xored.count {

+ 1 - 1
CryptoSwiftTests/ExtensionsTest.swift

@@ -78,7 +78,7 @@ final class ExtensionsTest: XCTestCase {
     
     func testtoUInt32Array() {
         let chunk:ArraySlice<UInt8> = [1,1,1,7,2,3,4,5]
-        let result = chunk.toUInt32Array()
+        let result = toUInt32Array(chunk)
         
         XCTAssert(result.count == 2, "Invalid conversion")
         XCTAssert(result[0] == 117506305, "Invalid conversion")

+ 11 - 10
CryptoSwiftTests/HashTests.swift

@@ -72,28 +72,29 @@ final class CryptoSwiftTests: XCTestCase {
     
     func testMD5PerformanceSwift() {
         self.measureMetrics([XCTPerformanceMetric_WallClockTime], automaticallyStartMeasuring: false, forBlock: { () -> Void in
-            let buf = UnsafeMutablePointer<UInt8>(calloc(2048, sizeof(UInt8)))
-            let data = NSData(bytes: buf, length: 2048)
+            let buf = UnsafeMutablePointer<UInt8>(calloc(1024 * 1024, sizeof(UInt8)))
+            let data = NSData(bytes: buf, length: 1024 * 1024)
+            let arr = data.arrayOfBytes()
             self.startMeasuring()
-            Hash.md5(data.arrayOfBytes()).calculate()
+                Hash.md5(arr).calculate()
             self.stopMeasuring()
-            buf.dealloc(1024)
+            buf.dealloc(1024 * 1024)
             buf.destroy()
         })
     }
     
     func testMD5PerformanceCommonCrypto() {
         self.measureMetrics([XCTPerformanceMetric_WallClockTime], automaticallyStartMeasuring: false, forBlock: { () -> Void in
-            let buf = UnsafeMutablePointer<UInt8>(calloc(2048, sizeof(UInt8)))
-            let data = NSData(bytes: buf, length: 2048)
-            self.startMeasuring()
+            let buf = UnsafeMutablePointer<UInt8>(calloc(1024 * 1024, sizeof(UInt8)))
+            let data = NSData(bytes: buf, length: 1024 * 1024)
             let outbuf = UnsafeMutablePointer<UInt8>.alloc(Int(CC_MD5_DIGEST_LENGTH))
-            CC_MD5(data.bytes, CC_LONG(data.length), outbuf)
+            self.startMeasuring()
+                CC_MD5(data.bytes, CC_LONG(data.length), outbuf)
             //let output = NSData(bytes: outbuf, length: Int(CC_MD5_DIGEST_LENGTH));
+            self.stopMeasuring()
             outbuf.dealloc(Int(CC_MD5_DIGEST_LENGTH))
             outbuf.destroy()
-            self.stopMeasuring()
-            buf.dealloc(1024)
+            buf.dealloc(1024 * 1024)
             buf.destroy()
         })
     }