Skip to content

Export Module

Functions for exporting and displaying profiling results.

Export Functions

export_csv

stichotrope.export.export_csv(results, file=None)

Export profiling results to CSV format matching CppProfiler.

CSV Format

Track,Block Name,Hit Count,Total Time (ns),Avg Time (ns),Min Time (ns),Max Time (ns),% Track,% Total

Parameters:

Name Type Description Default
results ProfilerResults

ProfilerResults to export

required
file Optional[TextIO]

Optional file object to write to (if None, returns string)

None

Returns:

Type Description
str

CSV string if file is None, otherwise empty string

Source code in stichotrope/export.py
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
def export_csv(results: ProfilerResults, file: Optional[TextIO] = None) -> str:
    """
    Export profiling results to CSV format matching CppProfiler.

    CSV Format:
        Track,Block Name,Hit Count,Total Time (ns),Avg Time (ns),Min Time (ns),Max Time (ns),% Track,% Total

    Args:
        results: ProfilerResults to export
        file: Optional file object to write to (if None, returns string)

    Returns:
        CSV string if file is None, otherwise empty string
    """
    output = StringIO()
    writer = csv.writer(output)

    # Write header
    writer.writerow(
        [
            "Track",
            "Block Name",
            "Hit Count",
            "Total Time (ns)",
            "Avg Time (ns)",
            "Min Time (ns)",
            "Max Time (ns)",
            "% Track",
            "% Total",
        ]
    )

    # Calculate total time across all tracks
    total_time_all = results.total_time_ns

    # Write data rows
    for track_idx in sorted(results.tracks.keys()):
        track = results.tracks[track_idx]
        track_total_time = track.total_time_ns

        for block_idx in sorted(track.blocks.keys()):
            block = track.blocks[block_idx]

            # Calculate percentages
            pct_track = (
                (block.total_time_ns / track_total_time * 100) if track_total_time > 0 else 0.0
            )
            pct_total = (block.total_time_ns / total_time_all * 100) if total_time_all > 0 else 0.0

            # Handle min_time_ns initialization value
            min_time = block.min_time_ns if block.hit_count > 0 else 0

            writer.writerow(
                [
                    track_idx,
                    block.name,
                    block.hit_count,
                    block.total_time_ns,
                    f"{block.avg_time_ns:.0f}",
                    min_time,
                    block.max_time_ns,
                    f"{pct_track:.2f}",
                    f"{pct_total:.2f}",
                ]
            )

    csv_str = output.getvalue()

    if file is not None:
        file.write(csv_str)
        return ""
    else:
        return csv_str

export_json

stichotrope.export.export_json(results, file=None, indent=2)

Export profiling results to JSON format.

JSON Structure

{ "profiler_name": "...", "tracks": [ { "track_idx": 0, "track_name": "...", "blocks": [ { "name": "...", "file": "...", "line": 123, "hit_count": 10, "total_time_ns": 1000000, "avg_time_ns": 100000.0, "min_time_ns": 90000, "max_time_ns": 110000 } ] } ] }

Parameters:

Name Type Description Default
results ProfilerResults

ProfilerResults to export

required
file Optional[TextIO]

Optional file object to write to (if None, returns string)

None
indent int

JSON indentation level

2

Returns:

Type Description
str

JSON string if file is None, otherwise empty string

Source code in stichotrope/export.py
 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
