Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ local/
pq_cache/
index_cache/

### Logging (or whatever you use)
logging/

### JVM crashes
hs_err_pid*
replay_pid*
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -29,39 +29,59 @@
public class BenchmarkTablePrinter {
private static final int MIN_COLUMN_WIDTH = 11;
private static final int MIN_HEADER_PADDING = 3;
private static final int MAX_COLUMN_WIDTH = 15; // tune as desired

private String headerFmt;
private String rowFmt;
private int[] colWidths;

public BenchmarkTablePrinter() {
headerFmt = null;
rowFmt = null;
}

/**
* Clears header/row formats so the next printed row will emit a fresh header.
* Call this when the set of columns may change (e.g., when topK changes).
*/
public void resetTable() {
headerFmt = null;
rowFmt = null;
colWidths = null;
}

private void initializeHeader(List<Metric> cols) {
if (headerFmt != null) {
return;
}
this.colWidths = new int[cols.size() + 1];

// Build the format strings for header & rows
StringBuilder hsb = new StringBuilder();
StringBuilder rsb = new StringBuilder();

// 1) Overquery column width
// Overquery column width
hsb.append("%-12s");
rsb.append("%-12.2f");
colWidths[0] = 12;

// 2) One column per Metric
int i = 0;
for (Metric m : cols) {
String hdr = m.getHeader();
String spec = m.getFmtSpec();
int width = Math.max(MIN_COLUMN_WIDTH, hdr.length() + MIN_HEADER_PADDING);
width = Math.min(width, MAX_COLUMN_WIDTH);

colWidths[i + 1] = width;

// Header: Always a string
hsb.append(" %-").append(width).append("s");
// Row: Use the Metric’s fmtSpec (e.g. ".2f", ".3f")
rsb.append(" %-").append(width).append(spec);

i++;
}

this.headerFmt = hsb.toString();
Expand All @@ -72,33 +92,55 @@ private void initializeHeader(List<Metric> cols) {
}

/**
* Call this once to print all the global parameters before the table.
* Prints the run-wide configuration header (index settings followed by query settings)
* once per run, before any results table output.
*
* @param params a map from parameter name (e.g. "mGrid") to its List value
* Iteration order is preserved (use an insertion-ordered map such as {@link java.util.LinkedHashMap}).
*
* @param indexParams ordered map of index-construction parameters to print
* @param queryParams ordered map of query/search parameters to print
*/
public void printConfig(Map<String, ?> params) {
System.out.println();
System.out.println("Configuration:");
params.forEach((name, values) ->
System.out.printf(Locale.US, " %-22s: %s%n", name, values)
);
public void printConfig(Map<String, ?> indexParams, Map<String, ?> queryParams) {
printSection("\nIndex configuration", indexParams);
printSection("\nQuery configuration", queryParams);
}

private void printSection(String title, Map<String, ?> params) {
System.out.println(title + ":");
for (var e : params.entrySet()) {
System.out.printf(" %-20s %s%n", e.getKey(), String.valueOf(e.getValue()));
}
}

private void printHeader(List<Metric> cols) {
// Prepare array: First "Overquery", then each Metric header
Object[] hdrs = new Object[cols.size() + 1];
hdrs[0] = "Overquery";
// Two header lines: split long headers onto a second line when possible
Object[] hdrs1 = new Object[cols.size() + 1];
Object[] hdrs2 = new Object[cols.size() + 1];

hdrs1[0] = "Overquery";
hdrs2[0] = "";

boolean anySecondLine = false;

for (int i = 0; i < cols.size(); i++) {
hdrs[i + 1] = cols.get(i).getHeader();
String hdr = cols.get(i).getHeader();
int width = colWidths[i + 1];

String[] parts = splitHeader2(hdr, width);
hdrs1[i + 1] = parts[0];
hdrs2[i + 1] = parts[1];
if (!parts[1].isEmpty()) anySecondLine = true;
}

// Print header line
String line = String.format(Locale.US, headerFmt, hdrs);
System.out.println(line);
// Underline of same length
System.out.println(String.join("",
Collections.nCopies(line.length(), "-")
));
String line1 = String.format(Locale.US, headerFmt, hdrs1);
System.out.println(line1);

if (anySecondLine) {
String line2 = String.format(Locale.US, headerFmt, hdrs2);
System.out.println(line2);
}

System.out.println(String.join("", Collections.nCopies(line1.length(), "-")));
}

/**
Expand All @@ -109,7 +151,7 @@ private void printHeader(List<Metric> cols) {
*/
public void printRow(double overquery,
List<Metric> cols) {
initializeHeader(cols);
initializeHeader(cols); // lazy: prints header on the first row after resetTable()

// Build argument array: First overquery, then each Metric.extract(...)
Object[] vals = new Object[cols.size() + 1];
Expand All @@ -129,4 +171,31 @@ public void printRow(double overquery,
public void printFooter() {
System.out.println();
}

// Helper for splitting the header into two rows
private static String[] splitHeader2(String hdr, int colWidth) {
if (hdr == null) return new String[] { "", "" };

// Manual break: "Line1\nLine2"
int nl = hdr.indexOf('\n');
if (nl >= 0) {
String a = hdr.substring(0, nl).trim();
String b = hdr.substring(nl + 1).trim();
return new String[] { a, b };
}

// Leave a little slack for padding/alignment
int max = Math.max(1, colWidth - MIN_HEADER_PADDING);

String s = hdr.trim();
if (s.length() <= max) return new String[] { s, "" };

// Find last space before max; if none, hard-split
int cut = s.lastIndexOf(' ', max);
if (cut <= 0) cut = max;

String a = s.substring(0, cut).trim();
String b = s.substring(cut).trim();
return new String[] { a, b };
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ public static Metric of(String key, String header, String fmtSpec, double value)

@Override
public String toString() {
return String.format(header + " = " + fmtSpec, value);
// Create a safe template: "%s = %" + fmtSpec
String template = "%s = %" + fmtSpec;

// Pass 'header' as the first argument (%s) and 'value' as the second argument (%.1f)
return String.format(template, header, value);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,25 @@ public List<Metric> run(
var diskSnapshot = diagnostics.getLatestDiskSnapshot();

if (systemSnapshot != null) {
results.add(Metric.of("search.system.max_heap_mb", "Max heap usage", ".1f",
// Max heap usage in MB
results.add(Metric.of("search.system.max_heap_mb", "Max heap usage (MB)", ".1f",
systemSnapshot.memoryStats.heapUsed / (1024.0 * 1024.0)));

results.add(Metric.of("search.system.max_offheap_mb", "Max offheap usage", ".1f",
// Max off-heap usage (direct + mapped) in MB
results.add(Metric.of("search.system.max_offheap_mb", "Max offheap usage (MB)", ".1f",
systemSnapshot.memoryStats.getTotalOffHeapMemory() / (1024.0 * 1024.0)));
}

if (diskSnapshot != null) {
// Number of index files created
results.add(Metric.of("search.disk.file_count", "File count", ".0f",
diskSnapshot.fileCount));

// Total size of index files created
results.add(Metric.of("search.disk.total_file_size_mb", "Total file size (MB)", ".1f",
diskSnapshot.totalBytes / (1024.0 * 1024.0)));
}

} catch (IOException e) {
throw new RuntimeException(e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ public List<Metric> runBenchmark(
var list = new ArrayList<Metric>();
if (computeAvgQps) {
list.add(Metric.of("search.throughput.avg_qps",
"Avg QPS (of " + numTestRuns + ")",
"Avg QPS\n (of " + numTestRuns + ")",
formatAvgQps,
avgQps));

Expand All @@ -273,18 +273,18 @@ public List<Metric> runBenchmark(
}
if (computeMedianQps) {
list.add(Metric.of("search.throughput.median_qps",
"Median QPS (of " + numTestRuns + ")",
"Median QPS\n (of " + numTestRuns + ")",
formatMedianQps,
medianQps));
}
if (computeMaxQps) {
list.add(Metric.of("search.throughput.max_qps",
"Max QPS (of " + numTestRuns + ")",
"Max QPS\n (of " + numTestRuns + ")",
formatMaxQps,
maxQps));

list.add(Metric.of("search.throughput.min_qps",
"Min QPS (of " + numTestRuns + ")",
"Min QPS\n (of " + numTestRuns + ")",
formatMaxQps,
minQps));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ public static List<String> unionLoggingMetricKeys(RunConfig runCfg, List<MultiCo
var ctx = ReportingSelectionResolver.Context.of("topK", Integer.toString(topK));
var resolved = ReportingSelectionResolver.resolve(runCfg.logging, SearchReportingCatalog.catalog(), ctx);
union.addAll(resolved.keys());
expandPrefixes(union, resolved.prefixes(), allConfigs);
}

// 2) Emit keys in canonical order, only if present in union
Expand Down Expand Up @@ -110,4 +111,61 @@ private static void addIfPresent(Set<String> union, List<String> ordered, String
ordered.add(key);
}
}

private static void expandPrefixes(Set<String> union,
Set<String> prefixes,
List<MultiConfig> allConfigs) {
if (prefixes == null || prefixes.isEmpty()) return;

// Collect quant types from YAML configs (domain is defined by yaml `compression.type` and `reranking`)
Set<String> indexQuantTypes = new HashSet<>();
Set<String> searchQuantTypes = new HashSet<>();
boolean wantsNVQ = false;

for (MultiConfig cfg : allConfigs) {
if (cfg == null) continue;

// construction compression types (PQ/BQ/None)
if (cfg.construction != null && cfg.construction.compression != null) {
for (var c : cfg.construction.compression) {
if (c != null && c.type != null && !"None".equals(c.type)) {
indexQuantTypes.add(c.type); // "PQ" or "BQ"
}
}
}

// construction reranking types (FP/NVQ)
if (cfg.construction != null && cfg.construction.reranking != null) {
if (cfg.construction.reranking.contains("NVQ")) {
wantsNVQ = true;
}
}

// search compression types (PQ/BQ/None)
if (cfg.search != null && cfg.search.compression != null) {
for (var c : cfg.search.compression) {
if (c != null && c.type != null && !"None".equals(c.type)) {
searchQuantTypes.add(c.type); // "PQ" or "BQ"
}
}
}
}

for (String p : prefixes) {
if ("construction.index_quant_time_s".equals(p)) {
for (String qt : indexQuantTypes) {
union.add(p + "." + qt + ".compute_time_s");
union.add(p + "." + qt + ".encoding_time_s");
}
if (wantsNVQ) {
union.add(p + ".NVQ.compute_time_s"); // we intentionally do not time NVQ encode
}
} else if ("search.search_quant_time_s".equals(p)) {
for (String qt : searchQuantTypes) {
union.add(p + "." + qt + ".compute_time_s");
union.add(p + "." + qt + ".encoding_time_s");
}
}
}
}
}
Loading