KLA*_*TER 3 decode ios swift codable encodable
我有这样的结构
struct RGBA: Codable {
var r: UInt8
var g: UInt8
var b: UInt8
var a: UInt8
}
Run Code Online (Sandbox Code Playgroud)
我想保存大量的这种结构(> 1_000_000)
解码
guard let history = try? JSONDecoder().decode(HistoryRGBA.self, from: data) else { return }
Run Code Online (Sandbox Code Playgroud)
编码
guard let jsonData = try? encoder.encode(dataForSave) else { return false }
Run Code Online (Sandbox Code Playgroud)
如何改善编码/解码时间和 RAM 内存量?
考虑到您的所有属性都是UInt8(字节),您可以使您的结构符合ContiguousBytes并保存其原始字节:
struct RGBA {
let r, g, b, a: UInt8
}
Run Code Online (Sandbox Code Playgroud)
extension RGBA: ContiguousBytes {
func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R {
try Swift.withUnsafeBytes(of: self) { try body($0) }
}
}
Run Code Online (Sandbox Code Playgroud)
extension ContiguousBytes {
init<T: ContiguousBytes>(_ bytes: T) {
self = bytes.withUnsafeBytes { $0.load(as: Self.self) }
}
}
Run Code Online (Sandbox Code Playgroud)
extension RGBA: ExpressibleByArrayLiteral {
typealias ArrayLiteralElement = UInt8
init(arrayLiteral elements: UInt8...) {
self.init(elements)
}
}
Run Code Online (Sandbox Code Playgroud)
extension Array {
var bytes: [UInt8] { withUnsafeBytes { .init($0) } }
var data: Data { withUnsafeBytes { .init($0) } }
}
Run Code Online (Sandbox Code Playgroud)
extension ContiguousBytes {
var bytes: [UInt8] { withUnsafeBytes { .init($0) } }
var data: Data { withUnsafeBytes { .init($0) } }
}
Run Code Online (Sandbox Code Playgroud)
extension ContiguousBytes {
func object<T>() -> T { withUnsafeBytes { $0.load(as: T.self) } }
func objects<T>() -> [T] { withUnsafeBytes { .init($0.bindMemory(to: T.self)) } }
}
Run Code Online (Sandbox Code Playgroud)
extension ContiguousBytes {
var rgba: RGBA { object() }
var rgbaCollection: [RGBA] { objects() }
}
Run Code Online (Sandbox Code Playgroud)
extension UIColor {
convenience init<T: Collection>(_ bytes: T) where T.Index == Int, T.Element == UInt8 {
self.init(red: CGFloat(bytes[0])/255,
green: CGFloat(bytes[1])/255,
blue: CGFloat(bytes[2])/255,
alpha: CGFloat(bytes[3])/255)
}
}
Run Code Online (Sandbox Code Playgroud)
extension RGBA {
var color: UIColor { .init(bytes) }
}
Run Code Online (Sandbox Code Playgroud)
let red: RGBA = [255, 0, 0, 255]
let green: RGBA = [0, 255, 0, 255]
let blue: RGBA = [0, 0, 255, 255]
let redBytes = red.bytes // [255, 0, 0, 255]
let redData = red.data // 4 bytes
let rgbaFromBytes = redBytes.rgba // RGBA
let rgbaFromData = redData.rgba // RGBA
let colorFromRGBA = red.color // r 1.0 g 0.0 b 0.0 a 1.0
let rgba: RGBA = [255,255,0,255] // RGBA yellow
let yellow = rgba.color // r 1.0 g 1.0 b 0.0 a 1.0
let colors = [red, green, blue] // [{r 255, g 0, b 0, a 255}, {r 0, g 255, b 0, a 255}, {r 0, g 0, b 255, a 255}]
let colorsData = colors.data // 12 bytes
let colorsFromData = colorsData.rgbaCollection // [{r 255, g 0, b 0, a 255}, {r 0, g 255, b 0, a 255}, {r 0, g 0, b 255, a 255}]
Run Code Online (Sandbox Code Playgroud)
编辑/更新:
struct LayerRGBA {
var canvas: [[RGBA]]
}
Run Code Online (Sandbox Code Playgroud)
extension LayerRGBA {
var data: Data { canvas.data }
init(_ data: Data) { canvas = data.objects() }
}
Run Code Online (Sandbox Code Playgroud)
struct AnimationRGBA {
var layers: [LayerRGBA]
}
Run Code Online (Sandbox Code Playgroud)
extension AnimationRGBA {
var data: Data { layers.data }
init(_ data: Data) {
layers = data.objects()
}
}
Run Code Online (Sandbox Code Playgroud)
struct HistoryRGBA {
var layers: [LayerRGBA] = []
var animations: [AnimationRGBA] = []
}
Run Code Online (Sandbox Code Playgroud)
extension HistoryRGBA {
var data: Data {
let layersData = layers.data
return layersData.count.data + layersData + animations.data
}
init(data: Data) {
let index = Int(Data(data.prefix(8))).advanced(by: 8)
self.init(layers: data.subdata(in: 8..<index).objects(),
animations: data.subdata(in: index..<data.endIndex).objects())
}
}
Run Code Online (Sandbox Code Playgroud)
extension Numeric {
var data: Data {
var bytes = self
return .init(bytes: &bytes, count: MemoryLayout<Self>.size)
}
}
Run Code Online (Sandbox Code Playgroud)
extension Numeric {
init<D: DataProtocol>(_ data: D) {
var value: Self = .zero
let _ = withUnsafeMutableBytes(of: &value, { data.copyBytes(to: $0)} )
self = value
}
}
Run Code Online (Sandbox Code Playgroud)
游乐场测试:
let layer1: LayerRGBA = .init(canvas: [colors,[red],[green, blue]])
let layer2: LayerRGBA = .init(canvas: [[red],[green, rgba]])
let loaded: LayerRGBA = .init(layer1.data)
loaded.canvas[0]
loaded.canvas[1]
loaded.canvas[2]
let animationRGBA: AnimationRGBA = .init(layers: [layer1,layer2])
let loadedAnimation: AnimationRGBA = .init(animationRGBA.data)
loadedAnimation.layers.count // 2
loadedAnimation.layers[0].canvas[0]
loadedAnimation.layers[0].canvas[1]
loadedAnimation.layers[0].canvas[2]
loadedAnimation.layers[1].canvas[0]
loadedAnimation.layers[1].canvas[1]
let hRGBA: HistoryRGBA = .init(layers: [loaded], animations: [animationRGBA])
let loadedHistory: HistoryRGBA = .init(data: hRGBA.data)
loadedHistory.layers[0].canvas[0]
loadedHistory.layers[0].canvas[1]
loadedHistory.layers[0].canvas[2]
loadedHistory.animations[0].layers[0].canvas[0]
loadedHistory.animations[0].layers[0].canvas[1]
loadedHistory.animations[0].layers[0].canvas[2]
loadedHistory.animations[0].layers[1].canvas[0]
loadedHistory.animations[0].layers[1].canvas[1]
Run Code Online (Sandbox Code Playgroud)
性能JSONEncoder/Decoder性能...不是很大。ZippyJSON是一种替代品,据说比 Foundation 的实现快 4 倍左右,如果您想要更好的性能和更低的内存使用量,您可能希望谷歌搜索某种流式 JSON 解码器库。
但是,您在评论中说您不需要 JSON 格式。这很好,因为我们可以更有效地将数据存储为原始字节数组,而不是基于文本的格式,例如 JSON:
extension RGBA {
static let size = 4 // the size of a (packed) RGBA structure
}
// encoding
var data = Data(count: history.rgba.count * RGBA.size)
for i in 0..<history.rgba.count {
let rgba = history.rgba[i]
data[i*RGBA.size] = rgba.r
data[i*RGBA.size+1] = rgba.g
data[i*RGBA.size+2] = rgba.b
data[i*RGBA.size+3] = rgba.a
}
// decoding
guard data.count % RGBA.size == 0 else {
// data is incomplete, handle error
return
}
let rgbaCount = data.count / RGBA.size
var result = [RGBA]()
result.reserveCapacity(rgbaCount)
for i in 0..<rgbaCount {
result.append(RGBA(r: data[i*RGBA.size],
g: data[i*RGBA.size+1],
b: data[i*RGBA.size+2],
a: data[i*RGBA.size+3]))
}
Run Code Online (Sandbox Code Playgroud)
这已经比在我的机器上使用 JSONEncoder 快了大约 50 倍(~100 毫秒而不是~5 秒)。
通过绕过 Swift 的一些安全检查和内存管理并使用原始指针,您可以变得更快:
// encoding
let byteCount = history.rgba.count * RGBA.size
let rawBuf = malloc(byteCount)!
let buf = rawBuf.bindMemory(to: UInt8.self, capacity: byteCount)
for i in 0..<history.rgba.count {
let rgba = history.rgba[i]
buf[i*RGBA.size] = rgba.r
buf[i*RGBA.size+1] = rgba.g
buf[i*RGBA.size+2] = rgba.b
buf[i*RGBA.size+3] = rgba.a
}
let data = Data(bytesNoCopy: rawBuf, count: byteCount, deallocator: .free)
// decoding
guard data.count % RGBA.size == 0 else {
// data is incomplete, handle error
return
}
let result: [RGBA] = data.withUnsafeBytes { rawBuf in
let buf = rawBuf.bindMemory(to: UInt8.self)
let rgbaCount = buf.count / RGBA.size
return [RGBA](unsafeUninitializedCapacity: rgbaCount) { resultBuf, initializedCount in
for i in 0..<rgbaCount {
resultBuf[i] = RGBA(r: data[i*RGBA.size],
g: data[i*RGBA.size+1],
b: data[i*RGBA.size+2],
a: data[i*RGBA.size+3])
}
}
}
Run Code Online (Sandbox Code Playgroud)
我的机器上的基准测试结果(我没有测试 ZippyJSON):
JSON:
Encode: 4967.0ms; 32280478 bytes
Decode: 5673.0ms
Data:
Encode: 96.0ms; 4000000 bytes
Decode: 19.0ms
Pointers:
Encode: 1.0ms; 4000000 bytes
Decode: 18.0ms
Run Code Online (Sandbox Code Playgroud)
通过将数组直接从内存写入磁盘而不进行序列化,您可能会变得更快,尽管我也没有测试过。当然,当您测试性能时,请确保您在发布模式下进行测试。
| 归档时间: |
|
| 查看次数: |
490 次 |
| 最近记录: |