161
162
def export_json(results: ProfilerResults, file: Optional[TextIO] = None, indent: int = 2) -> str:
    """
    Export profiling results to JSON format.

    JSON Structure:
        {
            "profiler_name": "...",
            "tracks": [
                {
                    "track_idx": 0,
                    "track_name": "...",
                    "blocks": [
                        {
                            "name": "...",
                            "file": "...",
                            "line": 123,
                            "hit_count": 10,
                            "total_time_ns": 1000000,
                            "avg_time_ns": 100000.0,
                            "min_time_ns": 90000,
                            "max_time_ns": 110000
                        }
                    ]
                }
            ]
        }

    Args:
        results: ProfilerResults to export
        file: Optional file object to write to (if None, returns string)
        indent: JSON indentation level

    Returns:
        JSON string if file is None, otherwise empty string
    """
    data: dict[str, Any] = {"profiler_name": results.profiler_name, "tracks": []}

    for track_idx in sorted(results.tracks.keys()):
        track = results.tracks[track_idx]
        track_data: dict[str, Any] = {
            "track_idx": track.track_idx,
            "track_name": track.track_name,
            "enabled": track.enabled,
            "blocks": [],
        }

        for block_idx in sorted(track.blocks.keys()):
            block = track.blocks[block_idx]

            # Handle min_time_ns initialization value
            min_time = block.min_time_ns if block.hit_count > 0 else 0

            block_data = {
                "name": block.name,
                "file": block.file,
                "line": block.line,
                "hit_count": block.hit_count,
                "total_time_ns": block.total_time_ns,
                "avg_time_ns": block.avg_time_ns,
                "min_time_ns": min_time,
                "max_time_ns": block.max_time_ns,
            }
            track_data["blocks"].append(block_data)

        data["tracks"].append(track_data)

    json_str = json.dumps(data, indent=indent)

    if file is not None:
        file.write(json_str)
        return ""
    else:
        return json_str

Display Functions

stichotrope.export.print_results(results)

Print profiling results to console in a formatted table.

Parameters:

Name Type Description Default
results ProfilerResults

ProfilerResults to print

required
Source code in stichotrope/export.py
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
def print_results(results: ProfilerResults) -> None:
    """
    Print profiling results to console in a formatted table.

    Args:
        results: ProfilerResults to print
    """
    print("=" * 120)
    print(f"Profiler: {results.profiler_name}")
    print(f"Total Time: {format_time_ns(results.total_time_ns)}")
    print(f"Total Hits: {results.total_hits:,}")
    print("=" * 120)

    for track_idx in sorted(results.tracks.keys()):
        track = results.tracks[track_idx]
        track_name = track.track_name if track.track_name else f"Track {track_idx}"

        print(f"\n{track_name} (Track {track_idx})")
        print(f"  Total Time: {format_time_ns(track.total_time_ns)}")
        print(f"  Total Hits: {track.total_hits:,}")
        print("-" * 120)

        # Print header
        print(
            f"{'Block Name':<40} {'Hits':>10} {'Total':>15} {'Avg':>15} {'Min':>15} {'Max':>15} {'%Track':>8}"
        )
        print("-" * 120)

        # Print blocks
        for block_idx in sorted(track.blocks.keys()):
            block = track.blocks[block_idx]

            # Calculate percentage of track
            pct_track = (
                (block.total_time_ns / track.total_time_ns * 100)
                if track.total_time_ns > 0
                else 0.0
            )

            # Handle min_time_ns initialization value
            min_time = block.min_time_ns if block.hit_count > 0 else 0

            print(
                f"{block.name:<40} "
                f"{block.hit_count:>10,} "
                f"{format_time_ns(block.total_time_ns):>15} "
                f"{format_time_ns(int(block.avg_time_ns)):>15} "
                f"{format_time_ns(min_time):>15} "
                f"{format_time_ns(block.max_time_ns):>15} "
                f"{pct_track:>7.2f}%"
            )

    print("=" * 120)

format_time_ns

stichotrope.export.format_time_ns(time_ns)

Format nanoseconds to human-readable time units.

Parameters:

Name Type Description Default
time_ns int

Time in nanoseconds

required

Returns:

Type Description
str

Formatted string (e.g., "1.23 ms", "456 μs", "789 ns")

