提交 1c07002d 编写于 作者: R Ray Liu 提交者: GitHub

Merge pull request #1475 from codeWorm2015/develop

use mps
...@@ -61,6 +61,7 @@ public class MobileNet: Net{ ...@@ -61,6 +61,7 @@ public class MobileNet: Net{
inputDim = Dim.init(inDim: [1, 224, 224, 3]) inputDim = Dim.init(inDim: [1, 224, 224, 3])
metalLoadMode = .LoadMetalInCustomMetalLib metalLoadMode = .LoadMetalInCustomMetalLib
metalLibPath = Bundle.main.path(forResource: "paddle-mobile-metallib", ofType: "metallib") metalLibPath = Bundle.main.path(forResource: "paddle-mobile-metallib", ofType: "metallib")
useMPS = true
} }
} }
...@@ -25,6 +25,7 @@ public class YoloNet: Net { ...@@ -25,6 +25,7 @@ public class YoloNet: Net {
inputDim = Dim.init(inDim: [1, 416, 416, 3]) inputDim = Dim.init(inDim: [1, 416, 416, 3])
metalLoadMode = .LoadMetalInCustomMetalLib metalLoadMode = .LoadMetalInCustomMetalLib
metalLibPath = Bundle.main.path(forResource: "paddle-mobile-metallib", ofType: "metallib") metalLibPath = Bundle.main.path(forResource: "paddle-mobile-metallib", ofType: "metallib")
useMPS = true
} }
override public func resultStr(res: [ResultHolder]) -> String { override public func resultStr(res: [ResultHolder]) -> String {
......
...@@ -181,7 +181,7 @@ class ViewController: UIViewController { ...@@ -181,7 +181,7 @@ class ViewController: UIViewController {
modelPickerView.dataSource = self modelPickerView.dataSource = self
threadPickerView.delegate = self threadPickerView.delegate = self
threadPickerView.dataSource = self threadPickerView.dataSource = self
if let image = UIImage.init(named: "test.jpg") { if let image = UIImage.init(named: "00001.jpg") {
selectImage = image selectImage = image
selectImageView.image = image selectImageView.image = image
} else { } else {
......
...@@ -56,6 +56,8 @@ import Foundation ...@@ -56,6 +56,8 @@ import Foundation
/// 输入维度,按照 n h w c 方式传入 /// 输入维度,按照 n h w c 方式传入
@objc public var inputDim: Dim = Dim.init(inDim: []) @objc public var inputDim: Dim = Dim.init(inDim: [])
/// 是否使用 MetalPerformanceShaders 进行运算
@objc public var useMPS: Bool = false
@objc public init(device: MTLDevice, inParamPointer: UnsafeMutableRawPointer, inParamSize:Int, inModelPointer: UnsafeMutableRawPointer, inModelSize: Int) { @objc public init(device: MTLDevice, inParamPointer: UnsafeMutableRawPointer, inParamSize:Int, inModelPointer: UnsafeMutableRawPointer, inModelSize: Int) {
self.paramPointer = inParamPointer self.paramPointer = inParamPointer
......
...@@ -87,6 +87,8 @@ import Foundation ...@@ -87,6 +87,8 @@ import Foundation
let initContext: InitContext = InitContext.init() let initContext: InitContext = InitContext.init()
initContext.metalLoadMode = net.metalLoadMode initContext.metalLoadMode = net.metalLoadMode
initContext.metalLibPath = net.metalLibPath initContext.metalLibPath = net.metalLibPath
initContext.useMPS = net.useMPS
executor = try Executor<Float32>.init(inDevice: inDevice, inQueue: inQueue, inProgram: program!, initContext: initContext) executor = try Executor<Float32>.init(inDevice: inDevice, inQueue: inQueue, inProgram: program!, initContext: initContext)
net.updateProgram(program: program!) net.updateProgram(program: program!)
} catch let error { } catch let error {
......
...@@ -28,23 +28,80 @@ extension Tensorial { ...@@ -28,23 +28,80 @@ extension Tensorial {
} }
} }
class DataConverter<P: PrecisionType> {
func convert(from: UnsafeMutablePointer<P>, to: UnsafeMutablePointer<P>, fromDim: Dim) {
fatalError(" need imp")
}
func getToDim(fromDim: Dim, layout: DataLayout) -> (dim: Dim, layout: DataLayout) {
fatalError(" need imp")
}
}
/// [ outputChannels ][ inputChannels ][ kernelHeight ][ kernelWidth ] ->
/// [ outputChannels ][ kernelHeight ][ kernelWidth ][ inputChannels ]
class MPSPointerConverter<P: PrecisionType>: DataConverter<P>{
/// [ outputChannels ][ inputChannels ][ kernelHeight ][ kernelWidth ] ->
/// [ outputChannels ][ kernelHeight ][ kernelWidth ][ inputChannels ]
/// - Parameters:
/// - from: from pointer
/// - to: to pointer
override func convert(from: UnsafeMutablePointer<P>, to: UnsafeMutablePointer<P>, fromDim: Dim) {
let outputChannels = fromDim[0]
let inputChannels = fromDim[1]
let kernelHeight = fromDim[2]
let kernelWidth = fromDim[3]
for outChannel in 0..<outputChannels {
for kernelH in 0..<kernelHeight {
for kernelW in 0..<kernelWidth {
for inChannel in 0..<inputChannels {
to[outChannel * inputChannels * kernelHeight * kernelWidth + kernelH * kernelWidth * inputChannels + kernelW * inputChannels + inChannel] =
from[outChannel * inputChannels * kernelHeight * kernelWidth + inChannel * kernelHeight * kernelWidth + kernelH * kernelWidth + kernelW]
}
}
}
}
}
override func getToDim(fromDim: Dim, layout: DataLayout) -> (dim: Dim, layout: DataLayout) {
if layout != DataLayout.NCHW() {
fatalError("not support")
}
let outputChannels = fromDim[0]
let inputChannels = fromDim[1]
let kernelHeight = fromDim[2]
let kernelWidth = fromDim[3]
let toDim = Dim.init(inDim: [outputChannels, kernelHeight, kernelWidth, inputChannels])
return (dim: toDim, layout: DataLayout.NHWC())
}
}
class Tensor<P: PrecisionType>: Tensorial { class Tensor<P: PrecisionType>: Tensorial {
var data: Data var data: Data
var dim: Dim var dim: Dim
/// 模型中的维度: 未经过转换 paddle 模型维度为 N C H W
var tensorDim: Dim
var buffer: MTLBuffer! var buffer: MTLBuffer!
private(set) var layout: DataLayout private(set) var layout: DataLayout
class Data { class Data {
init(inSize: Int, inPointer: UnsafeMutablePointer<P>) { private var released = false
size = inSize let count: Int
let size: Int
init(inCount: Int, inPointer: UnsafeMutablePointer<P>) {
count = inCount
size = inCount * MemoryLayout<P>.size
pointer = inPointer pointer = inPointer
} }
let size: Int internal private(set) var pointer: UnsafeMutablePointer<P>
var pointer: UnsafeMutablePointer<P> subscript(index: Int) -> P {
subscript(index: Int) -> P{
get { get {
return pointer[index] return pointer[index]
} }
...@@ -53,22 +110,40 @@ class Tensor<P: PrecisionType>: Tensorial { ...@@ -53,22 +110,40 @@ class Tensor<P: PrecisionType>: Tensorial {
} }
} }
func release() { func release() {
pointer.deinitialize(count: size) if !released {
pointer.deallocate() pointer.deinitialize(count: count)
pointer.deallocate()
released = true
}
} }
deinit { deinit {
// release() if !released {
pointer.deinitialize(count: count)
pointer.deallocate()
released = true
}
} }
} }
init(inDim: Dim, inLayout: DataLayout = DataLayout.NCHW()) { init(inDim: Dim, inLayout: DataLayout = DataLayout.NCHW()) {
tensorDim = inDim
dim = inDim dim = inDim
let size = inDim.numel() * MemoryLayout<P>.size let pointer = UnsafeMutablePointer<P>.allocate(capacity: inDim.numel())
let pointer = UnsafeMutablePointer<P>.allocate(capacity: size) data = Data.init(inCount: inDim.numel(), inPointer: pointer)
data = Data.init(inSize: size, inPointer: pointer)
layout = inLayout layout = inLayout
} }
func convert(converter: DataConverter<P>) -> UnsafeMutablePointer<P> {
let to = UnsafeMutablePointer<P>.allocate(capacity: numel())
converter.convert(from: data.pointer, to: to, fromDim: dim)
data = Data.init(inCount: numel(), inPointer: to)
let dimAndLayout = converter.getToDim(fromDim: dim, layout: layout)
dim = dimAndLayout.dim
layout = dimAndLayout.layout
return to
}
func convert(to: DataLayout) { func convert(to: DataLayout) {
guard to != layout else { guard to != layout else {
return return
...@@ -82,14 +157,15 @@ class Tensor<P: PrecisionType>: Tensorial { ...@@ -82,14 +157,15 @@ class Tensor<P: PrecisionType>: Tensorial {
// other not support // other not support
return return
} }
let newPointer = UnsafeMutablePointer<P>.allocate(capacity: data.size)
let newPointer = UnsafeMutablePointer<P>.allocate(capacity: numel())
if layout == DataLayout.NCHW() { if layout == DataLayout.NCHW() {
NCHW2NHWC(newPtr: newPointer) NCHW2NHWC(newPtr: newPointer)
} }
data.release() data.release()
data.pointer = newPointer data = Data.init(inCount: data.count, inPointer: newPointer)
layout = to layout = to
} }
...@@ -114,7 +190,7 @@ class Tensor<P: PrecisionType>: Tensorial { ...@@ -114,7 +190,7 @@ class Tensor<P: PrecisionType>: Tensorial {
dim.swapeDimAt(index1: 0, index2: 3) dim.swapeDimAt(index1: 0, index2: 3)
data.release() data.release()
data.pointer = transposePointer data = Data.init(inCount: data.count, inPointer: transposePointer)
} }
guard let floatPointer = data.pointer as? UnsafeMutablePointer<Float32> else { guard let floatPointer = data.pointer as? UnsafeMutablePointer<Float32> else {
...@@ -338,7 +414,7 @@ extension Tensor { ...@@ -338,7 +414,7 @@ extension Tensor {
func logDataPointer(header: String = "") { func logDataPointer(header: String = "") {
print(header) print(header)
var str = "" var str = ""
str += "data size: \(data.size) \n" str += "data count: \(data.count) \n"
str += "dim: \(dim) \n" str += "dim: \(dim) \n"
for i in 0..<numel() { for i in 0..<numel() {
str += " \(data.pointer[i])" str += " \(data.pointer[i])"
......
...@@ -60,10 +60,16 @@ extension Runable where Self: OperatorProtocol{ ...@@ -60,10 +60,16 @@ extension Runable where Self: OperatorProtocol{
} }
public class InitContext { public class InitContext {
/// metal 代码加载方式 /// metal 代码加载方式
var metalLoadMode: MetalLoadMode = .LoadMetalInDefaultLib var metalLoadMode: MetalLoadMode = .LoadMetalInDefaultLib
/// 当 metalLoadMode 为 LoadMetalInCustomMetalLib 时, metal library 路径不能为空 /// 当 metalLoadMode 为 LoadMetalInCustomMetalLib 时, metal library 路径不能为空
var metalLibPath: String? = nil var metalLibPath: String? = nil
/// 是否使用 MetalPerformanceShaders 进行运算
var useMPS: Bool = false
init() { init() {
metalLoadMode = .LoadMetalInDefaultLib metalLoadMode = .LoadMetalInDefaultLib
metalLibPath = nil metalLibPath = nil
......
...@@ -13,11 +13,65 @@ ...@@ -13,11 +13,65 @@
limitations under the License. */ limitations under the License. */
import Foundation import Foundation
import MetalPerformanceShaders
@available(iOS 10.0, *)
var convDic: [String : MPSCNNConvolution] = [:]
@available(iOS 10.0, *)
var imageDic: [String : MPSImage] = [:]
/// 获取唯一字符串
///
/// - Returns: 唯一字符串
func getUniqueKey() -> String {
return UUID.init().uuidString
}
class ConvAddKernel<P: PrecisionType>: Kernel, Computable { class ConvAddKernel<P: PrecisionType>: Kernel, Computable {
var metalParam: MetalConvParam! var metalParam: MetalConvParam!
let identifyingKey: String = getUniqueKey()
required init(device: MTLDevice, param: ConvAddParam<P>, initContext: InitContext) { required init(device: MTLDevice, param: ConvAddParam<P>, initContext: InitContext) {
param.output.initTexture(device: device, inTranspose: [0, 2, 3, 1], computePrecision: GlobalConfig.shared.computePrecision) param.output.initTexture(device: device, inTranspose: [0, 2, 3, 1], computePrecision: GlobalConfig.shared.computePrecision)
let offsetY = (Int(param.dilations[1]) * (param.filter.tensorDim[2] - 1) + 1)/2 - Int(param.paddings[1])
let offsetX = (Int(param.dilations[0]) * (param.filter.tensorDim[3] - 1) + 1)/2 - Int(param.paddings[0])
let key = identifyingKey
if initContext.useMPS {
if #available(iOS 10.0, *) {
if !(param.filter.tensorDim[1] == 1 && param.filter.tensorDim[0] == param.input.tensorDim[1]) && param.input.tensorDim[1] > 4 && param.output.tensorDim[1] > 4 {
let desc = MPSCNNConvolutionDescriptor(kernelWidth: param.filter.tensorDim[3],
kernelHeight: param.filter.tensorDim[2],
inputFeatureChannels: param.input.tensorDim[1],
outputFeatureChannels: param.output.tensorDim[1],
neuronFilter: nil)
desc.strideInPixelsX = Int(param.stride[0])
desc.strideInPixelsY = Int(param.stride[1])
let tensorPointer = param.filter.convert(converter: MPSPointerConverter<P>.init())
let yPointer = param.y.data.pointer
tensorPointer.withMemoryRebound(to: Float.self, capacity: param.filter.numel()) { (weightPointer: UnsafeMutablePointer<Float>) in
yPointer.withMemoryRebound(to: Float.self, capacity: param.y.numel(), { (biasePointer: UnsafeMutablePointer<Float>) in
let conv = MPSCNNConvolution.init(device: device, convolutionDescriptor: desc, kernelWeights: weightPointer, biasTerms: biasePointer, flags: .none)
conv.offset = MPSOffset.init(x: offsetX, y: offsetY, z: 0)
conv.edgeMode = .zero
convDic[key] = conv
})
}
imageDic[identifyingKey + "_input"] = MPSImage.init(texture: param.input.metalTexture, featureChannels: param.input.tensorDim[1])
imageDic[identifyingKey + "_output"] = MPSImage.init(texture: param.output.metalTexture, featureChannels: param.output.tensorDim[1])
super.init(device: device, inFunctionName: "place_holder", initContext: initContext)
return
}
}
}
let padWhenOneC = !(param.filter.channel == 1 && param.filter.n == param.input.tensorDim[1]) let padWhenOneC = !(param.filter.channel == 1 && param.filter.n == param.input.tensorDim[1])
param.filter.initBuffer(device: device, precision: GlobalConfig.shared.computePrecision, padWhenOneC: padWhenOneC) param.filter.initBuffer(device: device, precision: GlobalConfig.shared.computePrecision, padWhenOneC: padWhenOneC)
param.y.initBuffer(device: device, precision: GlobalConfig.shared.computePrecision) param.y.initBuffer(device: device, precision: GlobalConfig.shared.computePrecision)
...@@ -54,12 +108,6 @@ class ConvAddKernel<P: PrecisionType>: Kernel, Computable { ...@@ -54,12 +108,6 @@ class ConvAddKernel<P: PrecisionType>: Kernel, Computable {
fatalError() fatalError()
} }
let offsetY = (Int(param.dilations[1]) * (param.filter.height - 1) + 1)/2 - Int(param.paddings[1])
let offsetX = (Int(param.dilations[0]) * (param.filter.width - 1) + 1)/2 - Int(param.paddings[0])
// print(" function: \(functionName)") // print(" function: \(functionName)")
// print("offset x: \(offsetX)") // print("offset x: \(offsetX)")
// print("offset y: \(offsetY)") // print("offset y: \(offsetY)")
...@@ -73,10 +121,16 @@ class ConvAddKernel<P: PrecisionType>: Kernel, Computable { ...@@ -73,10 +121,16 @@ class ConvAddKernel<P: PrecisionType>: Kernel, Computable {
} }
func compute(commandBuffer: MTLCommandBuffer, param: ConvAddParam<P>) throws { func compute(commandBuffer: MTLCommandBuffer, param: ConvAddParam<P>) throws {
if #available(iOS 10.0, *) {
if let conv = convDic[identifyingKey], let inputImage = imageDic[identifyingKey + "_input"], let outputImage = imageDic[identifyingKey + "_output"] {
conv.encode(commandBuffer: commandBuffer, sourceImage: inputImage, destinationImage: outputImage)
return;
}
}
guard let encoder = commandBuffer.makeComputeCommandEncoder() else { guard let encoder = commandBuffer.makeComputeCommandEncoder() else {
throw PaddleMobileError.predictError(message: " encode is nil") throw PaddleMobileError.predictError(message: " encode is nil")
} }
encoder.setTexture(param.input.metalTexture, index: 0) encoder.setTexture(param.input.metalTexture, index: 0)
encoder.setTexture(param.output.metalTexture, index: 1) encoder.setTexture(param.output.metalTexture, index: 1)
encoder.setBytes(&metalParam, length: MemoryLayout<MetalConvParam>.size, index: 0) encoder.setBytes(&metalParam, length: MemoryLayout<MetalConvParam>.size, index: 0)
...@@ -85,4 +139,13 @@ class ConvAddKernel<P: PrecisionType>: Kernel, Computable { ...@@ -85,4 +139,13 @@ class ConvAddKernel<P: PrecisionType>: Kernel, Computable {
encoder.dispatch(computePipline: pipline, outTexture: param.output.metalTexture) encoder.dispatch(computePipline: pipline, outTexture: param.output.metalTexture)
encoder.endEncoding() encoder.endEncoding()
} }
deinit {
if #available(iOS 10.0, *) {
convDic.removeValue(forKey: identifyingKey)
imageDic.removeValue(forKey: identifyingKey + "_input")
imageDic.removeValue(forKey: identifyingKey + "_output")
}
}
} }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册