package unit; import java.util.*; /** * grpc-0001: channelz PropertyGrid GetIndex() std::find O(C^2) → O(1) with HashMap * * Simulates the defective pattern from grpc/src/core/channelz/property_list.cc: * GetIndex(std::vector& vec, value) does std::find on the vector * to find or insert a column/row name. Called for every Set(col, row, val). * With C distinct columns and R distinct rows: O(C^2) + O(R^2) total. * * Fixed: maintain a parallel HashMap for O(1) index lookup. * * Run: javac -d . GrpcPropertyGridAlgorithm.java && java unit.GrpcPropertyGridAlgorithm */ public class GrpcPropertyGridAlgorithm { // ---- Result ---- static class Result { final long slowOps; final long fastOps; final int columns; final int rows; Result(long slowOps, long fastOps, int columns, int rows) { this.slowOps = slowOps; this.fastOps = fastOps; this.columns = columns; this.rows = rows; } } // ---- Defective: std::find on vector — O(N) per lookup ---- static class DefectivePropertyGrid { final List columns = new ArrayList<>(); final List rows = new ArrayList<>(); final Map grid = new HashMap<>(); long opCount = 0; // Simulates: size_t GetIndex(std::vector& vec, value) int getIndex(List vec, String value) { for (int i = 0; i < vec.size(); i++) { opCount++; if (vec.get(i).equals(value)) return i; } // Not found: insert opCount++; // one more for the end-of-list check vec.add(value); return vec.size() - 1; } void set(String column, String row, String value) { int c = getIndex(columns, column); int r = getIndex(rows, row); grid.put(c + "," + r, value); } } // ---- Fixed: HashMap O(1) lookup + ordered vector for iteration ---- static class FixedPropertyGrid { final List columns = new ArrayList<>(); final List rows = new ArrayList<>(); final Map columnsMap = new HashMap<>(); final Map rowsMap = new HashMap<>(); final Map grid = new HashMap<>(); long opCount = 0; // Simulates fixed GetIndex with parallel HashMap int getIndex(List vec, Map map, String value) { opCount++; // one hash lookup Integer idx = map.get(value); if (idx == null) { idx = vec.size(); vec.add(value); map.put(value, idx); } return idx; } void set(String column, String row, String value) { int c = getIndex(columns, columnsMap, column); int r = getIndex(rows, rowsMap, row); grid.put(c + "," + r, value); } } // ---- check helper ---- static int passed = 0; static int total = 0; static void check(String name, boolean condition) { total++; if (condition) { passed++; System.out.println(" PASS: " + name); } else { System.out.println(" FAIL: " + name); } } public static void main(String[] args) { System.out.println("grpc-0001: channelz PropertyGrid GetIndex O(C^2) -> O(1)"); System.out.println(); // ---- Test 1: Small grid (5 columns, 5 rows) ---- { int C = 5, R = 5; DefectivePropertyGrid slow = new DefectivePropertyGrid(); FixedPropertyGrid fast = new FixedPropertyGrid(); for (int r = 0; r < R; r++) { for (int c = 0; c < C; c++) { String col = "col_" + c; String row = "row_" + r; String val = "v" + c + "_" + r; slow.set(col, row, val); fast.set(col, row, val); } } System.out.println("Test 1: " + C + "x" + R + " grid"); System.out.println(" Slow ops: " + slow.opCount); System.out.println(" Fast ops: " + fast.opCount); check("slow > fast (vector scan > hash lookup)", slow.opCount > fast.opCount); check("grid sizes match", slow.grid.size() == fast.grid.size()); } // ---- Test 2: Medium grid (50 columns, 50 rows) — O(C^2) becomes clear ---- { int C = 50, R = 50; DefectivePropertyGrid slow = new DefectivePropertyGrid(); FixedPropertyGrid fast = new FixedPropertyGrid(); // First pass: register all columns and rows for (int c = 0; c < C; c++) { for (int r = 0; r < R; r++) { slow.set("col_" + c, "row_" + r, "val"); fast.set("col_" + c, "row_" + r, "val"); } } System.out.println("Test 2: " + C + "x" + R + " grid (O(C^2) regime)"); System.out.println(" Slow ops: " + slow.opCount); System.out.println(" Fast ops: " + fast.opCount); double ratio = (double) slow.opCount / fast.opCount; System.out.printf(" Ratio slow/fast: %.1fx%n", ratio); check("slow > 10x fast ops at C=R=50", ratio > 10.0); } // ---- Test 3: High-frequency property updates (same columns, many rows) ---- // Simulates 1000 RPC calls, each setting 10 metrics across 20 channels { int calls = 1000; int metrics = 10; DefectivePropertyGrid slow = new DefectivePropertyGrid(); FixedPropertyGrid fast = new FixedPropertyGrid(); String[] metricNames = new String[metrics]; for (int i = 0; i < metrics; i++) metricNames[i] = "metric_" + i; for (int call = 0; call < calls; call++) { String rowName = "call_" + call; for (String metric : metricNames) { slow.set(metric, rowName, String.valueOf(call)); fast.set(metric, rowName, String.valueOf(call)); } } System.out.println("Test 3: " + calls + " RPC calls × " + metrics + " metrics"); System.out.println(" Slow ops: " + slow.opCount); System.out.println(" Fast ops: " + fast.opCount); double ratio = (double) slow.opCount / fast.opCount; System.out.printf(" Ratio slow/fast: %.1fx%n", ratio); // With 1000 rows and 10 cols: rows_ scan grows O(rows_count) per new row // column lookups are O(10) but row lookups are O(0..1000) → O(R^2/2) total check("slow > 100x fast ops for 1000 rows", ratio > 100.0); } // ---- Test 4: Correctness — column/row indices agree ---- { DefectivePropertyGrid slow = new DefectivePropertyGrid(); FixedPropertyGrid fast = new FixedPropertyGrid(); String[] cols = {"alpha", "beta", "gamma", "delta"}; String[] rowNames = {"r1", "r2", "r3"}; for (String r : rowNames) { for (String c : cols) { slow.set(c, r, c + "_" + r); fast.set(c, r, c + "_" + r); } } // Verify column and row order matches boolean colMatch = slow.columns.equals(fast.columns); boolean rowMatch = slow.rows.equals(fast.rows); System.out.println("Test 4: correctness — column/row order"); System.out.println(" Slow columns: " + slow.columns); System.out.println(" Fast columns: " + fast.columns); check("column order preserved in fixed version", colMatch); check("row order preserved in fixed version", rowMatch); } // ---- Test 5: O(C^2) scaling — measure column scan ops only (R=1 to isolate) ---- { // With R=1, only column scanning grows. Each new column triggers a scan of // all prior columns. Total column ops = 0+1+2+...+(C-1) = C*(C-1)/2. // Doubling C quadruples these ops: C1^2/2 → C2^2/2 → 4x. int R = 1; // single row to isolate column-only O(C^2) DefectivePropertyGrid slow1 = new DefectivePropertyGrid(); DefectivePropertyGrid slow2 = new DefectivePropertyGrid(); int C1 = 50, C2 = 100; for (int c = 0; c < C1; c++) slow1.set("col_" + c, "row_0", "v"); for (int c = 0; c < C2; c++) slow2.set("col_" + c, "row_0", "v"); System.out.println("Test 5: O(C^2) column scaling — C=" + C1 + " vs C=" + C2 + " (R=1)"); System.out.println(" Ops at C=" + C1 + ": " + slow1.opCount); System.out.println(" Ops at C=" + C2 + ": " + slow2.opCount); double scalingRatio = (double) slow2.opCount / slow1.opCount; System.out.printf(" Scaling: %.2fx (expect ~4x for O(C^2))%n", scalingRatio); check("ops grow ~4x when columns doubled (O(C^2) confirmed)", scalingRatio > 3.5); } System.out.println(); System.out.println(passed + "/" + total + " PASS"); } }