Source code in stichotrope/export.py
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
def format_time_ns(time_ns: int) -> str:
    """
    Format nanoseconds to human-readable time units.

    Args:
        time_ns: Time in nanoseconds

    Returns:
        Formatted string (e.g., "1.23 ms", "456 μs", "789 ns")
    """
    if time_ns >= 1_000_000_000:  # >= 1 second
        return f"{time_ns / 1_000_000_000:.2f} s"
    elif time_ns >= 1_000_000:  # >= 1 millisecond
        return f"{time_ns / 1_000_000:.2f} ms"
    elif time_ns >= 1_000:  # >= 1 microsecond
        return f"{time_ns / 1_000:.2f} μs"
    else:
        return f"{time_ns} ns"

Usage Examples

CSV Export

from stichotrope import Profiler, export_csv

profiler = Profiler("MyApp")

# ... run profiled code ...

# Export to CSV
results = profiler.get_results()
export_csv(results, "profiling_results.csv")

CSV Format:

The CSV file contains the following columns:

  • track_idx: Track index
  • block_idx: Block index within track
  • name: Block name
  • file: Source file path
  • line: Line number
  • duration_ns: Execution duration in nanoseconds
  • duration_ms: Execution duration in milliseconds
  • duration_s: Execution duration in seconds

JSON Export

from stichotrope import Profiler, export_json

profiler = Profiler("MyApp")

# ... run profiled code ...

# Export to JSON
results = profiler.get_results()
export_json(results, "profiling_results.json")

JSON Format:

{
  "profiler_name": "MyApp",
  "tracks": [
    {
      "track_idx": 0,
      "blocks": [
        {
          "block_idx": 0,
          "name": "process_data",
          "file": "/path/to/file.py",
          "line": 42,
          "duration_ns": 1234567,
          "duration_ms": 1.234567,
          "duration_s": 0.001234567
        }
      ]
    }
  ]
}

Console Output

from stichotrope import Profiler, print_results

profiler = Profiler("MyApp")

# ... run profiled code ...

# Print to console
results = profiler.get_results()
print_results(results)

Console Output Format:

Profiler: MyApp
Track 0:
  Block 0 (process_data): 1.23 ms
  Block 1 (transform_data): 2.45 ms
Track 1:
  Block 0 (database_query): 10.50 ms
  Block 1 (cache_lookup): 0.15 ms

Time Formatting

from stichotrope import format_time_ns

# Format nanoseconds to human-readable string
duration_ns = 1234567890
formatted = format_time_ns(duration_ns)
print(formatted)  # "1.23 s" or "1234.57 ms" depending on magnitude

CppProfiler Compatibility

The CSV export format is compatible with CppProfiler, allowing you to:

  • Use the same analysis tools for Python and C++ profiling data
  • Compare performance between Python and C++ implementations
  • Integrate Python profiling into existing CppProfiler workflows

Compatibility Notes

  • Column names match CppProfiler CSV format
  • Duration values provided in multiple units (ns, ms, s)
  • Track and block indices use the same semantics

Advanced Usage

Conditional Export

from stichotrope import Profiler, export_csv, export_json

profiler = Profiler("MyApp")

# ... run profiled code ...

results = profiler.get_results()

# Export only if profiling was enabled
if results.tracks:
    export_csv(results, "results.csv")
    export_json(results, "results.json")

Custom Export Path

import os
from datetime import datetime
from stichotrope import Profiler, export_csv

profiler = Profiler("MyApp")

# ... run profiled code ...

# Create timestamped export
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
export_dir = "profiling_results"
os.makedirs(export_dir, exist_ok=True)

export_path = os.path.join(export_dir, f"profile_{timestamp}.csv")
export_csv(profiler.get_results(), export_path)

Multiple Export Formats

from stichotrope import Profiler, export_csv, export_json, print_results

profiler = Profiler("MyApp")

# ... run profiled code ...

results = profiler.get_results()

# Export in all formats
print_results(results)  # Console output
export_csv(results, "results.csv")  # CSV for spreadsheets
export_json(results, "results.json")  # JSON for programmatic analysis

See Also