tensorflow-metal-plugin 0.1.2__tar.gz → 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/Makefile +28 -3
  2. {tensorflow_metal_plugin-0.1.2/tensorflow_metal_plugin.egg-info → tensorflow_metal_plugin-0.2.0}/PKG-INFO +40 -21
  3. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/README.md +39 -20
  4. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/setup.py +1 -1
  5. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/plugin_init.cc +26 -0
  6. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_cudnn_rnn_ops.mm +321 -62
  7. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_gather_scatter_ops.mm +127 -0
  8. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_kernel_util.mm +10 -0
  9. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_matmul_op.mm +93 -7
  10. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_maxpool_argmax_ops.mm +99 -35
  11. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_misc_ops.mm +142 -8
  12. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_mps_graph.mm +12 -0
  13. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_random_ops.mm +34 -24
  14. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_shader_library.h +9 -0
  15. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_shader_library.mm +275 -140
  16. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_training_ops.mm +57 -42
  17. tensorflow_metal_plugin-0.2.0/src/tensorflow/core/common_runtime/metal/metal_graph.h +43 -0
  18. tensorflow_metal_plugin-0.2.0/src/tensorflow/core/common_runtime/metal/metal_graph.mm +570 -0
  19. tensorflow_metal_plugin-0.2.0/src/tensorflow/core/common_runtime/metal/metal_profiler.h +58 -0
  20. tensorflow_metal_plugin-0.2.0/src/tensorflow/core/common_runtime/metal/metal_profiler.mm +310 -0
  21. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/metal_stream.h +11 -0
  22. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/metal_stream.mm +24 -1
  23. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/metal_stream_executor.mm +120 -12
  24. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0/tensorflow_metal_plugin.egg-info}/PKG-INFO +40 -21
  25. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/tensorflow_metal_plugin.egg-info/SOURCES.txt +4 -0
  26. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/LICENSE +0 -0
  27. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/MANIFEST.in +0 -0
  28. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/pyproject.toml +0 -0
  29. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/setup.cfg +0 -0
  30. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_activation_ops.mm +0 -0
  31. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_alias_ops.mm +0 -0
  32. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_array_ops.mm +0 -0
  33. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_batch_norm_global_ops.mm +0 -0
  34. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_batch_norm_ops.mm +0 -0
  35. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_batch_space_ops.mm +0 -0
  36. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_bincount_ops.mm +0 -0
  37. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_box_proposal_ops.mm +0 -0
  38. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_collective_ops.mm +0 -0
  39. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_compare_ops.mm +0 -0
  40. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_conv3d_ops.mm +0 -0
  41. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_conv_generic_ops.mm +0 -0
  42. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_conv_ops.mm +0 -0
  43. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_crop_resize_ops.mm +0 -0
  44. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_ctc_ops.mm +0 -0
  45. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_debug_ops.mm +0 -0
  46. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_depthwise_ops.mm +0 -0
  47. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_dilation_ops.mm +0 -0
  48. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_dynamic_ops.mm +0 -0
  49. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_elementwise_ops.mm +0 -0
  50. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_extra_ops.mm +0 -0
  51. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_fft_ops.mm +0 -0
  52. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_fill_ops.mm +0 -0
  53. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_fused_ops.mm +0 -0
  54. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_identity_op.mm +0 -0
  55. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_image2_ops.mm +0 -0
  56. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_image_ops.mm +0 -0
  57. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_index_ops.mm +0 -0
  58. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_inplace_ops.mm +0 -0
  59. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_kernel_util.h +0 -0
  60. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_kernels.h +0 -0
  61. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_kernels.mm +0 -0
  62. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_linalg_ops.mm +0 -0
  63. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_matrix_ops.mm +0 -0
  64. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_misc2_ops.mm +0 -0
  65. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_mps_graph.h +0 -0
  66. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_nms_ops.mm +0 -0
  67. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_nn_ops.mm +0 -0
  68. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_pool_variant_ops.mm +0 -0
  69. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_pooling_ops.mm +0 -0
  70. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_quant_ops.mm +0 -0
  71. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_quantize_dequantize_ops.mm +0 -0
  72. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_random_dist_ops.mm +0 -0
  73. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_reduction_ops.mm +0 -0
  74. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_ref_variable_ops.mm +0 -0
  75. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_resize_grad_ops.mm +0 -0
  76. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_rnn_ops.mm +0 -0
  77. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_search_ops.mm +0 -0
  78. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_slice_ops.mm +0 -0
  79. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_sparse_manip_ops.mm +0 -0
  80. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_sparse_ops.mm +0 -0
  81. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_sparse_segment_ops.mm +0 -0
  82. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_strided_ops.mm +0 -0
  83. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_transform_ops.mm +0 -0
  84. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/kernels/metal_volume_patch_ops.mm +0 -0
  85. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/metal_buffer_registry.h +0 -0
  86. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/metal_buffer_registry.mm +0 -0
  87. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/metal_platform.h +0 -0
  88. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/metal_platform.mm +0 -0
  89. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/src/tensorflow/core/common_runtime/metal/metal_stream_executor.h +0 -0
  90. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/tensorflow-plugins/__init__.py +0 -0
  91. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/tensorflow_metal_plugin.egg-info/dependency_links.txt +0 -0
  92. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/tensorflow_metal_plugin.egg-info/requires.txt +0 -0
  93. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/tensorflow_metal_plugin.egg-info/top_level.txt +0 -0
  94. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/tools/check_symbols.sh +0 -0
  95. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/tools/compile_shaders.py +0 -0
  96. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/tools/op_sweep.py +0 -0
  97. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/tools/probe_stream_options.sh +0 -0
  98. {tensorflow_metal_plugin-0.1.2 → tensorflow_metal_plugin-0.2.0}/tools/recipes.py +0 -0
