Я пишу быстрый код для MacOS 10.14.Я столкнулся с узким местом в производительности и изолировал его от следующего кода рендеринга, переписанного для удаления всех ненужных частей.
У меня есть NSImage (оригинал JPG), и я изменяю его размер следующим кодом:
extension NSImage {
func resized(size: NSSize) -> NSImage {
let cgImage = self.cgImage!
let bitsPerComponent = cgImage.bitsPerComponent
let bytesPerRow = cgImage.bytesPerRow
let colorSpace = cgImage.colorSpace!
let bitmapInfo = CGImageAlphaInfo.noneSkipLast
let context = CGContext(data: nil,
width: Int(cgImage.width / 2),
height: Int(cgImage.height / 2),
bitsPerComponent: bitsPerComponent,
bytesPerRow: bytesPerRow,
space: colorSpace,
bitmapInfo: bitmapInfo.rawValue)!
context.interpolationQuality = .high
let newSize = size
context.draw(cgImage,
in: NSRect(x: 0, y: 0, width: newSize.width, height: newSize.height))
let img = context.makeImage()!
return NSImage(cgImage: img, size: newSize)
}
var cgImage: CGImage? {
get {
guard let imageData = self.tiffRepresentation else { return nil }
guard let sourceData = CGImageSourceCreateWithData(imageData as CFData, nil) else { return nil }
return CGImageSourceCreateImageAtIndex(sourceData, 0, nil)
}
}
}
Затем я запускаю тест производительности со следующим кодом: import XCTest @testable import TestRendering
class TestRenderingTests: XCTestCase {
static var testImage: NSImage {
let url = Bundle(for: TestRenderingTests.self).url(forResource: "photo", withExtension: ".jpg")
return NSImage(contentsOf: url!)!
}
func testPerformanceExample() {
let originalImage = type(of: self).testImage
let multiFactor: CGFloat = 0.99
let resizedSize = NSSize(
width: originalImage.size.width * multiFactor,
height: originalImage.size.height * multiFactor
)
let resizedImage = originalImage.resized(size: resizedSize)
let baseImage = NSImage(size: resizedSize)
let rect = NSRect(
origin: NSPoint.zero,
size: resizedSize
)
self.measure {
baseImage.lockFocus()
resizedImage.draw(in: rect, from: rect, operation: .copy, fraction: 1)
baseImage.unlockFocus()
}
}
}
Если я запускаю тест производительности с масштабным коэффициентом multiFactor
, равным 1
,Я получаю определенное значение для измеряемого блока, которое я использую в качестве базовой линии.
Если затем изменить масштабный коэффициент multiFactor
на 0.99
, производительность измеренного блока ухудшится на 59%.
Почему этот спектакль ударил?Моя теория состоит в том, что функция изменения размера изображения, когда размер не равен исходному размеру, каким-то образом создает новое представление изображения, которое необходимо подвергать дальнейшей обработке каждый раз, когда оно отображается.Если изображение имеет исходный размер, каким-то образом оно просто использует исходное изображение и не требует предварительной обработки.
Я придумал эту теорию, просматривая трассировку стека при профилировании двух версий теста.
Следующая трассировка стека получена, когда масштабный коэффициент равен 1 (размер изображения не изменился):
Следующая трассировка стекаэто когда масштабный коэффициент равен 0,99:
Функции в стеке вызовов не совпадают: argb32_image_mark_argb32
против argb32_sample_argb32
.
Можно ли переписать функцию resized(size:)
таким образом, чтобы она создавала изображение, которое не нужно «отбирать» каждый раз при его рендеринге?
Для справки, яиспользуя следующее изображение в тесте: