Tengo una matriz con muchos millones de elementos (puntos de datos de 7201 x 7201) donde convierto los datos en una imagen en escala de grises.
var imagePixels = heights.map { terrainToColorScale($0.height, gradient: .Linear) } let _ = writeImageToFile(imagePixels, height: latStrides, width: lonStrides, imgColorSpace: .BW, to: imageURL, as: .png)Este fragmento de código tarda unos 11 segundos en completarse (CPU = 2,3 Ghz 8-Core i9), pero me gustaría obtener un mejor rendimiento si es posible. El código se está ejecutando actualmente en un solo subproceso.
¿Simplemente dividir mi matriz de heights en fragmentos (digamos 100 fragmentos) y ejecutar un grupo de tareas con una tarea para cada fragmento obtendría una mejora decente? ¿O estoy buscando entrar en Metal y sombreadores (¡¡No sé nada sobre Metal!!) para obtener un mejor resultado?
Solo por interés, la imagen típica generada es... (Imagen reducida porque sería demasiado grande para cargarla aquí). 
Actualización: Adición de código asociado con terrainToColorScale Básicamente, para una conversión lineal, tomará la altura del terreno (típicamente 0...9000) y la escalará para devolver un valor entre 0...255 También tengo implementaciones no lineales (no se muestra a continuación) que mostrarán mayor detalle para los conjuntos de datos que son en su mayoría elevaciones de terreno bajas/altas.
let terrainLowerCutOff: Double = 0.0 // Mean Sea Level let terrainUpperCutOff: Double = 9000.0 // Value in meters, just higher that Everest func terrainToColorScale(_ elev: Double, lowerCutOff: Double = terrainLowerCutOff, upperCutOff: Double = terrainUpperCutOff, gradient: ImageColorGradient = .Linear) -> UInt8 { switch gradient { case .Linear: return linearColorScale(elev, lowerCutOff: lowerCutOff, upperCutOff: upperCutOff) case .LinearInverse: return linearInverseColorScale(elev, lowerCutOff: lowerCutOff, upperCutOff: upperCutOff) case .CurveEmphasiseLows: return reciprocalPowerColorScale(elev, lowerCutOff: lowerCutOff, upperCutOff: upperCutOff) case .CurveEmphasiseLowsInverse: return reciprocalInversePowerColorScale(elev, lowerCutOff: lowerCutOff, upperCutOff: upperCutOff) case .CurveEmphasiseHighs: return powerColorScale(elev, lowerCutOff: lowerCutOff, upperCutOff: upperCutOff) case .CurveEmphasiseHighsInverse: return powerInverseColorScale(elev, lowerCutOff: lowerCutOff, upperCutOff: upperCutOff) } } fileprivate func linearColorScale(_ value: Double, lowerCutOff: Double, upperCutOff: Double) -> UInt8 { return UInt8( 255 * normaliseColorScale(value, lowerCutOff: lowerCutOff, upperCutOff: upperCutOff) ) } fileprivate func normaliseColorScale(_ value: Double, lowerCutOff: Double, upperCutOff: Double) -> Double { switch value { case _ where value <= lowerCutOff : return 0.0 case _ where value >= upperCutOff : return 1.0 default : return (value - lowerCutOff) / (upperCutOff - lowerCutOff) } }Esta no es una respuesta completa a su pregunta, pero creo que debería darle un punto de partida sobre dónde ir. vDSP es parte de Accelerate y está diseñado para acelerar las operaciones matemáticas en arreglos. Este código usa varios pasos, por lo que probablemente podría optimizarse más, y no tiene en cuenta ningún otro filtro que no sea lineal, pero no tengo suficiente conocimiento para hacer que los pasos sean más efectivos. Sin embargo, en mi máquina, vDSP es 4 veces más rápido que map para el siguiente procesamiento:
import Foundation import Accelerate let count = 7200 * 7200 var date = Date() print("Generating") let test: [CGPoint] = (0..<count).map { CGPoint(x: $0, y: Int.random(in: -2000...10000)) } print("Generating took \(Date().timeIntervalSince(date))") date = Date() print("Mapping") let heights: [Float] = test.map { Float($0.y) } print("Mapping took \(Date().timeIntervalSince(date))") date = Date() print("Converting via vDSP") let clipped = vDSP.clip(heights, to: 0...9000) let scaled = vDSP.divide(clipped, 9000) let multiplied = vDSP.multiply(255, scaled) let integers = vDSP.floatingPointToInteger(multiplied, integerType: UInt8.self, rounding: .towardNearestInteger) print("Converting via DSP took \(Date().timeIntervalSince(date))") date = Date() print("Converting via map") let mappedIntegers = heights.map { height -> UInt8 in let clipped: Float if height < 0 { clipped = 0 } else if height > 9000 { clipped = 9000 } else { clipped = height } let scaled = clipped / 9000 let multiplied = scaled * 255 return UInt8(multiplied.rounded()) } print("Converting via map took \(Date().timeIntervalSince(date))")