@@ -23,6 +23,19 @@ SDK := $(shell xcrun --sdk macosx --show-sdk-path 2>/dev/null)
23
23
  BUILD := build
24
24
  OUT := $(BUILD)/libmetal_plugin.dylib
25
25
 
26
+ # Which TensorFlow the objects in $(BUILD) were compiled against.
27
+ #
28
+ # The header dependencies recorded by -MMD name paths inside that TensorFlow,
29
+ # and those paths keep existing when PYTHON is pointed at a different one, so
30
+ # make sees nothing to redo and links objects built against two different
31
+ # TensorFlows. The result is a library that fails to load with an absl symbol
32
+ # missing, which says nothing about the cause. This makes the include path
33
+ # itself a dependency.
34
+ STAMP := $(BUILD)/tf-include-path
35
+ $(shell mkdir -p $(BUILD); \
36
+ [ "$$(cat $(STAMP) 2>/dev/null)" = "$(TF_INCLUDE)" ] || \
37
+ printf '%s' "$(TF_INCLUDE)" > $(STAMP))
38
+
26
39
  SOURCES := src/plugin_init.cc \
27
40
  $(wildcard src/tensorflow/core/common_runtime/metal/*.mm) \
28
41
  $(wildcard src/tensorflow/core/common_runtime/metal/kernels/*.mm)
@@ -69,7 +82,7 @@ LDFLAGS := -dynamiclib $(FRAMEWORKS) \
69
82
  -Wl,-undefined,dynamic_lookup \
70
83
  -Wl,-rpath,$(TF_LIB)
71
84
 
72
- .PHONY: all clean test sweep install
85
+ .PHONY: all clean test test-stream sweep install
73
86
 
74
87
  all: $(OUT)
75
88
 
@@ -78,7 +91,7 @@ $(OUT): $(OBJECTS)
78
91
  $(CXX) $(OBJECTS) $(LDFLAGS) -o $@
79
92
  @echo "built $@"
80
93
 
81
- $(BUILD)/%.o: src/%
94
+ $(BUILD)/%.o: src/% $(STAMP)
82
95
  @mkdir -p $(dir $@)
83
96
  $(CXX) $(CXXFLAGS) -c $< -o $@
84
97
 
@@ -86,9 +99,21 @@ $(BUILD)/%.o: src/%
86
99
  check-symbols: $(OUT)
87
100
  @bash tools/check_symbols.sh $(OUT)
88
101
 
89
- test: $(OUT)
102
+ test: $(OUT) test-stream
90
103
  $(PYTHON) tests/run_tests.py
91
104
 
105
+ # The StreamExecutor C API driven directly, without TensorFlow. Reaches what
106
+ # no op can: memset32 with a pattern that is not four equal bytes has one
107
+ # caller in the whole tree and it is a CUDA-only kernel.
108
+ $(BUILD)/stream_executor_test: tests/stream_executor_test.mm $(OUT)
109
+ @mkdir -p $(dir $@)
110
+ $(CXX) $(CXXFLAGS) tests/stream_executor_test.mm -o $@ \
111
+ -framework Foundation -L$(TF_LIB) -ltensorflow_framework.2 \
112
+ -Wl,-rpath,$(TF_LIB)
113
+
114
+ test-stream: $(BUILD)/stream_executor_test
115
+ $(BUILD)/stream_executor_test $(OUT)
116
+
92
117
  # Every registered op, through TensorFlow's own dispatch, against the CPU.
93
118
  sweep: $(OUT)
94
119
  PYTHONPATH=tools $(PYTHON) tools/op_sweep.py
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tensorflow-metal-plugin
3
- Version: 0.1.2
3
+ Version: 0.2.0
4
4
  Summary: Metal GPU backend for TensorFlow on Apple silicon
5
5
  License: Apache-2.0
6
6
  Classifier: Development Status :: 3 - Alpha
@@ -28,10 +28,15 @@ PluggableDevice. It loads into a stock TensorFlow wheel and adds
28
28
  `/physical_device:GPU:0`.
29
29
 
30
30
  This is the out-of-tree form of the backend proposed in
31
- [tensorflow/tensorflow#126254](https://github.com/tensorflow/tensorflow/pull/126254).
31
+ [tensorflow/tensorflow#126384](https://github.com/tensorflow/tensorflow/pull/126384).
32
32
  The sources are the same; the only difference is this repository exports
33
- `SE_InitPlugin` and `TF_InitKernel` from a shared object, where the in-tree
34
- form hands the same function pointers to `RegisterPluggableDevicePlugin`.
33
+ `SE_InitPlugin`, `TF_InitKernel`, `TF_InitProfiler` and `TF_InitGraph` from a
34
+ shared object, where the in-tree form hands the same function pointers to
35
+ `RegisterPluggableDevicePlugin`.
36
+
37
+ Four modules, then: the device and its memory, the kernels, a profiler that
38
+ puts Metal work on the TensorFlow timeline, and a graph pass that fuses a bias
39
+ and an activation into the convolution or matrix multiply in front of them.
35
40
 
36
41
  ## Status
37
42
 
@@ -126,8 +131,15 @@ error raised. `model.fit` reported `[nan, nan, nan]` and carried on.
126
131
 
127
132
  While those entry points are missing, every Metal kernel waits for the GPU
128
133
  before returning, which closes the window. It is announced in a warning at
129
- load, and `TF_METAL_SYNCHRONOUS` forces it either way. When the entry points
130
- come back the plugin returns to running asynchronously with no change here.
134
+ load, and `TF_METAL_SYNCHRONOUS` forces it either way.
135
+
136
+ Where the entry points do exist, the plugin implements `AssignVariableOp`,
137
+ `AssignAddVariableOp` and `AssignSubVariableOp` itself, on the device, and
138
+ nothing touches a variable from the host at all. That is what makes running
139
+ asynchronously safe rather than merely faster: without those kernels, an
140
+ asynchronous run reproduced the same `nan` on TensorFlow 2.19.1, where the C
141
+ API is present. With them, a training step is 11.48 ms against 21.04 ms on the
142
+ CPU, and correct.
131
143
 
132
144
  ## Is it faster than the CPU
133
145
 
@@ -136,21 +148,25 @@ on an M4 Max against TensorFlow 2.21.0, median of ten runs each, both devices
136
148
  in the same process on the same data, waiting for the device before stopping
137
149
  the clock:
138
150
 
139
- Two columns of speedup, because the wait above costs most of it. "Today" is
140
- what you get from a released TensorFlow; "async" is the same machine with
141
- `TF_METAL_SYNCHRONOUS=0`, which is what the plugin does once
151
+ Two columns, because the wait above costs most of it. "Today" is a released
152
+ TensorFlow, 2.21.0, where the kernel C API for resource variables is missing
153
+ and every kernel therefore waits. "Async" is 2.19.1, which still exports that
154
+ API, so the same code runs asynchronously; it is what a released TensorFlow
155
+ does again once
142
156
  [#126374](https://github.com/tensorflow/tensorflow/issues/126374) is fixed.
157
+ Both columns are measured, not projected.
143
158
 
144
159
  | | GPU today | CPU | today | async |
145
160
  | --- | ---: | ---: | ---: | ---: |
146
- | MatMul 2048x2048 | 3.80 ms | 14.42 ms | **3.8x** | 6.5x |
147
- | Conv2D, batch 64, 64x64x32 to 64 | 4.58 ms | 10.91 ms | **2.4x** | 3.0x |
148
- | MatMul 1024x1024 | 1.12 ms | 2.17 ms | 1.9x | 1.5x |
149
- | CNN training step, SGD, batch 128 | 19.46 ms | 20.61 ms | 1.1x | 1.7x |
150
- | CNN forward, batch 128 | 5.53 ms | 6.00 ms | 1.1x | 1.5x |
151
- | MatMul 512x512 | 0.44 ms | 0.36 ms | 0.8x | 0.9x |
152
- | ReduceSum 4096x4096 | 0.44 ms | 0.34 ms | 0.8x | 0.8x |
153
- | Elementwise 4096x4096 | 2.88 ms | 1.54 ms | 0.5x | 0.5x |
161
+ | MatMul 2048x2048 | 1.78 ms | 11.97 ms | **6.7x** | 5.5x |
162
+ | Conv2D, batch 64, 64x64x32 to 64 | 2.25 ms | 8.95 ms | **4.0x** | 4.2x |
163
+ | CNN training step, SGD, batch 128 | 12.60 ms | 18.80 ms | 1.5x | 1.8x |
164
+ | MatMul 1024x1024 | 1.19 ms | 1.89 ms | 1.6x | 2.0x |
165
+ | Conv2D, batch 16 | 1.37 ms | 2.31 ms | 1.7x | 1.7x |
166
+ | CNN forward, batch 128 | 4.55 ms | 5.17 ms | 1.1x | 1.5x |
167
+ | MatMul 512x512 | 0.37 ms | 0.34 ms | 0.9x | 1.0x |
168
+ | ReduceSum 4096x4096 | 0.46 ms | 0.28 ms | 0.6x | 0.7x |
169
+ | Elementwise 4096x4096 | 3.05 ms | 1.36 ms | 0.5x | 0.5x |
154
170
 
155
171
  The pattern is the ordinary one and worth stating plainly: the GPU wins where
156
172
  there is arithmetic to do per byte moved, and loses where there is not. A
@@ -159,9 +175,12 @@ operations per element, so it is bound by memory on a machine whose CPU shares
159
175
  that same memory. Small matrices lose to the cost of getting work to the
160
176
  device at all.
161
177
 
162
- A training step is 1.7x the CPU when the plugin can run asynchronously, and
163
- barely ahead of it while it cannot. That gap is the cost of the missing entry
164
- points, not of the backend.
178
+ The convolution numbers owe as much to the graph pass as to the kernels. It
179
+ folds the bias and the activation into the convolution, and it turns
180
+ TensorFlow's layout optimizer off, which was inserting an NHWC to NCHW
181
+ transpose on either side of every convolution: on a 4x16x16x8 case those cost
182
+ 42.8 and 44.2 microseconds around a 43.0 microsecond convolution. MPSGraph
183
+ takes either layout, so the rewrite was pure loss.
165
184
 
166
185
  `benchmarks/benchmark.py` reproduces the table.
167
186
 
@@ -264,7 +283,7 @@ Metal kernels with their dtypes is in
264
283
  ## Layout
265
284
 
266
285
  ```
267
- src/plugin_init.cc the two exported entry points
286
+ src/plugin_init.cc the exported entry points
268
287
  src/tensorflow/core/common_runtime/metal/ the backend, verbatim from the
269
288
  TensorFlow tree
270
289
  tools/ build probes and the symbol check
@@ -5,10 +5,15 @@ PluggableDevice. It loads into a stock TensorFlow wheel and adds
5
5
  `/physical_device:GPU:0`.
6
6
 
7
7
  This is the out-of-tree form of the backend proposed in
8
- [tensorflow/tensorflow#126254](https://github.com/tensorflow/tensorflow/pull/126254).
8
+ [tensorflow/tensorflow#126384](https://github.com/tensorflow/tensorflow/pull/126384).
9
9
  The sources are the same; the only difference is this repository exports
10
- `SE_InitPlugin` and `TF_InitKernel` from a shared object, where the in-tree
11
- form hands the same function pointers to `RegisterPluggableDevicePlugin`.
10
+ `SE_InitPlugin`, `TF_InitKernel`, `TF_InitProfiler` and `TF_InitGraph` from a
11
+ shared object, where the in-tree form hands the same function pointers to
12
+ `RegisterPluggableDevicePlugin`.
13
+
14
+ Four modules, then: the device and its memory, the kernels, a profiler that
15
+ puts Metal work on the TensorFlow timeline, and a graph pass that fuses a bias
16
+ and an activation into the convolution or matrix multiply in front of them.
12
17
 
13
18
  ## Status
14
19
 
@@ -103,8 +108,15 @@ error raised. `model.fit` reported `[nan, nan, nan]` and carried on.
103
108
 
104
109
  While those entry points are missing, every Metal kernel waits for the GPU
105
110
  before returning, which closes the window. It is announced in a warning at
106
- load, and `TF_METAL_SYNCHRONOUS` forces it either way. When the entry points
107
- come back the plugin returns to running asynchronously with no change here.
111
+ load, and `TF_METAL_SYNCHRONOUS` forces it either way.
112
+
113
+ Where the entry points do exist, the plugin implements `AssignVariableOp`,
114
+ `AssignAddVariableOp` and `AssignSubVariableOp` itself, on the device, and
115
+ nothing touches a variable from the host at all. That is what makes running
116
+ asynchronously safe rather than merely faster: without those kernels, an
117
+ asynchronous run reproduced the same `nan` on TensorFlow 2.19.1, where the C
118
+ API is present. With them, a training step is 11.48 ms against 21.04 ms on the
119
+ CPU, and correct.
108
120
 
109
121
  ## Is it faster than the CPU
110
122
 
@@ -113,21 +125,25 @@ on an M4 Max against TensorFlow 2.21.0, median of ten runs each, both devices
113
125
  in the same process on the same data, waiting for the device before stopping
114
126
  the clock:
115
127
 
116
- Two columns of speedup, because the wait above costs most of it. "Today" is
117
- what you get from a released TensorFlow; "async" is the same machine with
118
- `TF_METAL_SYNCHRONOUS=0`, which is what the plugin does once
128
+ Two columns, because the wait above costs most of it. "Today" is a released
129
+ TensorFlow, 2.21.0, where the kernel C API for resource variables is missing
130
+ and every kernel therefore waits. "Async" is 2.19.1, which still exports that
131
+ API, so the same code runs asynchronously; it is what a released TensorFlow
132
+ does again once
119
133
  [#126374](https://github.com/tensorflow/tensorflow/issues/126374) is fixed.
134
+ Both columns are measured, not projected.
120
135
 
121
136
  | | GPU today | CPU | today | async |
122
137
  | --- | ---: | ---: | ---: | ---: |
123
- | MatMul 2048x2048 | 3.80 ms | 14.42 ms | **3.8x** | 6.5x |
124
- | Conv2D, batch 64, 64x64x32 to 64 | 4.58 ms | 10.91 ms | **2.4x** | 3.0x |
125
- | MatMul 1024x1024 | 1.12 ms | 2.17 ms | 1.9x | 1.5x |
126
- | CNN training step, SGD, batch 128 | 19.46 ms | 20.61 ms | 1.1x | 1.7x |
127
- | CNN forward, batch 128 | 5.53 ms | 6.00 ms | 1.1x | 1.5x |
128
- | MatMul 512x512 | 0.44 ms | 0.36 ms | 0.8x | 0.9x |
129
- | ReduceSum 4096x4096 | 0.44 ms | 0.34 ms | 0.8x | 0.8x |
130
- | Elementwise 4096x4096 | 2.88 ms | 1.54 ms | 0.5x | 0.5x |
138
+ | MatMul 2048x2048 | 1.78 ms | 11.97 ms | **6.7x** | 5.5x |
139
+ | Conv2D, batch 64, 64x64x32 to 64 | 2.25 ms | 8.95 ms | **4.0x** | 4.2x |
140
+ | CNN training step, SGD, batch 128 | 12.60 ms | 18.80 ms | 1.5x | 1.8x |
141
+ | MatMul 1024x1024 | 1.19 ms | 1.89 ms | 1.6x | 2.0x |
142
+ | Conv2D, batch 16 | 1.37 ms | 2.31 ms | 1.7x | 1.7x |
143
+ | CNN forward, batch 128 | 4.55 ms | 5.17 ms | 1.1x | 1.5x |
144
+ | MatMul 512x512 | 0.37 ms | 0.34 ms | 0.9x | 1.0x |
145
+ | ReduceSum 4096x4096 | 0.46 ms | 0.28 ms | 0.6x | 0.7x |
146
+ | Elementwise 4096x4096 | 3.05 ms | 1.36 ms | 0.5x | 0.5x |
131
147
 
132
148
  The pattern is the ordinary one and worth stating plainly: the GPU wins where
133
149
  there is arithmetic to do per byte moved, and loses where there is not. A
@@ -136,9 +152,12 @@ operations per element, so it is bound by memory on a machine whose CPU shares
136
152
  that same memory. Small matrices lose to the cost of getting work to the
137
153
  device at all.
138
154
 
139
- A training step is 1.7x the CPU when the plugin can run asynchronously, and
140
- barely ahead of it while it cannot. That gap is the cost of the missing entry
141
- points, not of the backend.
155
+ The convolution numbers owe as much to the graph pass as to the kernels. It
156
+ folds the bias and the activation into the convolution, and it turns
157
+ TensorFlow's layout optimizer off, which was inserting an NHWC to NCHW
158
+ transpose on either side of every convolution: on a 4x16x16x8 case those cost
159
+ 42.8 and 44.2 microseconds around a 43.0 microsecond convolution. MPSGraph
160
+ takes either layout, so the rewrite was pure loss.
142
161
 
143
162
  `benchmarks/benchmark.py` reproduces the table.
144
163
 
@@ -241,7 +260,7 @@ Metal kernels with their dtypes is in
241
260
  ## Layout
242
261
 
243
262
  ```
244
- src/plugin_init.cc the two exported entry points
263
+ src/plugin_init.cc the exported entry points
245
264
  src/tensorflow/core/common_runtime/metal/ the backend, verbatim from the
246
265
  TensorFlow tree
247
266
  tools/ build probes and the symbol check
@@ -75,7 +75,7 @@ class BinaryDistribution(Distribution):
75
75
 
76
76
  setup(
77
77
  name="tensorflow-metal-plugin",
78
- version="0.1.2",
78
+ version="0.2.0",
79
79
  description="Metal GPU backend for TensorFlow on Apple silicon",
80
80
  long_description=(HERE / "README.md").read_text(),
81
81
  long_description_content_type="text/markdown",
@@ -27,7 +27,9 @@ limitations under the License.
27
27
  #include "tensorflow/c/experimental/stream_executor/stream_executor.h"
28
28
  #include "tensorflow/c/tf_status.h"
29
29
  #include "tensorflow/core/common_runtime/metal/kernels/metal_kernels.h"
30
+ #include "tensorflow/core/common_runtime/metal/metal_graph.h"
30
31
  #include "tensorflow/core/common_runtime/metal/metal_platform.h"
32
+ #include "tensorflow/core/common_runtime/metal/metal_profiler.h"
31
33
 
32
34
  namespace {
33
35
 
@@ -55,4 +57,28 @@ void SE_InitPlugin(SE_PlatformRegistrationParams* params, TF_Status* status) {
55
57
 
56
58
  void TF_InitKernel() { tensorflow::metal::RegisterAllMetalKernels(); }
57
59
 
60
+ // Optional: TensorFlow looks this up and carries on without it if absent. It
61
+ // is what puts a Metal row in the trace viewer next to the host's.
62
+ void TF_InitProfiler(TF_ProfilerRegistrationParams* params,
63
+ TF_Status* status) {
64
+ if (MetalDisabledByEnvironment()) {
65
+ TF_SetStatus(status, TF_FAILED_PRECONDITION,
66
+ "Metal: backend disabled by TF_DISABLE_METAL.");
67
+ return;
68
+ }
69
+ tensorflow::metal::MetalInitProfiler(params, status);
70
+ }
71
+
72
+ // Optional, like the profiler. Fuses a bias and an activation into the
73
+ // convolution or matrix multiply in front of them, which this backend has
74
+ // kernels for and which nothing else produces for a pluggable device.
75
+ void TF_InitGraph(TP_OptimizerRegistrationParams* params, TF_Status* status) {
76
+ if (MetalDisabledByEnvironment()) {
77
+ TF_SetStatus(status, TF_FAILED_PRECONDITION,
78
+ "Metal: backend disabled by TF_DISABLE_METAL.");
79
+ return;
80
+ }
81
+ tensorflow::metal::MetalInitGraph(params, status);
82
+ }
83
+
58
84
  } // extern "C"