{
  "what_this_is": "The values behind every table in the article 'M5 Pro vs M4 Pro: the training side of a Mac mini upgrade'. It is a transcript of the two training runs' own recorded telemetry, and separate matrix probes, with the ratios computed here. It is not a hardware reproduction kit: the training data, the model checkpoint and the run logs are internal.",
  "source": {
    "internal_measurement_note": "research/hardware/2026-09-25-mac-mini-m4-pro-vs-m5-pro-training-speed.md",
    "internal_note_revision": "a8e395a",
    "m5_full_run_completed": "2026-09-25 00:48 CEST",
    "generated_by": "editorial/m5-pro-vs-m4-pro-training/build-transcript.py"
  },
  "machines": {
    "m4_pro_64gb": {
      "model_identifier": "Mac16,11",
      "cpu": "14 cores",
      "gpu": "20 cores, Metal 4",
      "memory": "64 GB unified",
      "macos": "26.5 (25F71)",
      "python": "3.12.13",
      "mlx": "0.32.2"
    },
    "m5_pro_64gb": {
      "model_identifier": "Mac17,16",
      "cpu": "18 cores",
      "gpu": "20 cores, Metal 4",
      "memory": "64 GB unified",
      "macos": "27.0 (26A428)",
      "python": "3.12.14",
      "mlx": "0.32.2"
    },
    "shared": {
      "mlx_metal_library_sha256": "dc59d1cceb1a5c7e578232e6e41e28e2c73c9463ac6dbc3886c3ee17ffc270ed",
      "gpu_cores": "20 on both machines"
    }
  },
  "workload": {
    "model": "123M-parameter hybrid language model with a mixture-of-experts MLP",
    "stage": "Fine-tuning, 1 800 updates from a pretrained checkpoint",
    "batch": "4 x 1 024 tokens",
    "optimizer": "AdamW",
    "memory_policy": "layer rematerialization, chunked output head",
    "precision_of_compared_full_runs": "full float32 behaviour; M4 recorded MLX_ENABLE_TF32=1, M5 recorded 0",
    "controlled_arm": "20 updates with the same training recipe, data order, seed and starting weights; full float32 behaviour on both machines"
  },
  "ratio_definition": "speedup is M4 time / M5 time for seconds, M5 rate / M4 rate for GFLOP/s, and null for memory or missing comparisons",
  "measurements": [
    {
      "id": "training_seconds_per_update_full_run",
      "label": "Training, seconds per update, full 1 800-update run",
      "unit": "s",
      "m4_pro_64gb": 5.543,
      "m5_pro_64gb": 4.888,
      "speedup": 1.134
    },
    {
      "id": "training_seconds_per_update_identical_arm",
      "label": "Training, seconds per update, identical data, seed and weights (updates 2 to 19)",
      "unit": "s",
      "m4_pro_64gb": 5.547,
      "m5_pro_64gb": 4.832,
      "speedup": 1.148
    },
    {
      "id": "training_seconds_per_update_reduced_precision_arm",
      "label": "The same with the M5 Pro's reduced-precision matmul (not used for the published runs)",
      "unit": "s",
      "m4_pro_64gb": null,
      "m5_pro_64gb": 4.706,
      "speedup": null
    },
    {
      "id": "training_job_end_to_end",
      "label": "Training job end to end, 1 800 updates with validation, checkpoints, endpoint check and readout",
      "unit": "s",
      "m4_pro_64gb": 10527.0,
      "m5_pro_64gb": 9270.0,
      "speedup": 1.1356
    },
    {
      "id": "endpoint_generation_check",
      "label": "End-of-run generation check, 400 held-out prompts",
      "unit": "s",
      "m4_pro_64gb": 210.3,
      "m5_pro_64gb": 176.3,
      "speedup": 1.1929
    },
    {
      "id": "validation_pass",
      "label": "Validation pass over a fixed slice (mean of 13)",
      "unit": "s",
      "m4_pro_64gb": 14.6,
      "m5_pro_64gb": 12.4,
      "speedup": 1.1774
    },
    {
      "id": "generation_readout",
      "label": "Generation readout, same checkpoint, 400 prompts",
      "unit": "s",
      "m4_pro_64gb": 192.7,
      "m5_pro_64gb": 157.9,
      "speedup": 1.2204
    },
    {
      "id": "scoring_pass",
      "label": "Whole scoring pass, four readouts including model load",
      "unit": "s",
      "m4_pro_64gb": 315.0,
      "m5_pro_64gb": 257.0,
      "speedup": 1.2257
    },
    {
      "id": "peak_memory_training",
      "label": "Peak memory during training",
      "unit": "GiB",
      "m4_pro_64gb": 9.42,
      "m5_pro_64gb": 9.44,
      "speedup": null
    },
    {
      "id": "fp32_matmul_2048",
      "label": "float32 matmul, 2048 cubed, full precision",
      "unit": "GFLOP/s",
      "m4_pro_64gb": 4433.0,
      "m5_pro_64gb": 5736.0,
      "speedup": 1.2939
    },
    {
      "id": "reduced_precision_matmul_2048",
      "label": "The same matmul with the M5 Pro's default reduced-precision path",
      "unit": "GFLOP/s",
      "m4_pro_64gb": null,
      "m5_pro_64gb": 11548.0,
      "speedup": null
    }
  ],
  "controlled_training_trials": {
    "unit": "s/update",
    "updates_measured": "2 to 19 of a 20-update run",
    "m4_pro_full_precision": [
      5.547
    ],
    "m5_pro_full_precision": [
      4.832,
      4.846
    ],
    "m5_pro_default_precision": [
      4.706,
      4.715
    ],
    "headline_selection": "The table and charts use the first listed trial in each group, not a mean of trials.",
    "recorded_flags": {
      "m4_pro": 1,
      "m5_pro_full_precision": 0,
      "m5_pro_default_precision": 1
    }
  },
  "matmul_comparison_caveat": "M4 throughput came from an earlier provisioning probe with a different harness; cross-machine matmul ratios are approximate context, not a matched benchmark.",
  "derived": {
    "definition": "Ratios the article quotes that are not a row-against-row comparison in the table above.",
    "reduced_precision_training_arm_speedup": {
      "value": 1.0268,
      "quoted_as": "about 3 percent faster than the same arm at full precision"
    },
    "reduced_precision_matmul_vs_m4_full_precision": {
      "value": 2.605,
      "quoted_as": "about 2.6 times the M4 Pro's full-precision rate"
    }
  },
  "per_update_spread_full_run": {
    "m4_pro": {
      "p10": 5.45,
      "p90": 5.65,
      "unit": "s"
    },
    "m5_pro": {
      "p10": 4.82,
      "p90": 4.96,
      "unit": "s"
    }
  },
  "precision_switch_m5_pro": {
    "mlx_version": "0.32.2",
    "throughput_probe": "2048 cubed float32 matmul, 20 repetitions",
    "error_probe": "1024 cubed float32 matmul against float64",
    "relative_error_definition": "max(abs(result - reference)) / max(abs(reference))",
    "default_reduced_precision_path": {
      "float32_matmul_2048_gflops": 11548.0,
      "max_relative_error_vs_float64": 0.00082
    },
    "switch_off": {
      "float32_matmul_2048_gflops": 5736.0,
      "max_relative_error_vs_float64": 1.5e-06
    },
    "older_hardware_observations": "Toggling the flag did not change the M1 Max probe. M4 scoring recorded flag 1 and matched the M5 full-precision outputs; M4 was not re-probed after retirement.",
    "observed_with_default_path": {
      "rescored_outputs_changed": "9 of 1 600",
      "max_log_probability_shift_nats": 0.56,
      "first_training_loss_divergence": 0.00026
    },
    "observed_with_switch_off": {
      "scoring_outputs": "bit-identical to the M4 Pro",
      "training_losses_bit_identical": "16 of the first 20",
      "remaining_losses_within": 1e-06
    },
    "cost_of_switch_off_on_m5_pro": "about 3 percent on this training run",
    "upstream_clarification": {
      "url": "https://github.com/ml-explore/mlx/issues/3235",
      "status": "answered and closed 2026-03-10",
      "summary": "A maintainer states that the flag enables TF32 when NAX supports the operation."
    }
  }
}
