Sample CodeReviewed 2026-07-21View on Apple Developer

Customizing a PyTorch operation

At a glance

Item Summary
Purpose Implement a custom operation in PyTorch that uses Metal kernels to improve performance.
App architecture Objective-C++, Python source; the Python model invokes compiled_lib.mps_softshrink, and the Objective-C++ binding compiles embedded MSL before encoding it on PyTorch’s MPS command stream.
Main patterns Custom operator bridge, Reference-versus-custom strategy, Language boundary
Scope High-level review of 5 scanned source files and 3 detected declarations; build assets are omitted.

Project structure

Source bundle/
├── softshrink.py
├── run_sample.py
├── CustomSoftshrink.h
├── CustomSoftshrink.mm
└── compiler.py

Structure observations

  • The entry/composition boundary and renderer or operation boundary are separate in the source; Objective-C++ custom op is the principal feature coordinator.
  • GPU kernel source is embedded in a native header and compiled into a Metal library at runtime.
  • The tree above is intentionally pruned to composition, resource, and shader files.

Overall architecture

Reference code

softshrink.py:27 — feature handoff or setup anchor

    def forward(self, input):
        return compiled_lib.mps_softshrink(input, self.lambd)

Interpretation

The Python module calls a pybind11 extension. Objective-C++ validates the tensor, compiles the embedded Metal kernel, borrows PyTorch’s MPS command buffer/queue, and encodes the compute dispatch.

Ownership and state

Ownership evidence

run_sample.py:24run_sample creates for a local scope custom_model

    custom_model = CustomMPSSoftshrinkModel().to(mps_device)

    # Measures time.
    for _ in range(100):
        start = time.time()
        default_model.forward(x)
        torch.mps.synchronize()
        default_softshrink += time.time() - start
Owner Object or state Relationship Mutation authority
run_sample custom_model / CustomMPSSoftshrinkModel Creates for a local scope; the diagram uses composition because construction is source-visible. The declaring scope performs setup and replacement.
Objective-C++ custom op Feature-specific framework and Metal resources Operation-local calls or stored state; exclusive lifetime is not assumed beyond cited evidence. Feature setup/encoding code controls mutation and command submission.

Class and protocol design

Type Responsibility Depends on or conforms to Source
CustomMPSSoftshrinkModel represents model/data state used by the operation. nn.Module softshrink.py:33
SoftshrinkModel represents model/data state used by the operation. nn.Module softshrink.py:58
MPSSoftshrink wraps the compiled MPS soft-shrink operator as a reusable PyTorch module. nn.Module softshrink.py:18

No local protocol abstraction is present; the sample uses concrete framework, operation, or model types at this boundary.

Access control

Symbol Access Verified effect Design reason
CustomSoftshrink implementation details implementation-only native helpers, stored state, or registration code stays out of the imported header contract. Hide native implementation details from importing translation units. (CustomSoftshrink.mm:1)
softshrink Python module boundary leading underscores are conventions; the compiled extension/registration layer defines the native export boundary. Keep the usable surface no wider than the collaboration requires. (softshrink.py:1)
CUSTOM_KERNEL runtime-compiled Metal source Objective-C++ compiles the embedded MSL string into an MTLLibrary at runtime. Keep the embedded kernel definition beside the native dispatch bridge that compiles it. (CustomSoftshrink.h:11)

This sample does not use Swift private, fileprivate, or public for its native boundary. Objective-C/Objective-C++ use header versus implementation placement, C++ uses access specifiers/linkage, Python uses module conventions, and Metal entry points cross a compiled-library boundary; these are not Swift access levels.

Logic ownership and placement

Logic Owning type or file Why it lives there
Represents model/data state used by the operation CustomMPSSoftshrinkModelsoftshrink.py:33 The type’s callbacks and stored state align with this responsibility.
Represents model/data state used by the operation SoftshrinkModelsoftshrink.py:58 The type’s callbacks and stored state align with this responsibility.
Python-to-native custom operator bridge Objective-C++ custom op / softshrink.py:27 Keeps Metal/framework setup and encoding out of entry or lifecycle code.
Runtime-compiled Metal kernel on PyTorch’s MPS stream CustomSoftshrink.h:19 GPU-parallel code remains in the Metal compilation boundary.

Shader boundary reference

CustomSoftshrink.h:19 — representative GPU entry/helper

kernel void softshrink_kernel(constant T*     input  [[buffer(0)]],
                              device   T*     output [[buffer(1)]],
                              constant float& lambda [[buffer(2)]],
                              uint index [[thread_position_in_grid]]) {
    output[index] = input[index] >  lambda ? input[index] - lambda :
                    input[index] < -lambda ? input[index] + lambda : 0;
}

Design patterns

Pattern Source evidence Purpose or tradeoff
Custom operator bridge softshrink.py:27 Makes the sample’s Python-to-native custom operator bridge an explicit, reviewable boundary.
Reference-versus-custom strategy run_sample.py:23 Makes the sample’s Python-to-native custom operator bridge an explicit, reviewable boundary.
Language boundary softshrink.py:27 Python-facing APIs isolate compiled native/GPU implementation details.

Naming conventions

  • Role suffixes make ownership visible: CustomMPSSoftshrinkModel, SoftshrinkModel.
  • Method names describe setup or encoding actions: __init__, forward, extra_repr, test_speedup, test_correctness, test_softshrink.
  • Shader entry points use stage/operation names: softshrink_kernel.
  • Names favor concrete domain roles and target-local types; no broad public library namespace is introduced.

Architecture takeaways

  • Keep Python model/script focused on composition; Objective-C++ custom op is the owner of Python-to-native custom operator bridge.
  • Treat runtime-compiled Metal kernel on PyTorch’s MPS stream as a separate execution/compilation boundary with explicit resource and data-layout contracts.
  • The verified ownership edge is run_samplecustom_model; broader exclusive ownership is not inferred.
  • Access-control rationale follows concrete language boundaries rather than translating every header or shader symbol into Swift terms.

Source map

Source file Architectural role
softshrink.py MPSSoftshrink, CustomMPSSoftshrinkModel, SoftshrinkModel
run_sample.py entry point or feature implementation
CustomSoftshrink.h entry point or feature implementation
CustomSoftshrink.mm entry point or feature implementation
compiler.py entry point or feature implementation