java-topology/defects/thrift/unit/ThriftStructMembersAlgorithm.java

249 lines
9.8 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package unit;
import java.util.*;
/**
* thrift-0001: t_cpp_generator is_struct_storage_not_throwing() std::find O(M^2) → O(1)
*
* Simulates the defective pattern from Apache Thrift's C++ generator:
* is_struct_storage_not_throwing() flattens nested struct members using
* std::find on a vector<t_field*> for deduplication.
* When N nested structs each contribute M fields, total ops = O(N×M×total).
*
* Fixed: std::unordered_set<t_field*> for O(1) membership test.
*
* Run: javac -d . ThriftStructMembersAlgorithm.java && java unit.ThriftStructMembersAlgorithm
*/
public class ThriftStructMembersAlgorithm {
// Simulated t_field* — each instance is a unique pointer
static class Field {
final String name;
final boolean isStruct;
final List<Field> nestedFields;
Field(String name) {
this.name = name;
this.isStruct = false;
this.nestedFields = null;
}
Field(String name, List<Field> nested) {
this.name = name;
this.isStruct = true;
this.nestedFields = nested;
}
}
// ---- Result ----
static class Result {
final long ops;
final boolean noexcept;
Result(long ops, boolean noexcept) {
this.ops = ops;
this.noexcept = noexcept;
}
}
// ---- Defective: std::find on vector — O(M) per dedup check ----
static class DefectiveStructAnalyzer {
long opCount = 0;
boolean isStructStorageNotThrowing(List<Field> topLevelMembers) {
List<Field> members = new ArrayList<>(topLevelMembers);
for (int i = 0; i < members.size(); i++) {
Field field = members.get(i);
if (field.isStruct && field.nestedFields != null) {
for (Field nested : field.nestedFields) {
// std::find: O(members.size()) scan
boolean found = false;
for (Field m : members) {
opCount++;
if (m == nested) {
found = true;
break;
}
}
if (!found) {
members.add(nested);
}
}
}
// Simulate non-struct field checks (constant work)
}
return true;
}
}
// ---- Fixed: unordered_set O(1) dedup ----
static class FixedStructAnalyzer {
long opCount = 0;
boolean isStructStorageNotThrowing(List<Field> topLevelMembers) {
List<Field> members = new ArrayList<>(topLevelMembers);
Set<Field> seen = new IdentityHashMap<Field, Boolean>()
{{ for (Field f : members) put(f, Boolean.TRUE); }}.keySet();
// Use IdentityHashMap to simulate pointer-based set (reference equality)
Set<Field> seenSet = Collections.newSetFromMap(new IdentityHashMap<>());
seenSet.addAll(topLevelMembers);
for (int i = 0; i < members.size(); i++) {
Field field = members.get(i);
if (field.isStruct && field.nestedFields != null) {
for (Field nested : field.nestedFields) {
opCount++; // O(1) hash lookup
if (!seenSet.contains(nested)) {
members.add(nested);
seenSet.add(nested);
}
}
}
}
return true;
}
}
// ---- 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);
}
}
// Build a struct with N fields, each of which is a struct with M nested fields
static List<Field> buildNestedStruct(int topN, int nestedM, int nestDepth) {
if (nestDepth == 0) {
List<Field> leaves = new ArrayList<>();
for (int i = 0; i < nestedM; i++) leaves.add(new Field("leaf_" + i));
return leaves;
}
List<Field> inner = buildNestedStruct(topN, nestedM, nestDepth - 1);
List<Field> result = new ArrayList<>();
for (int i = 0; i < topN; i++) {
result.add(new Field("struct_" + nestDepth + "_" + i, new ArrayList<>(inner)));
}
return result;
}
public static void main(String[] args) {
System.out.println("thrift-0001: is_struct_storage_not_throwing std::find O(M^2) -> O(1)");
System.out.println();
// ---- Test 1: Simple flat struct (no nesting) — both same ----
{
List<Field> members = new ArrayList<>();
for (int i = 0; i < 10; i++) members.add(new Field("field_" + i));
DefectiveStructAnalyzer slow = new DefectiveStructAnalyzer();
FixedStructAnalyzer fast = new FixedStructAnalyzer();
slow.isStructStorageNotThrowing(members);
fast.isStructStorageNotThrowing(members);
System.out.println("Test 1: flat struct, 10 fields (no nesting)");
System.out.println(" Slow ops: " + slow.opCount + ", Fast ops: " + fast.opCount);
check("both handle flat struct (ops >= 0)", slow.opCount >= 0 && fast.opCount >= 0);
}
// ---- Test 2: Nested struct (5 top-level, each nested 10 fields) ----
{
// Build shared nested fields (same pointers = dedup matters)
List<Field> sharedNested = new ArrayList<>();
for (int i = 0; i < 10; i++) sharedNested.add(new Field("nested_" + i));
List<Field> members = new ArrayList<>();
for (int i = 0; i < 5; i++) {
members.add(new Field("struct_" + i, sharedNested));
}
DefectiveStructAnalyzer slow = new DefectiveStructAnalyzer();
FixedStructAnalyzer fast = new FixedStructAnalyzer();
slow.isStructStorageNotThrowing(members);
fast.isStructStorageNotThrowing(members);
System.out.println("Test 2: 5 struct fields each with 10 nested fields");
System.out.println(" Slow ops: " + slow.opCount + ", Fast ops: " + fast.opCount);
check("slow > fast (vector scan > hash set)", slow.opCount >= fast.opCount);
}
// ---- Test 3: Large nested struct — O(M^2) regime ----
{
// 20 struct fields each with 30 unique nested fields
int topN = 20, nestedM = 30;
List<Field> members = new ArrayList<>();
for (int i = 0; i < topN; i++) {
List<Field> nested = new ArrayList<>();
for (int j = 0; j < nestedM; j++) nested.add(new Field("n_" + i + "_" + j));
members.add(new Field("s_" + i, nested));
}
DefectiveStructAnalyzer slow = new DefectiveStructAnalyzer();
FixedStructAnalyzer fast = new FixedStructAnalyzer();
slow.isStructStorageNotThrowing(members);
fast.isStructStorageNotThrowing(members);
System.out.println("Test 3: 20 struct fields × 30 nested fields each (M=" + topN*nestedM + ")");
System.out.println(" Slow ops: " + slow.opCount + ", Fast ops: " + fast.opCount);
double ratio = (double) slow.opCount / Math.max(fast.opCount, 1);
System.out.printf(" Ratio slow/fast: %.1fx%n", ratio);
check("slow > 5x fast for M=600", ratio > 5.0);
}
// ---- Test 4: O(M^2) scaling — doubling members quadruples slow ops ----
{
// All unique nested fields → no early termination in std::find
int[] sizes = {10, 20};
long[] slowOps = new long[2];
for (int s = 0; s < 2; s++) {
int N = sizes[s];
List<Field> members = new ArrayList<>();
for (int i = 0; i < N; i++) {
List<Field> nested = new ArrayList<>();
for (int j = 0; j < N; j++) nested.add(new Field("n_" + i + "_" + j));
members.add(new Field("struct_" + i, nested));
}
DefectiveStructAnalyzer slow = new DefectiveStructAnalyzer();
slow.isStructStorageNotThrowing(members);
slowOps[s] = slow.opCount;
}
System.out.println("Test 4: O(M^2) scaling");
System.out.println(" N=10 ops: " + slowOps[0]);
System.out.println(" N=20 ops: " + slowOps[1]);
double ratio = (double) slowOps[1] / slowOps[0];
System.out.printf(" Scaling: %.2fx (expect >3x for O(M^2))%n", ratio);
check("ops grow super-linearly when N doubled (>3x)", ratio > 3.0);
}
// ---- Test 5: Correctness — same result (noexcept) ----
{
List<Field> nested = Arrays.asList(new Field("x"), new Field("y"));
List<Field> members = Arrays.asList(
new Field("a", nested),
new Field("b", nested),
new Field("c")
);
DefectiveStructAnalyzer slow = new DefectiveStructAnalyzer();
FixedStructAnalyzer fast = new FixedStructAnalyzer();
boolean slowResult = slow.isStructStorageNotThrowing(members);
boolean fastResult = fast.isStructStorageNotThrowing(members);
System.out.println("Test 5: correctness — both return same noexcept result");
check("slow and fast return same result", slowResult == fastResult);
}
System.out.println();
System.out.println(passed + "/" + total + " PASS");
}
}