diff --git a/lib/ovsdb-data.c b/lib/ovsdb-data.c
index f18f74298..f0d7bee23 100644
--- a/lib/ovsdb-data.c
+++ b/lib/ovsdb-data.c
@@ -2238,6 +2238,8 @@ ovsdb_symbol_table_insert(struct ovsdb_symbol_table *symtab,
 /* APIs for Generating and apply diffs.  */
 
 /* Find what needs to be added to and removed from 'old' to construct 'new'.
+ * If the optional 'diff' is porvided, it can be used to speed up processing,
+ * in case it is smaller than the original 'old' and 'new'.
  *
  * The 'added' and 'removed' datums are always safe; the orders of keys are
  * maintained since they are added in order.   */
@@ -2246,6 +2248,7 @@ ovsdb_datum_added_removed(struct ovsdb_datum *added,
                           struct ovsdb_datum *removed,
                           const struct ovsdb_datum *old,
                           const struct ovsdb_datum *new,
+                          const struct ovsdb_datum *diff,
                           const struct ovsdb_type *type)
 {
     size_t oi, ni;
@@ -2258,6 +2261,29 @@ ovsdb_datum_added_removed(struct ovsdb_datum *added,
         return;
     }
 
+    /* Use diff, if provided, unless it's comparable in size. */
+    if (diff && diff->n * 2 < old->n + new->n) {
+        unsigned int idx;
+
+        for (size_t di = 0; di < diff->n; di++) {
+            bool found = ovsdb_datum_find_key(old, &diff->keys[di],
+                                              type->key.type, &idx);
+
+            if (!found) {
+                ovsdb_datum_add_from_index_unsafe(added, diff, di, type);
+            } else {
+                if (type->value.type != OVSDB_TYPE_VOID
+                    && !ovsdb_atom_equals(&diff->values[di],
+                                          &old->values[idx],
+                                          type->value.type)) {
+                    ovsdb_datum_add_from_index_unsafe(added, diff, di, type);
+                }
+                ovsdb_datum_add_from_index_unsafe(removed, old, idx, type);
+            }
+        }
+        return;
+    }
+
     /* Generate the diff in O(n) time. */
     for (oi = ni = 0; oi < old->n && ni < new->n;) {
         int c = ovsdb_atom_compare_3way(&old->keys[oi], &new->keys[ni],
diff --git a/lib/ovsdb-data.h b/lib/ovsdb-data.h
index f048a8cb0..c0408ee49 100644
--- a/lib/ovsdb-data.h
+++ b/lib/ovsdb-data.h
@@ -256,6 +256,7 @@ void ovsdb_datum_added_removed(struct ovsdb_datum *added,
                                struct ovsdb_datum *removed,
                                const struct ovsdb_datum *old,
                                const struct ovsdb_datum *new,
+                               const struct ovsdb_datum *diff,
                                const struct ovsdb_type *type);
 
 void ovsdb_datum_diff(struct ovsdb_datum *diff,
diff --git a/ovsdb/transaction.c b/ovsdb/transaction.c
index b69d03b5a..e1a87bb74 100644
--- a/ovsdb/transaction.c
+++ b/ovsdb/transaction.c
@@ -347,12 +347,13 @@ update_row_ref_count(struct ovsdb_txn *txn, struct ovsdb_txn_row *r)
                     return error;
                 }
             } else if (r->old && r->new) {
-                struct ovsdb_datum added, removed;
+                struct ovsdb_datum added, removed, *diff;
 
+                diff = (r->diff) ? &r->diff->fields[column->index] : NULL;
                 ovsdb_datum_added_removed(&added, &removed,
                                           &r->old->fields[column->index],
                                           &r->new->fields[column->index],
-                                          &column->type);
+                                          diff, &column->type);
 
                 error = ovsdb_txn_adjust_row_refs(
                             txn, r->old, column, &removed, -1);
@@ -760,9 +761,13 @@ assess_weak_refs(struct ovsdb_txn *txn, struct ovsdb_txn_row *txn_row)
         if (datum->n != orig_n
             || bitmap_is_set(txn_row->changed, column->index)) {
             if (txn_row->old) {
+                struct ovsdb_datum *diff;
+
+                diff = (txn_row->diff && datum->n == orig_n)
+                       ? &txn_row->diff->fields[column->index] : NULL;
                 ovsdb_datum_added_removed(&added, &removed,
                                           &txn_row->old->fields[column->index],
-                                          datum, &column->type);
+                                          datum, diff, &column->type);
             } else {
                 ovsdb_datum_clone(&added, datum);
             }
@@ -790,6 +795,10 @@ assess_weak_refs(struct ovsdb_txn *txn, struct ovsdb_txn_row *txn_row)
 
         if (datum->n != orig_n) {
             bitmap_set1(txn_row->changed, column->index);
+            /* Can no longer rely on the previous diff. */
+            ovsdb_row_destroy(txn_row->diff);
+            txn_row->diff = NULL;
+
             if (datum->n < column->type.n_min) {
                 const struct uuid *row_uuid = ovsdb_row_get_uuid(txn_row->new);
                 if (zero && !txn_row->old) {
