%%%------------------------------------------------------------------- %% @doc GPU Speedup and Performance Optimization Demo %% Demonstrates massive performance improvements with MLX GPU acceleration %% @end %%%------------------------------------------------------------------- -module(gpu_speedup_demo). -export([extreme_speedup_demo/0, parallel_training/0, memory_optimization/0]). %% @doc Demonstrate extreme GPU speedups with large-scale operations extreme_speedup_demo() -> io:format("=== EXTREME GPU SPEEDUP DEMONSTRATION ===~n~n"), % Add MLX to path code:add_path("mlx/_build/default/lib/mlx/ebin"), % Test progressively larger matrix operations Sizes = [500, 1000, 2000, 4000], lists:foreach(fun(Size) -> io:format("Testing ~px~p matrix operations:~n", [Size, Size]), benchmark_matrix_size(Size) end, Sizes), % Demonstrate neural network speedups io:format("~nTesting neural network training speedups:~n"), neural_network_speedup_test(), % Memory bandwidth tests io:format("~nTesting memory bandwidth optimizations:~n"), memory_bandwidth_test(), io:format("~nExtreme speedup demonstration complete!~n"). %% @doc Benchmark specific matrix size benchmark_matrix_size(Size) -> A = mlx:random([Size, Size], float32), B = mlx:random([Size, Size], float32), % CPU benchmark mlx:set_default_device(cpu), CPUTime = time_operation(fun() -> C = mlx:matmul(A, B), mlx:eval(C) end), % GPU benchmark try mlx:set_default_device(gpu), GPUTime = time_operation(fun() -> C = mlx:matmul(A, B), mlx:eval(C) end), Speedup = CPUTime / GPUTime, GFLOPS_CPU = (2.0 * Size * Size * Size) / (CPUTime * 1000000), GFLOPS_GPU = (2.0 * Size * Size * Size) / (GPUTime * 1000000), io:format(" CPU: ~.2f ms (~.1f GFLOPS)~n", [CPUTime, GFLOPS_CPU]), io:format(" GPU: ~.2f ms (~.1f GFLOPS)~n", [GPUTime, GFLOPS_GPU]), io:format(" 🚀 SPEEDUP: ~.1fx faster on GPU!~n~n", [Speedup]) catch _:_ -> io:format(" CPU: ~.2f ms~n", [CPUTime]), io:format(" ⚠ GPU not available for this test~n~n") end. %% @doc Time a given operation time_operation(Fun) -> StartTime = erlang:system_time(microsecond), Fun(), EndTime = erlang:system_time(microsecond), (EndTime - StartTime) / 1000.0. % Convert to milliseconds %% @doc Test neural network training speedups neural_network_speedup_test() -> % Large neural network for realistic training BatchSize = 128, InputSize = 1024, HiddenSizes = [512, 256, 128], OutputSize = 10, % Create training data TrainData = mlx:random([BatchSize, InputSize], float32), Labels = mlx:random([BatchSize, OutputSize], float32), % Initialize network weights Network = init_large_network([InputSize | HiddenSizes] ++ [OutputSize]), io:format(" Network: ~p neurons across ~p layers~n", [InputSize + lists:sum(HiddenSizes) + OutputSize, length(HiddenSizes) + 1]), % CPU training mlx:set_default_device(cpu), CPUTime = time_operation(fun() -> train_batch(Network, TrainData, Labels) end), % GPU training try mlx:set_default_device(gpu), GPUTime = time_operation(fun() -> train_batch(Network, TrainData, Labels) end), Speedup = CPUTime / GPUTime, io:format(" CPU training: ~.2f ms~n", [CPUTime]), io:format(" GPU training: ~.2f ms~n", [GPUTime]), io:format(" 🚀 Neural Network Training Speedup: ~.1fx!~n~n", [Speedup]) catch _:_ -> io:format(" CPU training: ~.2f ms~n", [CPUTime]), io:format(" ⚠ GPU training not available~n~n") end. %% @doc Initialize large neural network init_large_network(LayerSizes) -> init_network_layers(LayerSizes, []). init_network_layers([_], Acc) -> lists:reverse(Acc); init_network_layers([In, Out | Rest], Acc) -> Scale = math:sqrt(2.0 / In), Weights = mlx:multiply(mlx:random([In, Out], float32), Scale), Biases = mlx:zeros([Out], float32), Layer = #{weights => Weights, biases => Biases}, init_network_layers([Out | Rest], [Layer | Acc]). %% @doc Train a single batch train_batch(Network, Data, Labels) -> % Forward pass Activations = forward_pass_large(Network, Data), % Compute loss Loss = mlx:mean(mlx:square(mlx:subtract(Activations, Labels))), % Force evaluation mlx:eval(Loss), Loss. %% @doc Forward pass through large network forward_pass_large(Network, Input) -> lists:foldl(fun(Layer, Activation) -> #{weights := W, biases := B} = Layer, Linear = mlx:add(mlx:matmul(Activation, W), B), mlx:relu(Linear) end, Input, Network). %% @doc Test memory bandwidth optimizations memory_bandwidth_test() -> % Test different data types and sizes Sizes = [1000000, 5000000, 10000000], % 1M, 5M, 10M elements lists:foreach(fun(Size) -> io:format(" Testing ~p element array operations:~n", [Size]), % Create large arrays A = mlx:random([Size], float32), B = mlx:random([Size], float32), % CPU memory bandwidth test mlx:set_default_device(cpu), CPUTime = time_operation(fun() -> Result = mlx:add(mlx:multiply(A, 2.0), B), mlx:eval(Result) end), % Calculate bandwidth (GB/s) - outside try block to avoid unsafe variables DataSize = Size * 4 * 3 / (1024 * 1024 * 1024), % 3 arrays * 4 bytes CPU_BW = DataSize / (CPUTime / 1000.0), % GPU memory bandwidth test try mlx:set_default_device(gpu), GPUTime = time_operation(fun() -> Result = mlx:add(mlx:multiply(A, 2.0), B), mlx:eval(Result) end), GPU_BW = DataSize / (GPUTime / 1000.0), io:format(" CPU: ~.2f ms (~.1f GB/s)~n", [CPUTime, CPU_BW]), io:format(" GPU: ~.2f ms (~.1f GB/s)~n", [GPUTime, GPU_BW]), io:format(" 🚀 Memory bandwidth improvement: ~.1fx~n", [GPU_BW / CPU_BW]) catch _:_ -> io:format(" CPU: ~.2f ms (~.1f GB/s)~n", [CPUTime, CPU_BW]) end end, Sizes). %% @doc Demonstrate parallel training across multiple models parallel_training() -> io:format("=== PARALLEL TRAINING DEMO ===~n~n"), code:add_path("mlx/_build/default/lib/mlx/ebin"), mlx:set_default_device(gpu), NumModels = 4, io:format("Training ~p models in parallel...~n", [NumModels]), % Create multiple models Models = lists:map(fun(I) -> io:format(" Initializing model ~p...~n", [I]), init_large_network([784, 256, 128, 10]) end, lists:seq(1, NumModels)), % Create training data for each model TrainingData = lists:map(fun(_) -> {mlx:random([100, 784], float32), mlx:random([100, 10], float32)} end, lists:seq(1, NumModels)), % Time parallel training StartTime = erlang:system_time(millisecond), % Train all models (simulated parallel execution) lists:foreach(fun({Model, {Data, Labels}}) -> train_batch(Model, Data, Labels) end, lists:zip(Models, TrainingData)), EndTime = erlang:system_time(millisecond), TotalTime = EndTime - StartTime, io:format("✓ Parallel training completed in ~p ms~n", [TotalTime]), io:format("✓ Average time per model: ~.2f ms~n", [TotalTime / NumModels]), ok. %% @doc Advanced memory optimization techniques memory_optimization() -> io:format("=== MEMORY OPTIMIZATION DEMO ===~n~n"), code:add_path("mlx/_build/default/lib/mlx/ebin"), % Test memory-efficient operations io:format("Testing in-place operations...~n"), test_inplace_operations(), % Test gradient accumulation io:format("Testing gradient accumulation...~n"), test_gradient_accumulation(), % Test memory pooling io:format("Testing memory pooling...~n"), test_memory_pooling(), io:format("Memory optimization demo complete!~n"). %% @doc Test in-place operations for memory efficiency test_inplace_operations() -> Size = [1000, 1000], A = mlx:random(Size, float32), % Standard operation (creates new memory) StartTime = erlang:system_time(microsecond), B = mlx:add(A, 1.0), C = mlx:multiply(B, 2.0), mlx:eval(C), StandardTime = (erlang:system_time(microsecond) - StartTime) / 1000.0, % In-place style operation (reuses memory where possible) StartTime2 = erlang:system_time(microsecond), D = mlx:multiply(mlx:add(A, 1.0), 2.0), % Fused operation mlx:eval(D), OptimizedTime = (erlang:system_time(microsecond) - StartTime2) / 1000.0, io:format(" Standard ops: ~.2f ms~n", [StandardTime]), io:format(" Optimized ops: ~.2f ms~n", [OptimizedTime]), io:format(" Memory efficiency improvement: ~.1fx~n", [StandardTime / OptimizedTime]). %% @doc Test gradient accumulation test_gradient_accumulation() -> % Simulate gradient accumulation for large batch training BatchSize = 32, InputSize = 512, AccumulatedGrad = mlx:zeros([InputSize, 10], float32), StartTime = erlang:system_time(microsecond), % Accumulate gradients from multiple mini-batches lists:foreach(fun(_) -> MiniBatch = mlx:random([BatchSize, InputSize], float32), Grad = mlx:random([InputSize, 10], float32), % Simulated gradient AccumulatedGrad = mlx:add(AccumulatedGrad, Grad) end, lists:seq(1, 4)), mlx:eval(AccumulatedGrad), Time = (erlang:system_time(microsecond) - StartTime) / 1000.0, io:format(" Gradient accumulation: ~.2f ms~n", [Time]). %% @doc Test memory pooling simulation test_memory_pooling() -> % Simulate memory pool by reusing arrays Size = [500, 500], StartTime = erlang:system_time(microsecond), % Create pool of reusable arrays Pool = lists:map(fun(_) -> mlx:zeros(Size, float32) end, lists:seq(1, 5)), % Simulate operations that reuse memory from pool lists:foreach(fun(Array) -> Result = mlx:add(Array, mlx:random(Size, float32)), mlx:eval(Result) end, Pool), Time = (erlang:system_time(microsecond) - StartTime) / 1000.0, io:format(" Memory pooling ops: ~.2f ms~n", [Time]), io:format(" Pool efficiency: Reused ~p arrays~n", [length(Pool)]).