File size: 5,389 Bytes
23e73f9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#!/usr/bin/env python3
"""
Print Gaussian count, timing, and VRAM for all our completed experiments.

Data sources (in priority order):
  - Gaussian count  : PLY header  (element vertex N)
  - iter_time_ms    : TensorBoard  train/iter_time_ms  (last value)
  - peak_vram_GB    : TensorBoard  train/peak_vram_GB  (max across run)
  - timing fallback : timing.json cumulative_gpu_time_sec / iters

Usage:
    python scripts/print_efficiency.py
"""
import os
import json
import glob
import numpy as np

OUTPUTS_ROOT = "/root/autodl-tmp/SplatAtlas/outputs"
ITERS = 30000

KNOWN = ("vanillasgf", "vanilla_3dgs", "vanilla_logged", "vanilla_thresh",
         "absgs", "pixelgs", "edgeloss")

OUTDOOR_360 = {"bicycle", "flowers", "garden", "stump", "treehill"}
INDOOR_360  = {"bonsai", "counter", "kitchen", "room"}
TNT = {
    "auditorium", "ballroom", "courtroom", "family", "francis", "horse",
    "lighthouse", "m60", "museum", "palace", "panther", "playground",
    "temple", "train"
}
ALL_SCENES = OUTDOOR_360 | INDOOR_360 | TNT


def is_ours(method):
    return any(method == p or method.startswith(p) for p in KNOWN)


def parse_dir(d):
    if d.startswith("vanilla_thresh_"):
        rest = d[len("vanilla_thresh_"):]
        for s in ALL_SCENES:
            if rest == s or rest.startswith(s + "_"):
                return "vanilla_thresh", s
        return None, None
    tok = d.split("_")
    for i in range(len(tok) - 1, 0, -1):
        sc = "_".join(tok[i:])
        me = "_".join(tok[:i])
        if sc in ALL_SCENES:
            return me, sc
    return None, None


def count_gaussians(ply_path):
    try:
        with open(ply_path, "rb") as f:
            for line in f:
                line = line.decode("ascii", errors="ignore").strip()
                if line.startswith("element vertex"):
                    return int(line.split()[-1])
                if line == "end_header":
                    break
    except Exception:
        pass
    return None


def read_tb(model_path):
    """Read last iter_time_ms and max peak_vram_GB from TensorBoard events."""
    try:
        from tensorboard.backend.event_processing.event_accumulator import EventAccumulator
        ea = EventAccumulator(model_path, size_guidance={"scalars": 0})
        ea.Reload()
        tags = ea.Tags().get("scalars", [])

        it_ms = None
        for tag in ["train/iter_time_ms", "iter_time_ms"]:
            if tag in tags:
                vals = ea.Scalars(tag)
                if vals:
                    # mean of last 10% steps (stable phase)
                    n = max(1, len(vals) // 10)
                    it_ms = float(np.mean([v.value for v in vals[-n:]]))
                break

        vram = None
        for tag in ["train/peak_vram_GB", "peak_vram_GB"]:
            if tag in tags:
                vals = ea.Scalars(tag)
                if vals:
                    vram = float(max(v.value for v in vals))
                break

        return it_ms, vram
    except Exception:
        return None, None


def read_timing_json(model_path, iters):
    """Fallback: cumulative_gpu_time_sec from timing.json."""
    try:
        p = os.path.join(model_path, "timing.json")
        d = json.load(open(p))
        cgt = d.get("cumulative_gpu_time_sec")
        if cgt:
            return float(cgt) / iters * 1000   # ms/iter
    except Exception:
        pass
    return None


def main():
    plys = sorted(glob.glob(
        f"{OUTPUTS_ROOT}/*/point_cloud/iteration_{ITERS}/point_cloud.ply"))

    rows = []
    for ply_path in plys:
        parts = ply_path.split("/")
        dirname = parts[-4]
        method, scene = parse_dir(dirname)
        if method is None or not is_ours(method):
            continue

        model_path = os.path.join(OUTPUTS_ROOT, dirname)
        n_gauss = count_gaussians(ply_path)
        it_ms, vram = read_tb(model_path)
        if it_ms is None:
            it_ms = read_timing_json(model_path, ITERS)

        rows.append((dirname, method, scene, n_gauss, it_ms, vram))

    # ── Print ──────────────────────────────────────────────────────────────
    print(f"\n{'Dir':<42} {'#Gauss':>10} {'ms/iter':>9} {'PeakGB':>8}")
    print("-" * 73)

    by_method = {}
    for dirname, method, scene, n, t, v in rows:
        ns = f"{n:,}" if n else "-"
        ts = f"{t:.2f}" if t else "-"
        vs = f"{v:.3f}" if v else "-"
        print(f"{dirname:<42} {ns:>10} {ts:>9} {vs:>8}")
        by_method.setdefault(method, []).append((n, t, v))

    # ── Per-method averages ────────────────────────────────────────────────
    print(f"\n{'Method':<35} {'Avg #Gauss':>11} {'Avg ms/iter':>12} {'Avg PeakGB':>11} {'N':>4}")
    print("-" * 67)
    for m, vals in sorted(by_method.items()):
        ns = [x[0] for x in vals if x[0] is not None]
        ts = [x[1] for x in vals if x[1] is not None]
        vs = [x[2] for x in vals if x[2] is not None]
        nav = f"{int(np.mean(ns)):,}" if ns else "-"
        tav = f"{np.mean(ts):.2f}" if ts else "-"
        vav = f"{np.mean(vs):.3f}" if vs else "-"
        print(f"{m:<35} {nav:>11} {tav:>12} {vav:>11} {len(vals):>4}")


if __name__ == "__main__":
    main()