wave17 complete: systemd/emacs/vim/qemu/tcl/kafka-0007/spark-0004 + 559/240
This commit is contained in:
parent
4221966e66
commit
cce7ec653a
32 changed files with 3007 additions and 5 deletions
247
defects/tcl/unit/TclDoImportAlgorithm.java
Normal file
247
defects/tcl/unit/TclDoImportAlgorithm.java
Normal file
|
|
@ -0,0 +1,247 @@
|
|||
package unit;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* tcl-0001: DoImport O(C×P) export pattern scan per namespace import
|
||||
*
|
||||
* Models Tcl's namespace import machinery.
|
||||
* SLOW: for each of C commands, linear scan P export patterns (current Tcl code)
|
||||
* FAST: pre-build a HashSet of exported command names; O(1) per command check
|
||||
*
|
||||
* In Tcl_Import with wildcard pattern, DoImport is called for each of C commands
|
||||
* that match the import pattern. Each DoImport call scans P export patterns
|
||||
* using Tcl_StringMatch. Total: O(C × P).
|
||||
*/
|
||||
public class TclDoImportAlgorithm {
|
||||
|
||||
// Simple wildcard matcher (subset of Tcl_StringMatch)
|
||||
static boolean stringMatch(String str, String pattern) {
|
||||
if (pattern.equals("*")) return true;
|
||||
if (pattern.endsWith("*")) {
|
||||
String prefix = pattern.substring(0, pattern.length() - 1);
|
||||
return str.startsWith(prefix);
|
||||
}
|
||||
return str.equals(pattern);
|
||||
}
|
||||
|
||||
// --- SLOW: linear scan of export patterns per command (current DoImport) ---
|
||||
|
||||
static class SlowNamespace {
|
||||
Map<String, Object> cmdTable = new HashMap<>();
|
||||
List<String> exportPatterns = new ArrayList<>();
|
||||
long opCount = 0;
|
||||
|
||||
void defineCommand(String name) {
|
||||
cmdTable.put(name, new Object());
|
||||
}
|
||||
|
||||
void addExportPattern(String pattern) {
|
||||
exportPatterns.add(pattern);
|
||||
}
|
||||
|
||||
/** Check if cmdName is exported: linear scan over P patterns */
|
||||
boolean isExported(String cmdName) {
|
||||
for (String pattern : exportPatterns) {
|
||||
opCount++;
|
||||
if (stringMatch(cmdName, pattern)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulate Tcl_Import with wildcard (imports all commands matching importPat
|
||||
* that are also exported).
|
||||
* Outer: O(C) hash iteration; inner: O(P) export check per command = O(C*P).
|
||||
*/
|
||||
int importAll(String importPat) {
|
||||
int count = 0;
|
||||
for (String cmdName : cmdTable.keySet()) {
|
||||
if (stringMatch(cmdName, importPat)) {
|
||||
if (isExported(cmdName)) { // O(P) per command
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
// --- FAST: HashSet cache of exported command names ---
|
||||
|
||||
static class FastNamespace {
|
||||
Map<String, Object> cmdTable = new HashMap<>();
|
||||
List<String> exportPatterns = new ArrayList<>();
|
||||
Set<String> exportedCmdsCache = null; // null = needs rebuild
|
||||
long opCount = 0;
|
||||
|
||||
void defineCommand(String name) {
|
||||
cmdTable.put(name, new Object());
|
||||
exportedCmdsCache = null; // invalidate on new command
|
||||
}
|
||||
|
||||
void addExportPattern(String pattern) {
|
||||
exportPatterns.add(pattern);
|
||||
exportedCmdsCache = null; // invalidate on export change
|
||||
}
|
||||
|
||||
/** Build the exported-commands cache once: O(C * P) total, amortized O(1) per lookup */
|
||||
void buildExportCache() {
|
||||
exportedCmdsCache = new HashSet<>();
|
||||
for (String cmdName : cmdTable.keySet()) {
|
||||
for (String pattern : exportPatterns) {
|
||||
opCount++;
|
||||
if (stringMatch(cmdName, pattern)) {
|
||||
exportedCmdsCache.add(cmdName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Check if cmdName is exported: O(1) hash lookup */
|
||||
boolean isExported(String cmdName) {
|
||||
if (exportedCmdsCache == null) {
|
||||
buildExportCache();
|
||||
}
|
||||
opCount++;
|
||||
return exportedCmdsCache.contains(cmdName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulate Tcl_Import: O(C) outer, O(1) inner after cache build.
|
||||
* Cache is built once; subsequent imports are O(C).
|
||||
*/
|
||||
int importAll(String importPat) {
|
||||
int count = 0;
|
||||
for (String cmdName : cmdTable.keySet()) {
|
||||
if (stringMatch(cmdName, importPat)) {
|
||||
if (isExported(cmdName)) { // O(1) after cache
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
// --- Test harness ---
|
||||
|
||||
static int passed = 0;
|
||||
static int total = 0;
|
||||
|
||||
static void check(String name, boolean cond) {
|
||||
total++;
|
||||
if (cond) {
|
||||
passed++;
|
||||
System.out.println(" PASS: " + name);
|
||||
} else {
|
||||
System.out.println(" FAIL: " + name);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("=== tcl-0001: DoImport O(C*P) export pattern scan ===");
|
||||
|
||||
int C = 1000; // commands in source namespace (e.g., Tk, Itcl)
|
||||
int P = 50; // export patterns
|
||||
|
||||
SlowNamespace slow = new SlowNamespace();
|
||||
FastNamespace fast = new FastNamespace();
|
||||
|
||||
// Define C commands: half with "pub_" prefix (to be exported), half with "priv_"
|
||||
for (int i = 0; i < C / 2; i++) {
|
||||
slow.defineCommand("pub_cmd_" + i);
|
||||
fast.defineCommand("pub_cmd_" + i);
|
||||
}
|
||||
for (int i = 0; i < C / 2; i++) {
|
||||
slow.defineCommand("priv_cmd_" + i);
|
||||
fast.defineCommand("priv_cmd_" + i);
|
||||
}
|
||||
|
||||
// P export patterns: most export pub_cmd_* subsets
|
||||
// First pattern matches everything with prefix "pub_cmd_"
|
||||
slow.addExportPattern("pub_cmd_*");
|
||||
fast.addExportPattern("pub_cmd_*");
|
||||
// Add P-1 more specific patterns (won't match much, but still scanned)
|
||||
for (int i = 1; i < P; i++) {
|
||||
slow.addExportPattern("special_" + i + "_*");
|
||||
fast.addExportPattern("special_" + i + "_*");
|
||||
}
|
||||
|
||||
// First import: correctness check
|
||||
int slowCount = slow.importAll("*");
|
||||
int fastCount = fast.importAll("*");
|
||||
|
||||
check("slow imports correct count (" + (C/2) + ")", slowCount == C / 2);
|
||||
check("fast imports correct count (" + (C/2) + ")", fastCount == C / 2);
|
||||
check("slow and fast agree", slowCount == fastCount);
|
||||
|
||||
// Correctness: specific commands
|
||||
check("slow: pub_cmd_0 is exported", slow.isExported("pub_cmd_0"));
|
||||
check("fast: pub_cmd_0 is exported", fast.isExported("pub_cmd_0"));
|
||||
check("slow: priv_cmd_0 is NOT exported", !slow.isExported("priv_cmd_0"));
|
||||
check("fast: priv_cmd_0 is NOT exported", !fast.isExported("priv_cmd_0"));
|
||||
|
||||
// Op count comparison: measure 10 repeated imports (cache warm for fast)
|
||||
slow.opCount = 0;
|
||||
fast.opCount = 0;
|
||||
fast.exportedCmdsCache = null; // start fresh
|
||||
|
||||
int importRuns = 10;
|
||||
for (int r = 0; r < importRuns; r++) {
|
||||
slow.importAll("*");
|
||||
fast.importAll("*");
|
||||
}
|
||||
|
||||
long slowOps = slow.opCount;
|
||||
long fastOps = fast.opCount;
|
||||
double ratio = (double) slowOps / fastOps;
|
||||
|
||||
System.out.println();
|
||||
System.out.printf("C=%d commands, P=%d export patterns, %d import calls:%n", C, P, importRuns);
|
||||
System.out.printf(" SLOW ops (linear pattern scan per cmd): %,d%n", slowOps);
|
||||
System.out.printf(" FAST ops (cache build once + O(1) lookups): %,d%n", fastOps);
|
||||
System.out.printf(" Ratio: %.1fx%n", ratio);
|
||||
|
||||
// SLOW: O(C*P) per call × 10 calls (early exit on first match for half, full scan for other half)
|
||||
check("slow ops >= C*importRuns", slowOps >= (long) C * importRuns);
|
||||
// FAST: O(C*P) once for cache build, then O(C) per subsequent import
|
||||
// Total fast ≈ C*P + (importRuns-1)*C
|
||||
check("fast ops <= (C*P + importRuns*C*2)", fastOps <= (long)(C * P + importRuns * C * 2));
|
||||
check("ratio >= 5x", ratio >= 5.0);
|
||||
|
||||
// Additional: multiple imports reuse cache (fast stays O(C), slow stays O(C*P))
|
||||
slow.opCount = 0;
|
||||
fast.opCount = 0;
|
||||
int imports = 10;
|
||||
for (int i = 0; i < imports; i++) {
|
||||
slow.importAll("*");
|
||||
fast.importAll("*");
|
||||
}
|
||||
long slowOps2 = slow.opCount;
|
||||
long fastOps2 = fast.opCount;
|
||||
double ratio2 = (double) slowOps2 / fastOps2;
|
||||
|
||||
System.out.printf("%nAfter %d repeated imports (cache hot for fast):%n", imports);
|
||||
System.out.printf(" SLOW ops: %,d%n", slowOps2);
|
||||
System.out.printf(" FAST ops: %,d%n", fastOps2);
|
||||
System.out.printf(" Ratio: %.1fx%n", ratio2);
|
||||
|
||||
check("repeated import ratio >= 10x", ratio2 >= 10.0);
|
||||
|
||||
System.out.println();
|
||||
System.out.println(passed + "/" + total + " PASS");
|
||||
|
||||
if (passed != total) {
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue