iOS Development

ios – CoreData : Why updating single column utilizing Batch Replace Operation is way slower than utilizing NSManagedObject?

Spread the love


All of the whereas, my ideas on Batch Replace Operations are that they’re much sooner than updating through NSManagedObject. Nevertheless, as per my testing on a 2000 rows manufacturing information set, utilizing Batch Replace Operations is 2 to three instances slower than updating through NSManagedObject.

My replace sample is, there are round 2000 rows. I’m updating every of their column named “order”, with totally different worth for every row.

The objective is straightforward, we need to make the present row’s order worth, is bigger than earlier row’s order worth.

Here is my code snippet. nsPlainNote is an NSManagedObject

Replace through Batch Replace Operation (sluggish)

non-public func _updateOrdersIfPossible2(context: NSManagedObjectContext, updateOrders: [UpdateOrder]) {
    let depend = updateOrders.depend
    
    if depend < 2 {
        return
    }
    
    var prevOrder = updateOrders[0].order
    
    var updatedObjectIDs = [NSManagedObjectID]()
    
    if !Utils.isValidOrder(prevOrder) {
        prevOrder = prevOrder - 1
        
        precondition(Utils.isValidOrder(prevOrder))
        
        // Time-consuming operation.
        if let updatedObjectID = _updateWithoutMerge(context: context, objectID: updateOrders[0].objectID, propertiesToUpdate: [
            "order": prevOrder
        ]) {
            updatedObjectIDs.append(updatedObjectID)
        }
    }
    
    for index in 1..<depend {
        precondition(Utils.isValidOrder(prevOrder))
        
        let updateOrder = updateOrders[index]
        
        if !Utils.isValidOrder(updateOrder.order) || updateOrder.order <= prevOrder {
            var newOrder = prevOrder + 1
            
            if !Utils.isValidOrder(newOrder) {
                newOrder = newOrder + 1
            }
            
            precondition(newOrder > prevOrder)
            
            prevOrder = newOrder
            
            precondition(Utils.isValidOrder(newOrder))
            
            // Time-consuming operation.
            if let updatedObjectID = _updateWithoutMerge(context: context, objectID: updateOrder.objectID, propertiesToUpdate: [
                "order": newOrder
            ]) {
                updatedObjectIDs.append(updatedObjectID)
            }
        } else {
            // Skip from updating. Quick!
            
            prevOrder = updateOrder.order
        }
    }   // for index in 1..<depend
    
    if !updatedObjectIDs.isEmpty {
        let modifications = [NSUpdatedObjectsKey : updatedObjectIDs]
        CoreDataStack.INSTANCE.mergeChanges(modifications)
    }
}

non-public func _updateWithoutMerge(context: NSManagedObjectContext, objectID: NSManagedObjectID, propertiesToUpdate: [AnyHashable : Any]) -> NSManagedObjectID? {
    return RepositoryUtils._updateWithoutMerge(
        context: context,
        entityName: "NSPlainNote",
        objectID: objectID,
        propertiesToUpdate: propertiesToUpdate
    )
}

static func _updateWithoutMerge(context: NSManagedObjectContext, entityName: String, objectID: NSManagedObjectID, propertiesToUpdate: [AnyHashable : Any]) -> NSManagedObjectID? {
    var outcome: NSManagedObjectID? = nil
    
    do {
        let batchUpdateRequest = NSBatchUpdateRequest(entityName: entityName)
        batchUpdateRequest.predicate = NSPredicate(format: "self = %@", objectID)
        batchUpdateRequest.propertiesToUpdate = propertiesToUpdate
        batchUpdateRequest.resultType = .updatedObjectIDsResultType
        
        let batchUpdateResult = strive context.execute(batchUpdateRequest) as? NSBatchUpdateResult
        
        if let managedObjectIDs = batchUpdateResult?.outcome as? [NSManagedObjectID] {
            outcome = managedObjectIDs.first
        }
    } catch {
        context.rollback()
        
        error_log(error)
    }
    
    return outcome
}

As per my benchmark, most time are spent within the loop of calling _updateWithoutMerge.


Replace through NSManagedObject (Quicker)

non-public func _updateOrdersIfPossible(context: NSManagedObjectContext, updateOrders: [UpdateOrder]) {
    let depend = updateOrders.depend
    if depend < 2 {
        return
    }
    var prevOrder = updateOrders[0].order
    var updatedObjectIDs = [NSManagedObjectID]()
    if !Utils.isValidOrder(prevOrder) {
        prevOrder = prevOrder - 1
        precondition(Utils.isValidOrder(prevOrder))
        // Time-consuming operation.
        if let nsPlainNote = NSPlainNoteRepository.getNSPlainNote(context: context, objectID: updateOrders[0].objectID, propertiesToFetch: ["order"]) {
            nsPlainNote.order = prevOrder
        }
    }
    for index in 1..<depend {
        precondition(Utils.isValidOrder(prevOrder))
        let updateOrder = updateOrders[index]
        if !Utils.isValidOrder(updateOrder.order) || updateOrder.order <= prevOrder {
            var newOrder = prevOrder + 1
            if !Utils.isValidOrder(newOrder) {
                newOrder = newOrder + 1
            }
            precondition(newOrder > prevOrder)
            prevOrder = newOrder
            precondition(Utils.isValidOrder(newOrder))
            // Time-consuming operation.
            if let nsPlainNote = NSPlainNoteRepository.getNSPlainNote(context: context, objectID: updateOrder.objectID, propertiesToFetch: ["order"]) {
                nsPlainNote.order = newOrder
            }
        } else {
            // Skip from updating. Quick!
            prevOrder = updateOrder.order
        }
    }   // for index in 1..<depend
    RepositoryUtils.saveContextIfPossible(context)
}
static func getNSPlainNote(context: NSManagedObjectContext, objectID: NSManagedObjectID, propertiesToFetch: [Any]?) -> NSPlainNote? {
    var nsPlainNote: NSPlainNote? = nil
    context.performAndWait {
        let fetchRequest = NSPlainNote.fetchRequest()
        fetchRequest.predicate = NSPredicate(format: "self = %@", objectID)
        fetchRequest.propertiesToFetch = propertiesToFetch
        fetchRequest.fetchLimit = 1
        do {
            let nsPlainNotes = strive fetchRequest.execute()
            if let _nsPlainNote = nsPlainNotes.first {
                nsPlainNote = _nsPlainNote
            }
        } catch {
            error_log(error)
        }
    }
    return nsPlainNote
}

I assumed utilizing Batch Replace Operation ought to be sooner than utilizing NSManagedObject?

Am I having improper expectation, or there may be an error in my implementation?

Thanks.

Leave a Reply

Your email address will not be published. Required fields are marked *