#include #include #include #include #include #include #include #include #include #include #include namespace { class MetalBuffer { public: MetalBuffer() = default; MetalBuffer(const MetalBuffer&) = delete; MetalBuffer& operator=(const MetalBuffer&) = delete; MetalBuffer(MetalBuffer&& other) noexcept : buffer_(other.buffer_), has_value_(other.has_value_) { other.buffer_ = {}; other.has_value_ = false; } MetalBuffer& operator=(MetalBuffer&& other) noexcept { if (this != &other) { reset(); buffer_ = other.buffer_; has_value_ = other.has_value_; other.buffer_ = {}; other.has_value_ = false; } return *this; } ~MetalBuffer() { reset(); } gptoss_metal_buffer* get() { return &buffer_; } const gptoss_metal_buffer* get() const { return &buffer_; } void* ptr() const { return buffer_.ptr; } size_t size_bytes() const { return buffer_.size; } bool valid() const { return has_value_; } void wrap(const gptoss_metal_device* device, size_t size, const void* data) { reset(); TORCH_CHECK(gptoss_metal_buffer_wrap(device, size, data, &buffer_) == gptoss_status_success, "metal_buffer_wrap failed"); has_value_ = true; } void wrap_mps_tensor(const gptoss_metal_device* device, const at::Tensor& tensor) { reset(); TORCH_CHECK(tensor.is_contiguous(), "tensor must be contiguous for MPS wrap"); const size_t size_bytes = static_cast(tensor.numel()) * tensor.element_size(); const size_t byte_offset = static_cast(tensor.storage_offset()) * tensor.element_size(); const void* buffer_handle = tensor.storage().data(); TORCH_CHECK( gptoss_metal_buffer_wrap_mtlbuffer(device, buffer_handle, size_bytes, byte_offset, &buffer_) == gptoss_status_success, "metal_buffer_wrap_mtlbuffer failed"); has_value_ = true; } void create(const gptoss_metal_device* device, size_t size, const void* data = nullptr) { reset(); TORCH_CHECK(gptoss_metal_buffer_create(device, size, data, &buffer_) == gptoss_status_success, "metal_buffer_create failed"); has_value_ = true; } void reset() { if (has_value_) { (void) gptoss_metal_buffer_release(&buffer_); buffer_ = {}; has_value_ = false; } } private: gptoss_metal_buffer buffer_{}; bool has_value_ = false; }; template void run_metal_kernel(const char* kernel_symbol, EncodeFn&& encode_fn) { gptoss_metal_device device{}; gptoss_metal_library library{}; gptoss_metal_function fn{}; gptoss_metal_command_queue cq{}; gptoss_metal_command_buffer cb{}; bool borrowed_queue = false; bool borrowed_buffer = false; at::mps::MPSStream* stream = nullptr; auto cleanup = [&]() { if (!borrowed_buffer) { (void) gptoss_metal_command_buffer_release(&cb); } else { cb = {}; } if (!borrowed_queue) { (void) gptoss_metal_command_queue_release(&cq); } else { cq = {}; } (void) gptoss_metal_function_release(&fn); (void) gptoss_metal_library_release(&library); (void) gptoss_metal_device_release(&device); }; TORCH_CHECK(gptoss_metal_device_create_system_default(&device) == gptoss_status_success, "device_create failed"); try { TORCH_CHECK(gptoss_metal_library_create_default(&device, &library) == gptoss_status_success, "library_create failed"); TORCH_CHECK(gptoss_metal_function_create(&library, kernel_symbol, &fn) == gptoss_status_success, "function_create failed"); stream = at::mps::getCurrentMPSStream(); if (stream != nullptr) { // stream->synchronize(at::mps::SyncType::COMMIT_AND_WAIT); stream->endKernelCoalescing(); void* command_queue_obj = stream->commandQueue(); if (command_queue_obj != nullptr) { TORCH_CHECK( gptoss_metal_command_queue_wrap_existing(command_queue_obj, &cq) == gptoss_status_success, "wrap_command_queue failed"); borrowed_queue = true; void* command_buffer_obj = stream->commandBuffer(); if (command_buffer_obj != nullptr) { TORCH_CHECK( gptoss_metal_command_buffer_wrap_existing(command_buffer_obj, &cb) == gptoss_status_success, "wrap_command_buffer failed"); borrowed_buffer = true; } } } if (!borrowed_queue) { TORCH_CHECK(gptoss_metal_command_queue_create(&device, &cq) == gptoss_status_success, "cq_create failed"); } if (!borrowed_buffer) { TORCH_CHECK(gptoss_metal_command_buffer_create(&cq, &cb) == gptoss_status_success, "cb_create failed"); } encode_fn(device, fn, cb); if (borrowed_buffer) { TORCH_CHECK(stream != nullptr, "stream must be valid when using borrowed command buffer"); stream->synchronize(at::mps::SyncType::COMMIT); } else { TORCH_CHECK(gptoss_metal_command_buffer_commit(&cb) == gptoss_status_success, "commit failed"); TORCH_CHECK(gptoss_metal_command_buffer_wait_completion(&cb, nullptr) == gptoss_status_success, "wait failed"); } } catch (...) { cleanup(); throw; } cleanup(); } at::Tensor to_cpu_contiguous(const at::Tensor& tensor) { if (tensor.device().is_cpu() && tensor.is_contiguous()) { return tensor; } return tensor.contiguous().to(at::kCPU); } at::Tensor empty_cpu_like(const at::Tensor& tensor) { return at::empty_like(tensor, tensor.options().device(at::kCPU)).contiguous(); } void copy_back(at::Tensor& dst, const at::Tensor& src_cpu) { dst.copy_(src_cpu.to(dst.device(), /*non_blocking=*/false, /*copy=*/true)); } void create_control_buffer(const gptoss_metal_device* device, MetalBuffer& buffer) { struct gptoss_control ctrl {0}; buffer.create(device, sizeof(ctrl), &ctrl); } template void run_dense_matmul_bf16(const char* kernel_symbol, LaunchFn&& launch_fn, const at::Tensor& input, const at::Tensor& weight_bf16, const at::Tensor& bias_bf16, at::Tensor& output) { TORCH_CHECK(input.dtype() == at::kFloat, "input must be float32"); TORCH_CHECK(weight_bf16.dtype() == at::kBFloat16, "weight must be bfloat16"); TORCH_CHECK(bias_bf16.dtype() == at::kBFloat16, "bias must be bfloat16"); TORCH_CHECK(output.dtype() == at::kFloat, "output must be float32"); TORCH_CHECK(input.dim() == 2, "input must be 2D"); TORCH_CHECK(weight_bf16.dim() == 2, "weight must be 2D"); TORCH_CHECK(bias_bf16.dim() == 1, "bias must be 1D"); TORCH_CHECK(output.dim() == 2, "output must be 2D"); const int64_t num_tokens = input.size(0); const int64_t num_cols = input.size(1); const int64_t num_rows = output.size(1); TORCH_CHECK(output.size(0) == num_tokens, "output first dimension must match number of tokens"); TORCH_CHECK(weight_bf16.size(0) == num_cols && weight_bf16.size(1) == num_rows, "weight shape must be [num_cols, num_rows]"); TORCH_CHECK(bias_bf16.size(0) == num_rows, "bias length must equal number of rows"); const bool use_mps = input.device().is_mps() && weight_bf16.device().is_mps() && bias_bf16.device().is_mps() && output.device().is_mps(); at::Tensor input_cpu; at::Tensor weight_cpu; at::Tensor bias_cpu; at::Tensor out_cpu; size_t in_bytes = 0; size_t weight_bytes = 0; size_t bias_bytes = 0; size_t out_bytes = 0; at::Tensor input_mps; at::Tensor weight_mps; at::Tensor bias_mps; at::Tensor output_mps; if (use_mps) { input_mps = input.contiguous(); weight_mps = weight_bf16.transpose(0, 1).contiguous(); bias_mps = bias_bf16.contiguous(); output_mps = output.contiguous(); } else { input_cpu = to_cpu_contiguous(input); weight_cpu = weight_bf16.transpose(0, 1).contiguous().to(at::kCPU); bias_cpu = to_cpu_contiguous(bias_bf16); out_cpu = empty_cpu_like(output); in_bytes = static_cast(input_cpu.numel()) * input_cpu.element_size(); weight_bytes = static_cast(weight_cpu.numel()) * weight_cpu.element_size(); bias_bytes = static_cast(bias_cpu.numel()) * bias_cpu.element_size(); out_bytes = static_cast(out_cpu.numel()) * out_cpu.element_size(); } MetalBuffer input_buf; MetalBuffer weight_buf; MetalBuffer bias_buf; MetalBuffer out_buf; MetalBuffer control_buf; run_metal_kernel(kernel_symbol, [&](const gptoss_metal_device& device, const gptoss_metal_function& fn, gptoss_metal_command_buffer& cb) { if (use_mps) { input_buf.wrap_mps_tensor(&device, input_mps); weight_buf.wrap_mps_tensor(&device, weight_mps); bias_buf.wrap_mps_tensor(&device, bias_mps); out_buf.wrap_mps_tensor(&device, output_mps); } else { input_buf.wrap(&device, in_bytes, input_cpu.data_ptr()); weight_buf.wrap(&device, weight_bytes, weight_cpu.data_ptr()); bias_buf.wrap(&device, bias_bytes, bias_cpu.data_ptr()); out_buf.create(&device, out_bytes, nullptr); } create_control_buffer(&device, control_buf); TORCH_CHECK( launch_fn( &cb, &fn, input_buf.get(), 0, weight_buf.get(), 0, bias_buf.get(), 0, out_buf.get(), 0, control_buf.get(), 0, static_cast(num_tokens), static_cast(num_cols), static_cast(num_rows)) == gptoss_status_success, "encode dense matmul failed"); }); if (use_mps) { if (!output_mps.is_alias_of(output)) { output.copy_(output_mps); } } else { std::memcpy(out_cpu.data_ptr(), out_buf.ptr(), out_bytes); copy_back(output, out_cpu); } } } // namespace void f32_bf16w_matmul_torch(const at::Tensor &input, const at::Tensor &weight_bf16, const at::Tensor &bias_bf16, at::Tensor &output, int64_t num_tokens, int64_t num_cols, int64_t num_rows, int64_t threadgroup_size) { TORCH_CHECK(input.dtype() == at::kFloat, "input must be float32"); TORCH_CHECK(weight_bf16.dtype() == at::kBFloat16, "weight must be bfloat16"); TORCH_CHECK(bias_bf16.dtype() == at::kBFloat16, "bias must be bfloat16"); TORCH_CHECK(output.dtype() == at::kFloat, "output must be float32"); TORCH_CHECK(input.dim() == 2, "input must be 2D"); TORCH_CHECK(weight_bf16.dim() == 2, "weight must be 2D"); TORCH_CHECK(bias_bf16.dim() == 1, "bias must be 1D"); TORCH_CHECK(output.dim() == 2, "output must be 2D"); TORCH_CHECK(input.size(0) == num_tokens && input.size(1) == num_cols, "input shape must be [num_tokens, num_cols]"); TORCH_CHECK(weight_bf16.size(0) == num_cols && weight_bf16.size(1) == num_rows, "weight shape must be [num_cols, num_rows]"); TORCH_CHECK(bias_bf16.size(0) == num_rows, "bias length must be num_rows"); TORCH_CHECK(output.size(0) == num_tokens && output.size(1) == num_rows, "output shape must be [num_tokens, num_rows]"); const bool use_mps = input.device().is_mps() && weight_bf16.device().is_mps() && bias_bf16.device().is_mps() && output.device().is_mps(); at::Tensor input_cpu; at::Tensor weight_cpu; at::Tensor bias_cpu; at::Tensor out_cpu; size_t in_bytes = 0; size_t wt_bytes = 0; size_t bs_bytes = 0; size_t out_bytes = 0; at::Tensor input_mps; at::Tensor weight_mps; at::Tensor bias_mps; at::Tensor output_mps; if (use_mps) { input_mps = input.contiguous(); weight_mps = weight_bf16.transpose(0, 1).contiguous(); bias_mps = bias_bf16.contiguous(); output_mps = output.contiguous(); } else { input_cpu = to_cpu_contiguous(input); weight_cpu = weight_bf16.transpose(0, 1).contiguous().to(at::kCPU); bias_cpu = to_cpu_contiguous(bias_bf16); out_cpu = empty_cpu_like(output); in_bytes = static_cast(num_tokens) * static_cast(num_cols) * sizeof(float); wt_bytes = static_cast(num_rows) * static_cast(num_cols) * sizeof(uint16_t); bs_bytes = static_cast(num_rows) * sizeof(uint16_t); out_bytes = static_cast(num_tokens) * static_cast(num_rows) * sizeof(float); } MetalBuffer in_buf; MetalBuffer wt_buf; MetalBuffer bs_buf; MetalBuffer out_buf; MetalBuffer ctrl_buf; run_metal_kernel("gptoss_f32_bf16w_matmul", [&](const gptoss_metal_device& device, const gptoss_metal_function& fn, gptoss_metal_command_buffer& cb) { if (use_mps) { in_buf.wrap_mps_tensor(&device, input_mps); wt_buf.wrap_mps_tensor(&device, weight_mps); bs_buf.wrap_mps_tensor(&device, bias_mps); out_buf.wrap_mps_tensor(&device, output_mps); } else { in_buf.wrap(&device, in_bytes, input_cpu.data_ptr()); wt_buf.wrap(&device, wt_bytes, weight_cpu.data_ptr()); bs_buf.wrap(&device, bs_bytes, bias_cpu.data_ptr()); out_buf.create(&device, out_bytes, nullptr); } create_control_buffer(&device, ctrl_buf); TORCH_CHECK( gptoss_metal_command_buffer_encode_launch_f32_bf16w_matmul( &cb, &fn, static_cast(threadgroup_size), in_buf.get(), 0, wt_buf.get(), 0, bs_buf.get(), 0, out_buf.get(), 0, ctrl_buf.get(), 0, static_cast(num_tokens), static_cast(num_cols), static_cast(num_rows)) == gptoss_status_success, "encode failed"); }); if (use_mps) { if (!output_mps.is_alias_of(output)) { output.copy_(output_mps); } } else { std::memcpy(out_cpu.data_ptr(), out_buf.ptr(), out_bytes); copy_back(output, out_cpu); } } void bf16_f32_embeddings_torch(const at::Tensor& token_ids, const at::Tensor& weight_bf16, at::Tensor& output, int64_t threadgroup_size) { TORCH_CHECK(token_ids.dtype() == at::kInt || token_ids.dtype() == at::kLong, "token_ids must be int32 or int64"); TORCH_CHECK(weight_bf16.dtype() == at::kBFloat16, "weight must be bfloat16"); TORCH_CHECK(output.dtype() == at::kFloat, "output must be float32"); TORCH_CHECK(token_ids.dim() == 1, "token_ids must be 1D"); TORCH_CHECK(weight_bf16.dim() == 2, "weight must be 2D"); TORCH_CHECK(output.dim() == 2, "output must be 2D"); const int64_t num_tokens = token_ids.size(0); TORCH_CHECK(output.size(0) == num_tokens, "output first dimension must match num_tokens"); const int64_t num_channels = output.size(1); TORCH_CHECK(num_channels % 4 == 0, "num_channels must be divisible by 4"); TORCH_CHECK(weight_bf16.size(1) == num_channels, "weight second dimension must equal embedding dimension (num_channels)"); TORCH_CHECK(threadgroup_size >= 0, "threadgroup_size must be non-negative"); const bool use_mps = token_ids.device().is_mps() && weight_bf16.device().is_mps() && output.device().is_mps(); at::Tensor tokens_cpu; at::Tensor weight_cpu; at::Tensor out_cpu; size_t token_bytes = 0; size_t weight_bytes = 0; size_t out_bytes = 0; at::Tensor tokens_mps; at::Tensor weight_mps; at::Tensor output_mps; if (use_mps) { tokens_mps = (token_ids.dtype() == at::kInt ? token_ids : token_ids.to(at::kInt)).contiguous(); weight_mps = weight_bf16.contiguous(); output_mps = output.contiguous(); } else { tokens_cpu = token_ids.dtype() == at::kInt ? to_cpu_contiguous(token_ids) : token_ids.to(at::kInt).contiguous().to(at::kCPU); weight_cpu = to_cpu_contiguous(weight_bf16); out_cpu = empty_cpu_like(output); token_bytes = static_cast(num_tokens) * sizeof(uint32_t); weight_bytes = static_cast(weight_cpu.numel()) * weight_cpu.element_size(); out_bytes = static_cast(out_cpu.numel()) * out_cpu.element_size(); } MetalBuffer tokens_buf; MetalBuffer weight_buf; MetalBuffer out_buf; MetalBuffer control_buf; run_metal_kernel("gptoss_bf16_f32_embeddings", [&](const gptoss_metal_device& device, const gptoss_metal_function& fn, gptoss_metal_command_buffer& cb) { if (use_mps) { tokens_buf.wrap_mps_tensor(&device, tokens_mps); weight_buf.wrap_mps_tensor(&device, weight_mps); out_buf.wrap_mps_tensor(&device, output_mps); } else { tokens_buf.wrap(&device, token_bytes, tokens_cpu.data_ptr()); weight_buf.wrap(&device, weight_bytes, weight_cpu.data_ptr()); out_buf.create(&device, out_bytes, nullptr); } create_control_buffer(&device, control_buf); TORCH_CHECK( gptoss_metal_command_buffer_encode_launch_bf16_f32_embeddings( &cb, &fn, static_cast(threadgroup_size), tokens_buf.get(), 0, weight_buf.get(), 0, out_buf.get(), 0, control_buf.get(), 0, static_cast(num_tokens), static_cast(num_channels)) == gptoss_status_success, "encode embeddings failed"); }); if (use_mps) { if (!output_mps.is_alias_of(output)) { output.copy_(output_mps); } } else { std::memcpy(out_cpu.data_ptr(), out_buf.ptr(), out_bytes); copy_back(output, out_cpu); } } void f32_bf16w_rmsnorm_torch(const at::Tensor& input, const at::Tensor& weight_bf16, at::Tensor& output, double epsilon) { TORCH_CHECK(input.dtype() == at::kFloat, "input must be float32"); TORCH_CHECK(weight_bf16.dtype() == at::kBFloat16, "weight must be bfloat16"); TORCH_CHECK(output.dtype() == at::kFloat, "output must be float32"); TORCH_CHECK(input.dim() == 2, "input must be 2D"); TORCH_CHECK(weight_bf16.dim() == 1, "weight must be 1D"); TORCH_CHECK(output.dim() == 2, "output must be 2D"); const int64_t num_tokens = input.size(0); const int64_t num_channels = input.size(1); TORCH_CHECK(output.size(0) == num_tokens && output.size(1) == num_channels, "output shape must match input shape"); TORCH_CHECK(weight_bf16.size(0) == num_channels, "weight length must equal number of channels"); TORCH_CHECK(num_channels % 4 == 0, "num_channels must be divisible by 4"); const bool use_mps = input.device().is_mps(); at::Tensor input_cpu; at::Tensor weight_cpu; at::Tensor out_cpu; size_t in_bytes = 0; size_t weight_bytes = 0; size_t out_bytes = 0; at::Tensor input_mps; at::Tensor weight_mps; at::Tensor output_mps; if (use_mps) { input_mps = input.contiguous(); weight_mps = weight_bf16.contiguous(); output_mps = output.contiguous(); } else { input_cpu = to_cpu_contiguous(input); weight_cpu = to_cpu_contiguous(weight_bf16); out_cpu = empty_cpu_like(output); in_bytes = static_cast(input_cpu.numel()) * input_cpu.element_size(); weight_bytes = static_cast(weight_cpu.numel()) * weight_cpu.element_size(); out_bytes = static_cast(out_cpu.numel()) * out_cpu.element_size(); } MetalBuffer input_buf; MetalBuffer weight_buf; MetalBuffer out_buf; MetalBuffer control_buf; run_metal_kernel("gptoss_f32_bf16w_rmsnorm", [&](const gptoss_metal_device& device, const gptoss_metal_function& fn, gptoss_metal_command_buffer& cb) { if (use_mps) { input_buf.wrap_mps_tensor(&device, input_mps); weight_buf.wrap_mps_tensor(&device, weight_mps); out_buf.wrap_mps_tensor(&device, output_mps); } else { input_buf.wrap(&device, in_bytes, input_cpu.data_ptr()); weight_buf.wrap(&device, weight_bytes, weight_cpu.data_ptr()); out_buf.create(&device, out_bytes, nullptr); } create_control_buffer(&device, control_buf); TORCH_CHECK( gptoss_metal_command_buffer_encode_launch_f32_bf16w_rmsnorm( &cb, &fn, input_buf.get(), 0, weight_buf.get(), 0, out_buf.get(), 0, control_buf.get(), 0, static_cast(num_tokens), static_cast(num_channels), static_cast(epsilon)) == gptoss_status_success, "encode rmsnorm failed"); }); if (use_mps) { if (!output_mps.is_alias_of(output)) { output.copy_(output_mps); } } else { std::memcpy(out_cpu.data_ptr(), out_buf.ptr(), out_bytes); copy_back(output, out_cpu); } } void f32_bf16w_dense_matmul_qkv_torch(const at::Tensor& input, const at::Tensor& weight_bf16, const at::Tensor& bias_bf16, at::Tensor& output) { run_dense_matmul_bf16( "gptoss_f32_bf16w_dense_matmul_qkv", gptoss_metal_command_buffer_encode_launch_f32_bf16w_dense_matmul_qkv, input, weight_bf16, bias_bf16, output); } void f32_bf16w_dense_matmul_attn_output_torch(const at::Tensor& input, const at::Tensor& weight_bf16, const at::Tensor& bias_bf16, at::Tensor& output) { run_dense_matmul_bf16( "gptoss_f32_bf16w_dense_matmul_attn_output", gptoss_metal_command_buffer_encode_launch_f32_bf16w_dense_matmul_attn_output, input, weight_bf16, bias_bf16, output); } void f32_bf16w_dense_matmul_mlp_gate_torch(const at::Tensor& input, const at::Tensor& weight_bf16, const at::Tensor& bias_bf16, at::Tensor& output) { run_dense_matmul_bf16( "gptoss_f32_bf16w_dense_matmul_mlp_gate", gptoss_metal_command_buffer_encode_launch_f32_bf16w_dense_matmul_mlp_gate, input, weight_bf16, bias_bf16, output); } void f32_rope_torch(at::Tensor& activations, double rope_base, double interpolation_scale, double yarn_offset, double yarn_scale, double yarn_multiplier, int64_t num_tokens, int64_t num_q_heads, int64_t num_kv_heads, int64_t attn_head_dim, int64_t token_offset, int64_t threadgroup_size) { TORCH_CHECK(activations.dtype() == at::kFloat, "activations must be float32"); TORCH_CHECK(num_tokens >= 0 && num_q_heads >= 0 && num_kv_heads >= 0 && attn_head_dim >= 0, "shape parameters must be non-negative"); TORCH_CHECK(threadgroup_size >= 0, "threadgroup_size must be non-negative"); const bool use_mps = activations.device().is_mps(); at::Tensor activations_cpu; at::Tensor activations_mps; size_t activations_bytes = 0; if (use_mps) { activations_mps = activations.contiguous(); activations_bytes = static_cast(activations_mps.numel()) * activations_mps.element_size(); } else { activations_cpu = to_cpu_contiguous(activations); activations_bytes = static_cast(activations_cpu.numel()) * activations_cpu.element_size(); } MetalBuffer activations_buf; MetalBuffer control_buf; run_metal_kernel("gptoss_f32_rope", [&](const gptoss_metal_device& device, const gptoss_metal_function& fn, gptoss_metal_command_buffer& cb) { if (use_mps) { activations_buf.wrap_mps_tensor(&device, activations_mps); } else { activations_buf.wrap(&device, activations_bytes, activations_cpu.data_ptr()); } create_control_buffer(&device, control_buf); TORCH_CHECK( gptoss_metal_command_buffer_encode_launch_f32_rope( &cb, &fn, static_cast(threadgroup_size), activations_buf.get(), 0, control_buf.get(), 0, static_cast(rope_base), static_cast(interpolation_scale), static_cast(yarn_offset), static_cast(yarn_scale), static_cast(yarn_multiplier), static_cast(num_tokens), static_cast(num_q_heads), static_cast(num_kv_heads), static_cast(attn_head_dim), static_cast(token_offset)) == gptoss_status_success, "encode rope failed"); }); if (use_mps) { if (!activations_mps.is_alias_of(activations)) { activations.copy_(activations_mps); } } else { copy_back(activations, activations_cpu); } } void f32_bf16w_matmul_qkv_torch(const at::Tensor& input, const at::Tensor& weight_bf16, const at::Tensor& bias_bf16, at::Tensor& output, at::Tensor& kv_cache, int64_t kv_cache_offset_bytes, int64_t num_tokens, int64_t num_cols, int64_t num_q_heads, int64_t num_kv_heads, int64_t attn_head_dim, int64_t token_offset, int64_t max_tokens, double rope_base, double interpolation_scale, double yarn_offset, double yarn_scale, double yarn_multiplier, int64_t threadgroup_size) { TORCH_CHECK(input.dtype() == at::kFloat, "input must be float32"); TORCH_CHECK(weight_bf16.dtype() == at::kBFloat16, "weight must be bfloat16"); TORCH_CHECK(bias_bf16.dtype() == at::kBFloat16, "bias must be bfloat16"); TORCH_CHECK(output.dtype() == at::kFloat, "output must be float32"); TORCH_CHECK(kv_cache.dtype() == at::kFloat, "kv_cache must be float32"); TORCH_CHECK(input.dim() == 2, "input must be 2D"); TORCH_CHECK(weight_bf16.dim() == 2, "weight must be 2D"); TORCH_CHECK(bias_bf16.dim() == 1, "bias must be 1D"); TORCH_CHECK(output.dim() == 2, "output must be 2D"); TORCH_CHECK(num_tokens >= 0 && num_cols >= 0 && num_q_heads >= 0 && num_kv_heads >= 0 && attn_head_dim >= 0 && max_tokens >= 0, "shape parameters must be non-negative"); TORCH_CHECK(threadgroup_size >= 0, "threadgroup_size must be non-negative"); TORCH_CHECK(kv_cache_offset_bytes >= 0, "kv_cache_offset_bytes must be non-negative"); TORCH_CHECK(input.size(0) == num_tokens && input.size(1) == num_cols, "input shape must be [num_tokens, num_cols]"); const int64_t num_rows = (num_q_heads + 2 * num_kv_heads) * attn_head_dim; TORCH_CHECK(output.size(0) == num_tokens && output.size(1) == num_rows, "output shape must be [num_tokens, (num_q_heads + 2 * num_kv_heads) * attn_head_dim]"); TORCH_CHECK(weight_bf16.size(0) == num_cols && weight_bf16.size(1) == num_rows, "weight shape must be [num_cols, (num_q_heads + 2 * num_kv_heads) * attn_head_dim]"); TORCH_CHECK(bias_bf16.size(0) == num_rows, "bias length must equal output feature dimension"); const bool use_mps = input.device().is_mps() && weight_bf16.device().is_mps() && bias_bf16.device().is_mps() && output.device().is_mps() && kv_cache.device().is_mps(); at::Tensor input_cpu; at::Tensor weight_cpu; at::Tensor bias_cpu; at::Tensor out_cpu; at::Tensor kv_cpu; size_t in_bytes = 0; size_t weight_bytes = 0; size_t bias_bytes = 0; size_t out_bytes = 0; size_t kv_bytes = 0; at::Tensor input_mps; at::Tensor weight_mps; at::Tensor bias_mps; at::Tensor out_mps; at::Tensor kv_mps; if (use_mps) { input_mps = input.contiguous(); weight_mps = weight_bf16.transpose(0, 1).contiguous(); bias_mps = bias_bf16.contiguous(); out_mps = output.contiguous(); kv_mps = kv_cache.contiguous(); } else { input_cpu = to_cpu_contiguous(input); weight_cpu = weight_bf16.transpose(0, 1).contiguous().to(at::kCPU); bias_cpu = to_cpu_contiguous(bias_bf16); out_cpu = empty_cpu_like(output); kv_cpu = to_cpu_contiguous(kv_cache); in_bytes = static_cast(input_cpu.numel()) * input_cpu.element_size(); weight_bytes = static_cast(weight_cpu.numel()) * weight_cpu.element_size(); bias_bytes = static_cast(bias_cpu.numel()) * bias_cpu.element_size(); out_bytes = static_cast(out_cpu.numel()) * out_cpu.element_size(); kv_bytes = static_cast(kv_cpu.numel()) * kv_cpu.element_size(); } MetalBuffer input_buf; MetalBuffer weight_buf; MetalBuffer bias_buf; MetalBuffer out_buf; MetalBuffer kv_buf; MetalBuffer control_buf; run_metal_kernel("gptoss_f32_bf16w_matmul_qkv", [&](const gptoss_metal_device& device, const gptoss_metal_function& fn, gptoss_metal_command_buffer& cb) { if (use_mps) { input_buf.wrap_mps_tensor(&device, input_mps); weight_buf.wrap_mps_tensor(&device, weight_mps); bias_buf.wrap_mps_tensor(&device, bias_mps); out_buf.wrap_mps_tensor(&device, out_mps); kv_buf.wrap_mps_tensor(&device, kv_mps); } else { input_buf.wrap(&device, in_bytes, input_cpu.data_ptr()); weight_buf.wrap(&device, weight_bytes, weight_cpu.data_ptr()); bias_buf.wrap(&device, bias_bytes, bias_cpu.data_ptr()); out_buf.create(&device, out_bytes, nullptr); kv_buf.wrap(&device, kv_bytes, kv_cpu.data_ptr()); } create_control_buffer(&device, control_buf); TORCH_CHECK( gptoss_metal_command_buffer_encode_launch_f32_bf16w_matmul_qkv( &cb, &fn, static_cast(threadgroup_size), input_buf.get(), 0, weight_buf.get(), 0, bias_buf.get(), 0, out_buf.get(), 0, kv_buf.get(), static_cast(kv_cache_offset_bytes), control_buf.get(), 0, static_cast(num_tokens), static_cast(num_cols), static_cast(num_q_heads), static_cast(num_kv_heads), static_cast(attn_head_dim), static_cast(token_offset), static_cast(max_tokens), static_cast(rope_base), static_cast(interpolation_scale), static_cast(yarn_offset), static_cast(yarn_scale), static_cast(yarn_multiplier)) == gptoss_status_success, "encode matmul_qkv failed"); }); if (use_mps) { if (!out_mps.is_alias_of(output)) { output.copy_(out_mps); } if (!kv_mps.is_alias_of(kv_cache)) { kv_cache.copy_(kv_mps); } } else { std::memcpy(out_cpu.data_ptr(), out_buf.ptr(), out_bytes); copy_back(output, out_cpu); copy_back(kv_cache, kv_cpu); } } void f32_sdpa_torch(const at::Tensor& q, int64_t q_offset_bytes, const at::Tensor& kv, int64_t kv_offset_bytes, const at::Tensor& s_bf16, int64_t s_offset_bytes, at::Tensor& output, int64_t output_offset_bytes, int64_t window, int64_t kv_stride, int64_t num_q_tokens, int64_t num_kv_tokens, int64_t num_q_heads, int64_t num_kv_heads, int64_t head_dim) { TORCH_CHECK(q.dtype() == at::kFloat, "q must be float32"); TORCH_CHECK(kv.dtype() == at::kFloat, "kv must be float32"); TORCH_CHECK(s_bf16.dtype() == at::kBFloat16, "s must be bfloat16"); TORCH_CHECK(output.dtype() == at::kFloat, "output must be float32"); TORCH_CHECK(q_offset_bytes >= 0 && kv_offset_bytes >= 0 && s_offset_bytes >= 0 && output_offset_bytes >= 0, "offsets must be non-negative"); TORCH_CHECK(window >= 0 && kv_stride >= 0 && num_q_tokens >= 0 && num_kv_tokens >= 0 && num_q_heads >= 0 && num_kv_heads >= 0 && head_dim >= 0, "shape parameters must be non-negative"); const bool use_mps = q.device().is_mps() && kv.device().is_mps() && s_bf16.device().is_mps() && output.device().is_mps(); at::Tensor q_cpu; at::Tensor kv_cpu; at::Tensor s_cpu; at::Tensor out_cpu; size_t q_bytes = 0; size_t kv_bytes = 0; size_t s_bytes = 0; size_t out_bytes = 0; at::Tensor q_mps; at::Tensor kv_mps; at::Tensor s_mps; at::Tensor out_mps; if (use_mps) { q_mps = q.contiguous(); kv_mps = kv.contiguous(); s_mps = s_bf16.contiguous(); out_mps = output.contiguous(); } else { q_cpu = to_cpu_contiguous(q); kv_cpu = to_cpu_contiguous(kv); s_cpu = to_cpu_contiguous(s_bf16); out_cpu = empty_cpu_like(output); q_bytes = static_cast(q_cpu.numel()) * q_cpu.element_size(); kv_bytes = static_cast(kv_cpu.numel()) * kv_cpu.element_size(); s_bytes = static_cast(s_cpu.numel()) * s_cpu.element_size(); out_bytes = static_cast(out_cpu.numel()) * out_cpu.element_size(); } MetalBuffer q_buf; MetalBuffer kv_buf; MetalBuffer s_buf; MetalBuffer out_buf; MetalBuffer control_buf; run_metal_kernel("gptoss_f32_sdpa_q8_d64", [&](const gptoss_metal_device& device, const gptoss_metal_function& fn, gptoss_metal_command_buffer& cb) { if (use_mps) { q_buf.wrap_mps_tensor(&device, q_mps); kv_buf.wrap_mps_tensor(&device, kv_mps); s_buf.wrap_mps_tensor(&device, s_mps); out_buf.wrap_mps_tensor(&device, out_mps); } else { q_buf.wrap(&device, q_bytes, q_cpu.data_ptr()); kv_buf.wrap(&device, kv_bytes, kv_cpu.data_ptr()); s_buf.wrap(&device, s_bytes, s_cpu.data_ptr()); out_buf.create(&device, out_bytes, nullptr); } create_control_buffer(&device, control_buf); TORCH_CHECK( gptoss_metal_command_buffer_encode_launch_f32_sdpa( &cb, &fn, q_buf.get(), static_cast(q_offset_bytes), kv_buf.get(), static_cast(kv_offset_bytes), s_buf.get(), static_cast(s_offset_bytes), out_buf.get(), static_cast(output_offset_bytes), control_buf.get(), 0, static_cast(window), static_cast(kv_stride), static_cast(num_q_tokens), static_cast(num_kv_tokens), static_cast(num_q_heads), static_cast(num_kv_heads), static_cast(head_dim)) == gptoss_status_success, "encode sdpa failed"); }); if (use_mps) { if (!out_mps.is_alias_of(output)) { output.copy_(out_mps); } } else { std::memcpy(out_cpu.data_ptr(), out_buf.ptr(), out_bytes); copy_back(output, out_cpu); } } void f32_topk_torch(const at::Tensor& scores, at::Tensor& expert_ids, at::Tensor& expert_scores, int64_t num_tokens, int64_t num_experts, int64_t num_active_experts) { TORCH_CHECK(scores.dtype() == at::kFloat, "scores must be float32"); TORCH_CHECK(expert_ids.dtype() == at::kInt, "expert_ids must be int32"); TORCH_CHECK(expert_scores.dtype() == at::kFloat, "expert_scores must be float32"); TORCH_CHECK(num_tokens >= 0 && num_experts >= 0 && num_active_experts >= 0, "shape parameters must be non-negative"); TORCH_CHECK(scores.size(0) == num_tokens, "scores first dimension must match num_tokens"); TORCH_CHECK(scores.numel() == num_tokens * num_experts, "scores must have num_tokens * num_experts elements"); TORCH_CHECK(expert_ids.numel() == num_tokens * num_active_experts, "expert_ids must have num_tokens * num_active_experts elements"); TORCH_CHECK(expert_scores.numel() == num_tokens * num_active_experts, "expert_scores must have num_tokens * num_active_experts elements"); const bool scores_use_mps = scores.device().is_mps(); at::Tensor scores_cpu; at::Tensor scores_mps; size_t score_bytes = 0; if (scores_use_mps) { scores_mps = scores.contiguous(); } else { scores_cpu = to_cpu_contiguous(scores); score_bytes = static_cast(scores_cpu.numel()) * scores_cpu.element_size(); } std::vector predictions(static_cast(num_tokens) * static_cast(num_active_experts)); const size_t pred_bytes = predictions.size() * sizeof(gptoss_expert_prediction); MetalBuffer score_buf; MetalBuffer pred_buf; MetalBuffer control_buf; run_metal_kernel("gptoss_f32_topk_softmax_e128_k4", [&](const gptoss_metal_device& device, const gptoss_metal_function& fn, gptoss_metal_command_buffer& cb) { if (scores_use_mps) { score_buf.wrap_mps_tensor(&device, scores_mps); } else { score_buf.wrap(&device, score_bytes, scores_cpu.data_ptr()); } pred_buf.wrap(&device, pred_bytes, predictions.data()); create_control_buffer(&device, control_buf); TORCH_CHECK( gptoss_metal_command_buffer_encode_launch_f32_topk( &cb, &fn, score_buf.get(), 0, pred_buf.get(), 0, control_buf.get(), 0, static_cast(num_tokens), static_cast(num_experts), static_cast(num_active_experts)) == gptoss_status_success, "encode topk failed"); }); auto ids_cpu = expert_ids.to(at::kCPU).contiguous(); auto scores_out_cpu = expert_scores.to(at::kCPU).contiguous(); auto* ids_ptr = ids_cpu.data_ptr(); auto* scores_ptr = scores_out_cpu.data_ptr(); const size_t total = predictions.size(); for (size_t i = 0; i < total; ++i) { ids_ptr[i] = static_cast(predictions[i].expert_id); scores_ptr[i] = predictions[i].score; } copy_back(expert_ids, ids_cpu); copy_back(expert_scores, scores_out_cpu); } void expert_routing_metadata_torch(const at::Tensor& expert_ids, const at::Tensor& expert_scores, at::Tensor& expert_offsets, at::Tensor& intra_expert_offsets, int64_t num_tokens, int64_t num_experts) { TORCH_CHECK(expert_ids.dtype() == at::kInt, "expert_ids must be int32"); TORCH_CHECK(expert_scores.dtype() == at::kFloat, "expert_scores must be float32"); TORCH_CHECK(expert_offsets.dtype() == at::kInt, "expert_offsets must be int32"); TORCH_CHECK(intra_expert_offsets.dtype() == at::kInt, "intra_expert_offsets must be int32"); TORCH_CHECK(num_tokens >= 0 && num_experts >= 0, "shape parameters must be non-negative"); TORCH_CHECK(expert_ids.numel() == num_tokens, "expert_ids must have num_tokens elements"); TORCH_CHECK(expert_scores.numel() == num_tokens, "expert_scores must have num_tokens elements"); TORCH_CHECK(intra_expert_offsets.numel() == num_tokens, "intra_expert_offsets must have num_tokens elements"); TORCH_CHECK(expert_offsets.numel() == num_experts + 1, "expert_offsets must have num_experts + 1 elements"); auto ids_cpu = to_cpu_contiguous(expert_ids); auto scores_cpu = to_cpu_contiguous(expert_scores); const bool use_mps = expert_offsets.device().is_mps() && intra_expert_offsets.device().is_mps(); at::Tensor offsets_cpu; at::Tensor intra_offsets_cpu; at::Tensor offsets_mps; at::Tensor intra_offsets_mps; size_t offsets_bytes = 0; size_t intra_bytes = 0; if (use_mps) { offsets_mps = expert_offsets.contiguous(); intra_offsets_mps = intra_expert_offsets.contiguous(); } else { offsets_cpu = to_cpu_contiguous(expert_offsets); intra_offsets_cpu = to_cpu_contiguous(intra_expert_offsets); offsets_bytes = static_cast(offsets_cpu.numel()) * offsets_cpu.element_size(); intra_bytes = static_cast(intra_offsets_cpu.numel()) * intra_offsets_cpu.element_size(); } std::vector predictions(static_cast(num_tokens)); const auto* ids_ptr = ids_cpu.data_ptr(); const auto* scores_ptr = scores_cpu.data_ptr(); for (int64_t i = 0; i < num_tokens; ++i) { predictions[static_cast(i)] = gptoss_expert_prediction { .expert_id = static_cast(ids_ptr[i]), .score = scores_ptr[i], }; } const size_t pred_bytes = predictions.size() * sizeof(gptoss_expert_prediction); MetalBuffer pred_buf; MetalBuffer offsets_buf; MetalBuffer intra_offsets_buf; run_metal_kernel("gptoss_f32_expert_routing_metadata", [&](const gptoss_metal_device& device, const gptoss_metal_function& fn, gptoss_metal_command_buffer& cb) { pred_buf.wrap(&device, pred_bytes, predictions.data()); if (use_mps) { offsets_buf.wrap_mps_tensor(&device, offsets_mps); intra_offsets_buf.wrap_mps_tensor(&device, intra_offsets_mps); } else { offsets_buf.wrap(&device, offsets_bytes, offsets_cpu.data_ptr()); intra_offsets_buf.wrap(&device, intra_bytes, intra_offsets_cpu.data_ptr()); } TORCH_CHECK( gptoss_metal_command_buffer_encode_launch_expert_routing_metadata( &cb, &fn, pred_buf.get(), 0, offsets_buf.get(), 0, intra_offsets_buf.get(), 0, static_cast(num_tokens), static_cast(num_experts)) == gptoss_status_success, "encode expert_routing_metadata failed"); }); if (use_mps) { if (!offsets_mps.is_alias_of(expert_offsets)) { expert_offsets.copy_(offsets_mps); } if (!intra_offsets_mps.is_alias_of(intra_expert_offsets)) { intra_expert_offsets.copy_(intra_offsets_mps); } } else { copy_back(expert_offsets, offsets_cpu); copy_back(intra_expert_offsets, intra_offsets_cpu); } } void f32_scatter_torch(const at::Tensor& input, const at::Tensor& expert_ids, const at::Tensor& expert_scores, const at::Tensor& expert_offsets, const at::Tensor& intra_expert_offsets, at::Tensor& output, int64_t num_channels, int64_t num_tokens, int64_t num_active_experts) { TORCH_CHECK(input.dtype() == at::kFloat, "input must be float32"); TORCH_CHECK(expert_ids.dtype() == at::kInt, "expert_ids must be int32"); TORCH_CHECK(expert_scores.dtype() == at::kFloat, "expert_scores must be float32"); TORCH_CHECK(expert_offsets.dtype() == at::kInt, "expert_offsets must be int32"); TORCH_CHECK(intra_expert_offsets.dtype() == at::kInt, "intra_expert_offsets must be int32"); TORCH_CHECK(output.dtype() == at::kFloat, "output must be float32"); TORCH_CHECK(num_channels >= 0 && num_tokens >= 0 && num_active_experts >= 0, "shape parameters must be non-negative"); TORCH_CHECK(input.numel() == static_cast(num_tokens / num_active_experts) * num_channels, "input size mismatch"); TORCH_CHECK(expert_ids.numel() == num_tokens, "expert_ids must have num_tokens elements"); TORCH_CHECK(expert_scores.numel() == num_tokens, "expert_scores must have num_tokens elements"); TORCH_CHECK(intra_expert_offsets.numel() == num_tokens, "intra_expert_offsets must have num_tokens elements"); TORCH_CHECK(output.numel() == num_tokens * num_channels / num_active_experts, "output size mismatch"); const bool use_mps = input.device().is_mps() && expert_offsets.device().is_mps() && intra_expert_offsets.device().is_mps() && output.device().is_mps(); at::Tensor input_cpu; at::Tensor expert_offsets_cpu; at::Tensor intra_offsets_cpu; at::Tensor output_cpu; size_t input_bytes = 0; size_t offsets_bytes = 0; size_t intra_bytes = 0; size_t output_bytes = 0; at::Tensor input_mps; at::Tensor expert_offsets_mps; at::Tensor intra_offsets_mps; at::Tensor output_mps; if (use_mps) { input_mps = input.contiguous(); expert_offsets_mps = expert_offsets.contiguous(); intra_offsets_mps = intra_expert_offsets.contiguous(); output_mps = output.contiguous(); } else { input_cpu = to_cpu_contiguous(input); expert_offsets_cpu = to_cpu_contiguous(expert_offsets); intra_offsets_cpu = to_cpu_contiguous(intra_expert_offsets); output_cpu = empty_cpu_like(output); input_bytes = static_cast(input_cpu.numel()) * input_cpu.element_size(); offsets_bytes = static_cast(expert_offsets_cpu.numel()) * expert_offsets_cpu.element_size(); intra_bytes = static_cast(intra_offsets_cpu.numel()) * intra_offsets_cpu.element_size(); output_bytes = static_cast(output_cpu.numel()) * output_cpu.element_size(); } std::vector predictions(static_cast(num_tokens)); const auto* ids_ptr = expert_ids.to(at::kCPU).contiguous().data_ptr(); const auto* scores_ptr = expert_scores.to(at::kCPU).contiguous().data_ptr(); for (int64_t i = 0; i < num_tokens; ++i) { predictions[static_cast(i)] = gptoss_expert_prediction { .expert_id = static_cast(ids_ptr[i]), .score = scores_ptr[i], }; } const size_t pred_bytes = predictions.size() * sizeof(gptoss_expert_prediction); MetalBuffer input_buf; MetalBuffer pred_buf; MetalBuffer offsets_buf; MetalBuffer intra_offsets_buf; MetalBuffer output_buf; run_metal_kernel("gptoss_f32_scatter_e4", [&](const gptoss_metal_device& device, const gptoss_metal_function& fn, gptoss_metal_command_buffer& cb) { if (use_mps) { input_buf.wrap_mps_tensor(&device, input_mps); offsets_buf.wrap_mps_tensor(&device, expert_offsets_mps); intra_offsets_buf.wrap_mps_tensor(&device, intra_offsets_mps); output_buf.wrap_mps_tensor(&device, output_mps); } else { input_buf.wrap(&device, input_bytes, input_cpu.data_ptr()); offsets_buf.wrap(&device, offsets_bytes, expert_offsets_cpu.data_ptr()); intra_offsets_buf.wrap(&device, intra_bytes, intra_offsets_cpu.data_ptr()); output_buf.create(&device, output_bytes, nullptr); } pred_buf.wrap(&device, pred_bytes, predictions.data()); TORCH_CHECK( gptoss_metal_command_buffer_encode_launch_f32_scatter( &cb, &fn, input_buf.get(), 0, pred_buf.get(), 0, offsets_buf.get(), 0, intra_offsets_buf.get(), 0, output_buf.get(), 0, static_cast(num_channels), static_cast(num_tokens / num_active_experts), static_cast(num_active_experts)) == gptoss_status_success, "encode scatter failed"); }); if (use_mps) { if (!output_mps.is_alias_of(output)) { output.copy_(output_mps); } } else { std::memcpy(output_cpu.data_ptr(), output_buf.ptr(), output_bytes); copy_back(output, output_cpu); } } void f32_bf16w_matmul_add_torch(const at::Tensor& input, const at::Tensor& weight_bf16, const at::Tensor& bias_bf16, at::Tensor& output, int64_t num_tokens, int64_t num_cols, int64_t num_rows, int64_t threadgroup_size) { TORCH_CHECK(input.dtype() == at::kFloat, "input must be float32"); TORCH_CHECK(weight_bf16.dtype() == at::kBFloat16, "weight must be bfloat16"); TORCH_CHECK(bias_bf16.dtype() == at::kBFloat16, "bias must be bfloat16"); TORCH_CHECK(output.dtype() == at::kFloat, "output must be float32"); TORCH_CHECK(input.dim() == 2, "input must be 2D"); TORCH_CHECK(weight_bf16.dim() == 2, "weight must be 2D"); TORCH_CHECK(bias_bf16.dim() == 1, "bias must be 1D"); TORCH_CHECK(output.dim() == 2, "output must be 2D"); TORCH_CHECK(input.size(0) == num_tokens && input.size(1) == num_cols, "input shape must be [num_tokens, num_cols]"); TORCH_CHECK(weight_bf16.size(0) == num_cols && weight_bf16.size(1) == num_rows, "weight shape must be [num_cols, num_rows]"); TORCH_CHECK(bias_bf16.size(0) == num_rows, "bias length must equal num_rows"); TORCH_CHECK(output.size(0) == num_tokens && output.size(1) == num_rows, "output shape must be [num_tokens, num_rows]"); const bool use_mps = input.device().is_mps() && weight_bf16.device().is_mps() && bias_bf16.device().is_mps() && output.device().is_mps(); at::Tensor input_cpu; at::Tensor weight_cpu; at::Tensor bias_cpu; at::Tensor out_cpu; size_t in_bytes = 0; size_t weight_bytes = 0; size_t bias_bytes = 0; size_t out_bytes = 0; at::Tensor input_mps; at::Tensor weight_mps; at::Tensor bias_mps; at::Tensor out_mps; if (use_mps) { input_mps = input.contiguous(); weight_mps = weight_bf16.transpose(0, 1).contiguous(); bias_mps = bias_bf16.contiguous(); out_mps = output.contiguous(); } else { input_cpu = to_cpu_contiguous(input); weight_cpu = weight_bf16.transpose(0, 1).contiguous().to(at::kCPU); bias_cpu = to_cpu_contiguous(bias_bf16); out_cpu = to_cpu_contiguous(output); in_bytes = static_cast(input_cpu.numel()) * input_cpu.element_size(); weight_bytes = static_cast(weight_cpu.numel()) * weight_cpu.element_size(); bias_bytes = static_cast(bias_cpu.numel()) * bias_cpu.element_size(); out_bytes = static_cast(out_cpu.numel()) * out_cpu.element_size(); } MetalBuffer input_buf; MetalBuffer weight_buf; MetalBuffer bias_buf; MetalBuffer out_buf; MetalBuffer control_buf; run_metal_kernel("gptoss_f32_bf16w_matmul", [&](const gptoss_metal_device& device, const gptoss_metal_function& fn, gptoss_metal_command_buffer& cb) { if (use_mps) { input_buf.wrap_mps_tensor(&device, input_mps); weight_buf.wrap_mps_tensor(&device, weight_mps); bias_buf.wrap_mps_tensor(&device, bias_mps); out_buf.wrap_mps_tensor(&device, out_mps); } else { input_buf.wrap(&device, in_bytes, input_cpu.data_ptr()); weight_buf.wrap(&device, weight_bytes, weight_cpu.data_ptr()); bias_buf.wrap(&device, bias_bytes, bias_cpu.data_ptr()); out_buf.create(&device, out_bytes, nullptr); std::memcpy(out_buf.ptr(), out_cpu.data_ptr(), out_bytes); } create_control_buffer(&device, control_buf); TORCH_CHECK( gptoss_metal_command_buffer_encode_launch_f32_bf16w_matmul_add( &cb, &fn, static_cast(threadgroup_size), input_buf.get(), 0, weight_buf.get(), 0, bias_buf.get(), 0, out_buf.get(), 0, control_buf.get(), 0, static_cast(num_tokens), static_cast(num_cols), static_cast(num_rows)) == gptoss_status_success, "encode matmul_add failed"); }); if (use_mps) { if (!out_mps.is_alias_of(output)) { output.copy_(out_mps); } } else { std::memcpy(out_cpu.data_ptr(), out_buf.ptr(), out_bytes); copy_back(output, out_cpu); } }