249 lines
9.8 KiB
Java
249 lines
9.8 KiB
Java
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");
|
||
}
|
||
}
|