3 minute read

Questo documento descrive come viene calcolata la memoria occupata da:

  • Variabili globali (prefissi gi_, gk_, ga_, gp_…)
  • Tabelle globali generate con ftgen

e come utilizzare lo script Python calc_mem.py per ottenere un report dettagliato.


1. Concetti chiave

  1. MYFLT
    • Tipo utilizzato da Csound per variabili e valori di table.
    • Di default a doppia precisione (64 bit → 8 byte per valore).
  2. Variabili globali
    • Definite nel .orc con init o =.
    • Ogni variabile occupa 8 byte.
  3. Tabelle ftgen
    • Un array di N punti + 1 guard point per l’interpolazione.
    • Memoria = (N + 1) × 8 byte.
  4. Macro $NOME
    • Sostituite automaticamente dallo script in gi_NOME per un parsing corretto.

2. Funzionamento dello script

  1. Parsing
    • Legge tutte le righe del file .orc.
    • Raccoglie definizioni di variabili globali (init, =).
    • Sostituisce macro $NOMEgi_NOME.
  2. Risoluzione variabili
    • Memorizza ogni espressione in var_exprs.
    • Risolve iterativamente con eval, finché riesce.
  3. Estrazione ftgen
    • Individua ogni riga con ftgen.
    • Valuta l’espressione di dimensione (anche se è gi_memory_duration / gi_memory_resolution o gi_state_history_size * 11).
    • Calcola base_points = int(size_val), total_points = base_points + 1, mem_bytes = total_points × 8.
  4. Report e CSV
    • Stampa su console i dettagli.
    • Esporta in CSV (memory_report.csv o nome a piacere) con:
      Type,Name,Line/Count,Expr/BasePoints,TotalPoints,MemBytes
      var,gi_Index,,,,8
      ...
      table,gi_eve_attacco,27,gi_NUMEVENTI/1000,1001,8008
      ...
      summary,,,,"MemVarsBytes",4096
      summary,,,,"MemTablesBytes",123456
      summary,,,,"TotalMemBytes",127552
      

      3. Esempio di esecuzione

python calc_mem.py MACROS/init.orc memoria.csv
  • Output console:
    📄 File: MACROS/init.orc
    🔢 Variabili globali risolte:  fifty → 400 byte
    📊 Tabelle ftgen trovate:
    riga 27: gi_eve_attacco size=gi_NUMEVENTI →1000+1 →1001 punti, mem=8008 byte
    ...
    🧠 Memoria tabelle: 123456 byte
    🧮 Memoria totale stimata: 127552 byte (124.75 kB)
    ✅ Dettagli esportati in memoria.csv
    
  • Contenuto di memoria.csv: Vedi sezione 2. —

    4. Come interpretare i risultati

  • MemVarsBytes: memoria totale occupata dalle variabili globali.
  • MemTablesBytes: memoria totale occupata dalle tabelle ftgen.
  • TotalMemBytes: somma delle due, stimata all’init-time di Csound.

5. Appendice

Script di python

import re
import sys
import csv

def parse_orc_memory(filepath, bytes_per_value=8, csv_path="memory_report.csv"):
    # 1) Regex per #define, init, assign, ftgen
    define_re = re.compile(r'^\s*#define\s+(\w+)\s+#(\d+)#')
    init_re   = re.compile(r'^\s*(g[akip]\w*)\s+init\s+(.+)$')
    assign_re = re.compile(r'^\s*(g[akip]\w*)\s*=\s*(.+)$')
    ftgen_re  = re.compile(r'^\s*(g[akip]\w*)\s+ftgen\s+[^,]+,\s*[^,]+,\s*([^,]+)')

    var_exprs  = {}
    var_values = {}

    # 2) Leggi file, processa define, sostituisci macro e raccogli definizioni
    lines = []
    with open(filepath) as f:
        for raw in f:
            # rimuovi newline
            no_nl = raw.rstrip('\n')
            # #define OTTAVE #10#
            mdef = define_re.match(no_nl)
            if mdef:
                key, val = mdef.groups()
                # salviamo direttamente come valore float
                var_values[f"gi_{key}"] = float(val)
                continue

            # rimuovi commenti
            no_comment = no_nl.split(';',1)[0].rstrip()
            # sostituisci macro $OTTAVE → gi_OTTAVE
            line = re.sub(r'\$(\w+)', r'gi_\1', no_comment)
            lines.append(line)

            # match init/assign
            m = init_re.match(line) or assign_re.match(line)
            if m:
                name, expr = m.groups()
                var_exprs[name.strip()] = expr.strip()

    # 3) Risolvi iterativamente le espressioni (init e assign)
    changed = True
    while changed:
        changed = False
        for name, expr in list(var_exprs.items()):
            if name in var_values:
                continue
            try:
                val = eval(expr, {}, var_values)
                var_values[name] = float(val)
                changed = True
            except Exception:
                continue

    # 4) Estrai e calcola le ftgen
    tables = []
    for lineno, line in enumerate(lines, 1):
        m = ftgen_re.match(line)
        if not m:
            continue
        name, size_expr = m.groups()
        try:
            size_val = eval(size_expr, {}, var_values)
        except Exception:
            print(f"⚠️ Warning: non ho potuto valutare “{size_expr}” a riga {lineno}")
            continue
        base_size = int(abs(size_val))
        points    = base_size + 1
        mem       = points * bytes_per_value
        tables.append({
            "line": lineno,
            "name": name,
            "expr": size_expr,
            "base_points": base_size,
            "total_points": points,
            "mem_bytes": mem
        })

    # 5) Calcola memoria variabili semplici
    simple_vars = sorted(var_values.keys())
    mem_simple  = len(simple_vars) * bytes_per_value
    mem_tables  = sum(t["mem_bytes"] for t in tables)
    total_mem   = mem_simple + mem_tables

    # 6) Stampa su console
    print(f"📄 File: {filepath}")
    print(f"🔢 Variabili globali risolte: {len(simple_vars)}{mem_simple} byte")
    print("📊 Tabelle ftgen trovate:")
    for t in tables:
        print(f"  riga {t['line']}: {t['name']} size={t['expr']} → "
              f"{t['base_points']}+1→{t['total_points']} punti, mem={t['mem_bytes']} byte")
    print(f"🧠 Memoria tabelle: {mem_tables} byte")
    print(f"🧮 Memoria totale stimata: {total_mem} byte ({total_mem/1024:.2f} kB)")

    # 7) Esporta in CSV
    with open(csv_path, "w", newline="") as csvfile:
        fieldnames = ["type","line","name","expr","base_points",
                      "total_points","mem_bytes"]
        writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
        writer.writeheader()
        # variabili
        for name in simple_vars:
            writer.writerow({
                "type":"var","line":"","name":name,"expr":"",
                "base_points":"","total_points":"",
                "mem_bytes":bytes_per_value
            })
        # tabelle
        for t in tables:
            writer.writerow({"type":"table", **t})

    print(f"\n✅ Report CSV scritto in: {csv_path}")


if __name__ == "__main__":
    if len(sys.argv) != 3:
        print("Usage: python memory_report.py <init.orc> <output.csv>")
        sys.exit(1)
    _, orc, csv_out = sys.argv
    parse_orc_memory(orc, csv_path=csv_out)

    # python calc_mem.py init.globalsInclude memoria.csv