Skip to content

Commit 04f3dd7

Browse files
Optimize _lis_outer_body_tf
The optimized code achieves a **12.5x speedup** (from 8.17s to 606ms) by eliminating a nested `tf.while_loop` and replacing it with vectorized TensorFlow operations. ## Key Optimization **What changed:** The original implementation used a nested loop structure where `_lis_outer_body_tf` called an inner `tf.while_loop` that iterated through all previous elements (0 to i-1) one at a time, performing tensor scatter updates on each iteration. The optimized version replaces this entire inner loop with a single vectorized computation using TensorFlow's array slicing and reduction operations. **Why it's faster:** 1. **Eliminates O(i) graph operations per iteration**: The original inner loop created `i` separate graph nodes for each outer iteration (visible in line profiler: 1618 total hits for inner body with ~5-7ms per operation). The optimized version executes just one `tf.cond` with vectorized operations inside, processing all comparisons in parallel. 2. **Vectorized comparisons instead of scalar iterations**: Instead of comparing `arr[j] < arr[i]` sequentially for j=0,1,2,...,i-1, the optimized code: - Slices the entire prefix: `arr_prefix = tf.slice(arr, [0], [i])` - Creates a single boolean mask: `mask = tf.less(arr_prefix, arr_i)` - Computes all candidates at once: `candidates = tf.where(mask, prefix + 1, fill_val)` - Finds the maximum in one operation: `tf.reduce_max(candidates)` 3. **Reduces memory traffic**: The original code performed `i` separate `tf.tensor_scatter_nd_update` operations (one per inner loop iteration). The optimized version performs just one final scatter update per outer iteration. **Performance characteristics by test case:** - **Best improvements** (100x - 60x faster): Large-scale tests with high indices (i=250, i=200, i=100) where the inner loop would have iterated many times. The `test_large_scale_performance_boundary` shows 6305% speedup (1.31s → 20.5ms). - **Modest improvements** (1.5x - 2.8x faster): Tests with small indices (i=2) still benefit but less dramatically since the inner loop overhead is smaller. - **Slight slowdowns** (10-20% slower): Tests at i=1 show minor regression because the vectorized operations have fixed overhead that exceeds the cost of a single-iteration inner loop. However, this is rare in practice. **Impact on workloads:** This optimization is particularly valuable when computing longest increasing subsequences (LIS) or similar dynamic programming algorithms where the outer loop processes many elements sequentially. The speedup scales quadratically with input size, making it essential for arrays larger than ~50 elements. If this function is called in a hot path (e.g., within training loops or repeated graph executions), the cumulative savings would be substantial.
1 parent 9d4fa82 commit 04f3dd7

1 file changed

Lines changed: 20 additions & 6 deletions

File tree

code_to_optimize/sample_code.py

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -427,12 +427,26 @@ def _lis_inner_cond_tf(j, _dp_inner, _arr, i):
427427

428428

429429
def _lis_outer_body_tf(i, dp, arr, n):
430-
_, dp, _, _ = tf.while_loop(
431-
_lis_inner_cond_tf,
432-
_lis_inner_body_tf,
433-
[0, dp, arr, i]
434-
)
435-
return i + 1, dp, arr, n
430+
def true_fn():
431+
prefix = tf.slice(dp, [0], [i])
432+
arr_prefix = tf.slice(arr, [0], [i])
433+
arr_i = tf.gather(arr, i)
434+
mask = tf.less(arr_prefix, arr_i)
435+
fill_val = tf.reduce_min(dp) - tf.constant(1, dtype=dp.dtype)
436+
candidates = tf.where(mask, prefix + 1, tf.fill(tf.shape(prefix), fill_val))
437+
max_cand = tf.reduce_max(candidates)
438+
dp_i = tf.gather(dp, i)
439+
new_val = tf.maximum(dp_i, max_cand)
440+
indices = tf.reshape(i, [1, 1])
441+
updates = tf.reshape(new_val, [1])
442+
dp_updated = tf.tensor_scatter_nd_update(dp, indices, updates)
443+
return dp_updated
444+
445+
def false_fn():
446+
return dp
447+
448+
dp_updated = tf.cond(tf.greater(i, 0), true_fn, false_fn)
449+
return i + 1, dp_updated, arr, n
436450

437451

438452
def _lis_outer_cond_tf(i, _dp, _arr, n):

0 commit comments

Comments
 (0)