Plan (summary): isolate the op, measure baseline, profile CPU/GPU, reason about memory/layout and caching, apply targeted optimizations (vectorize CPU, fuse/rewrite GPU kernels), remove unnecessary copies, iterate with regression tests and benchmarks.1) Isolation & benchmarking- Build a small harness that calls only your op with representative input shapes and dtypes; run warm-up + timed runs; measure throughput and latency.- Example harness commands:bash
# run Python microbench
python bench_op.py --iters 1000 --shape 1,224,224,3 --dtype float32
2) Profiling tools & workflow- TensorFlow profiler / TensorBoard profiler for end-to-end traces (use trace viewer to find op hot-spots).- CPU: perf record / report, Intel VTune for hotspots, call stacks, CPU counters (cache misses, branch-mispredict).- GPU: Nsight Systems for timeline and API-level view, Nsight Compute (or nvprof legacy) for per-kernel metrics (SM utilization, memory throughput, achieved occupancy).- Example commands:bash
# GPU timeline
nsys profile -o profile.qdrep python run_model.py
# Kernel metrics
nv-nsight-cu-cli --metrics sm__throughput.avg ./my_inference_bin
3) Reason about memory layout & cache- Ensure data is contiguous in memory and matches kernel expectations (NHWC vs NCHW). Re-ordering tensors can cost copies—benchmark both layouts.- Align buffers to 64-byte cache lines, use padding/tiling to avoid conflict misses.- Apply blocking (tile loops) so inner loops stride 1 over contiguous memory; minimize working set to fit L1/L2.- Use prefetch intrinsics (e.g., __builtin_prefetch) where beneficial; verify with perf L1/L2 miss counters.4) CPU vectorization & concurrency- Compile with -O3 -march=native (or target arch) and enable vectorization reports.- Use restrict pointers or __attribute__((assume_aligned(64))) / alignas to help the compiler.- Use pragmas: #pragma omp simd or #pragma GCC ivdep for safe vectorization.- For critical kernels consider hand-written SIMD (AVX2/AVX512) intrinsics or use Eigen/Tensorflow kernel primitives which already vectorize.- Example hint:cpp
// tell compiler pointer is aligned and no-alias
float* __restrict__ TF_ATTRIBUTE_ALIGNED(64) dst = ...
#pragma omp simd
for (int i=0;i<n;++i) dst[i]=src[i]*scale;
5) GPU kernel optimization & fusion- Fuse small kernels to reduce global memory traffic and kernel launch overhead (e.g., combine elementwise ops into one kernel). Consider writing a custom fused CUDA kernel or enable XLA compilation.- Optimize memory access: ensure coalesced loads/stores, use shared memory to stage tiles, avoid bank conflicts.- Tune block/grid sizes for occupancy; use __launch_bounds__ or occupancy calculators.- Use warp-level primitives and loop unrolling where appropriate.- Use asynchronous streams and pinned host memory for overlapping memcpy and compute.6) Reduce memory copies between op boundaries- Avoid materializing intermediate tensors; implement in-place updates when safe (follow TensorFlow's aliasing rules).- Use TF's ReusableAllocator / BFC allocator tuning; pre-allocate workspaces to reuse across calls.- Expose a custom kernel that takes a pre-allocated output buffer (resource handle) to avoid alloc/free per-inference.- Use zero-copy when transferring data from host to device (pinned memory + cudaMemcpyAsync) and minimize host<->device round trips.7) Validation and metrics- Measure before/after: latency P50/P95, throughput (samples/sec), GPU SM/utilization, memory bandwidth, CPU cycles per op.- Add unit/perf tests to avoid regressions; maintain CI microbenchmarks.Trade-offs- Hand-tuned SIMD/CUDA gives best perf but increases maintenance and portability costs; prefer higher-level vectorized libs or Eigen when possible.- Kernel fusion reduces memory traffic but can increase register pressure and reduce occupancy—profile to balance.This practical loop—profile → isolate → optimize (layout/vectorize/fuse) → reduce copies → validate—will systematically reduce your custom op bottleneck.