Genta Hirauchi

公開日:2020/03/10
更新日:2020/08/03

【Swift Metal】thread_position_in_grid等の属性について解説

  • thread_position_in_gridやthreads_per_gridって何?
  • 1D、2D、3Dで値がどう変わるの?

こんな悩みを抱えている方、たくさんいらっしゃると思います。
僕自身、MSLの実装を始めたばかりの頃は、属性値をなかなか理解できずに苦労致しました。

本記事では、MSLの属性値についての解説や、実際にどのような値が取れるのかについて記載しております。

目次

MSLの属性について

まずはじめに、MSLにはどのような属性があるのかを紹介致します。以下は、MSLに用意されている属性の中から、GPGPUの実装で使用頻度の高い属性をピックアップしたものです。

属性データタイプ概要
thread_execution_widthushort, uintThreadGroupの横幅
thread_position_in_gridushort, ushort2, ushort3, uint, uint2, uint3GridにおけるThreadの位置
thread_position_in_threadgroupushort, ushort2, ushort3, uint, uint2, uint3ThreadGroupにおけるThreadの位置
threadgroup_position_in_gridushort, ushort2, ushort3, uint, uint2, uint3GridにおけるThreadGroupの位置
threadgroups_per_gridushort, ushort2, ushort3, uint, uint2, uint3Grid毎のThreadGroup数
threads_per_gridushort, ushort2, ushort3, uint, uint2, uint3Grid毎のThread数
threads_per_threadgroupushort, ushort2, ushort3, uint, uint2, uint3ThreadGroup毎のThread数

その他の属性に関しましては、Metal Shading Language Specificationの”Table 5.7. Attributes for kernel function input arguments”に記載されておりますので、こちらをご覧いただければと思います。

1D、2D、3Dにおける属性の値

ここからは、実際に属性にはどのような値が格納されているのかをみていきます。
なお、今回みていく属性は、「MSLの属性について」でピックアップした7つの属性に絞っておりますので、予めご了承ください。

1Dの場合

まずは、1D(widthのみ)の実装の場合の値についてです。以下は、1000000の並列処理をGPUで実行するサンプルコードです。

  • ViewController
  • Shader.metal
import UIKit
import Metal

class ViewController: UIViewController {

    private let device = MTLCreateSystemDefaultDevice()!
    private var library: MTLLibrary!
    private var commandQueue: MTLCommandQueue!
    private var computePipelineState: MTLComputePipelineState!

    private let xCount = 1000000
    private var outputDataCount = 0

    let attribute = ["thread_execution_width", "thread_position_in_grid", "thread_position_in_threadgroup", "threadgroup_position_in_grid", "threadgroups_per_grid", "threads_per_grid", "threads_per_threadgroup"];

    override func viewDidLoad() {
        super.viewDidLoad()

        outputDataCount = xCount

        initMetal()
        startGPU()
    }

    private func initMetal() {
        guard let libUrl = Bundle.main.url(forResource: "default.metallib", withExtension: nil) else { return }
        library = try! device.makeLibrary(URL: libUrl)

        let function = library.makeFunction(name: "sample")!
        computePipelineState = try! device.makeComputePipelineState(function: function)

        commandQueue = device.makeCommandQueue()!
    }

    private func startGPU() {
        var outputDataList: [[Float]] = []
        for _ in 0..<attribute.count {
            outputDataList.append([Float](repeating: 1, count: outputDataCount))
        }

        var outputBufferList: [MTLBuffer] = []
        for i in 0..<attribute.count {
            outputBufferList.append(device.makeBuffer(bytes: outputDataList[i], length: MemoryLayout<Float>.stride * outputDataList[i].count, options: [])!)
        }

        let commandBuffer = commandQueue.makeCommandBuffer()!
        let computeCommandEncoder = commandBuffer.makeComputeCommandEncoder()!
        computeCommandEncoder.setComputePipelineState(computePipelineState)

        for i in 0..<attribute.count {
            computeCommandEncoder.setBuffer(outputBufferList[i], offset: 0, index: i)
        }
        computeCommandEncoder.setBytes(&outputDataCount, length: MemoryLayout<Float>.size, index: attribute.count)

        let width = computePipelineState.threadExecutionWidth
        let threadgroupsPerGrid = MTLSize(width: (xCount + width - 1) / width, height: 1, depth: 1)
        let threadsPerThreadgroup = MTLSize(width: width, height: 1, depth: 1)
        print("threadgroupsPerGrid  : \(threadgroupsPerGrid)")
        print("threadsPerThreadgroup: \(threadsPerThreadgroup)")
        computeCommandEncoder.dispatchThreadgroups(threadgroupsPerGrid, threadsPerThreadgroup: threadsPerThreadgroup)

        computeCommandEncoder.endEncoding()
        commandBuffer.commit()
        commandBuffer.waitUntilCompleted()

        var resultDataList: [[Float]] = []
        for i in 0..<attribute.count {
            let result = Data(bytesNoCopy: outputBufferList[i].contents(), count: MemoryLayout<Float>.stride * outputDataList[i].count, deallocator: .none)
            resultDataList.append(
                result.withUnsafeBytes {
                    Array(UnsafeBufferPointer(start: $0.baseAddress!.assumingMemoryBound(to: Float.self ), count: $0.count / MemoryLayout<Float>.size))
                }
            )
        }

        for i in 0..<outputDataCount {
            var str = "index: \(i) ||"
            for j in 0..<attribute.count {
                str.append(" \(attribute[j]): \(resultDataList[j][i]), ")
            }
            print(str)
        }
    }
}
#include <metal_stdlib>
using namespace metal;

kernel void sample(device float* outputData0    [[ buffer(0) ]],
                   device float* outputData1    [[ buffer(1) ]],
                   device float* outputData2    [[ buffer(2) ]],
                   device float* outputData3    [[ buffer(3) ]],
                   device float* outputData4    [[ buffer(4) ]],
                   device float* outputData5    [[ buffer(5) ]],
                   device float* outputData6    [[ buffer(6) ]],
                   const device int& dataLength [[ buffer(7) ]],

                   uint thread_execution_width         [[thread_execution_width]],         // ThreadGroupの横幅
                   uint thread_position_in_grid        [[thread_position_in_grid]],        // GridにおけるThreadの位置
                   uint thread_position_in_threadgroup [[thread_position_in_threadgroup]], // ThreadGroupにおけるThreadの位置
                   uint threadgroup_position_in_grid   [[threadgroup_position_in_grid]],   // GridにおけるThreadGroupの位置
                   uint threadgroups_per_grid          [[threadgroups_per_grid]],          // Grid毎のThreadGroup数
                   uint threads_per_grid               [[threads_per_grid]],               // Grid毎のThread数
                   uint threads_per_threadgroup        [[threads_per_threadgroup]])        // ThreadGroup毎のThread数
{
    int index = thread_position_in_grid;

    if (index > dataLength) { return; }

    outputData0[index]  = thread_execution_width;
    outputData1[index]  = thread_position_in_grid;
    outputData2[index]  = thread_position_in_threadgroup;
    outputData3[index]  = threadgroup_position_in_grid;
    outputData4[index]  = threadgroups_per_grid;
    outputData5[index]  = threads_per_grid;
    outputData6[index]  = threads_per_threadgroup;
}

【スレッドとスレッドグループの値】

threadgroupsPerGrid : MTLSize(width: 31250, height: 1, depth: 1)
threadsPerThreadgroup: MTLSize(width: 32, height: 1, depth: 1)

【出力結果(重要な部分のみピックアップ)】

indexthread
execution
width
thread
position
in
grid
thread
position
in
threadgroup
threadgroup
position
in
grid
threadgroups
per
grid
threads
per
grid
threads
per
threadgroup
032.00.00.00.031250.01000000.032.0
132.01.01.00.031250.01000000.032.0
232.02.02.00.031250.01000000.032.0
332.03.03.00.031250.01000000.032.0
3032.030.030.00.031250.01000000.032.0
3132.031.031.00.031250.01000000.032.0
3232.032.00.01.031250.01000000.032.0
3332.033.01.01.031250.01000000.032.0
99999632.0999996.028.031249.031250.01000000.032.0
99999732.0999997.029.031249.031250.01000000.032.0
99999832.0999998.030.031249.031250.01000000.032.0
99999932.0999999.031.031249.031250.01000000.032.0

thread_execution_widthからは、32が出力されました。
これは、Threadのwidthです。

thread_position_in_gridからは、0~999999が出力されました。
GridのWidthが、Threadのwidth(32) * ThreadGroupのwidth(31250) = 1000000 であるため、そのWidth間のThreadは0~999999となるためです。

thread_position_in_threadgroupからは、0~31が繰り返し出力されました。
ThreadGroupのWidthが32であるため、そのWidth間のThreadは0~31となるためです。

threadgroup_position_in_gridからは、32ごとにインクリメントされた値が出力されました。
ThreadGroupのWidthが32であるため、ThreadGroupの位置は、32ごとに切り替わるためです。

threadgroups_per_gridからは、31250が出力されました。
これは、dispatchThreadgroupsで設定した、threadgroupsPerGridのwidthです。

threads_per_gridからは、1000000が出力されました。
これは、dispatchThreadgroupsで設定した、threadgroupsPerGridのwidth(31250)と、threadsPerThreadgroupのwidth(32)をかけた値です。

threads_per_threadgroupからは、32が出力されました。
これは、dispatchThreadgroupsで設定した、threadsPerThreadgroupのwidthです。

2Dの場合

続いて、2D(width * height)の実装の場合の値についてです。
※ 変更箇所の色を変えております。

  • ViewController
  • Shader.metal
import UIKit
import Metal

class ViewController: UIViewController {

    private let device = MTLCreateSystemDefaultDevice()!
    private var library: MTLLibrary!
    private var commandQueue: MTLCommandQueue!
    private var computePipelineState: MTLComputePipelineState!

    private let xCount = 1000
    private let yCount = 1000
    private var outputDataCount = 0

    let attribute = ["thread_execution_width", "thread_position_in_grid", "thread_position_in_threadgroup", "threadgroup_position_in_grid", "threadgroups_per_grid", "threads_per_grid", "threads_per_threadgroup"];

    override func viewDidLoad() {
        super.viewDidLoad()

        outputDataCount = xCount * yCount

        initMetal()
        startGPU()
    }

    private func initMetal() {
        guard let libUrl = Bundle.main.url(forResource: "default.metallib", withExtension: nil) else { return }
        library = try! device.makeLibrary(URL: libUrl)

        let function = library.makeFunction(name: "sample")!
        computePipelineState = try! device.makeComputePipelineState(function: function)

        commandQueue = device.makeCommandQueue()!
    }

    private func startGPU() {
        var outputDataList: [[Float]] = []
        for _ in 0..<attribute.count {
            outputDataList.append([Float](repeating: 1, count: outputDataCount))
        }

        var outputBufferList: [MTLBuffer] = []
        for i in 0..<attribute.count {
            outputBufferList.append(device.makeBuffer(bytes: outputDataList[i], length: MemoryLayout<Float>.stride * outputDataList[i].count, options: [])!)
        }

        let commandBuffer = commandQueue.makeCommandBuffer()!
        let computeCommandEncoder = commandBuffer.makeComputeCommandEncoder()!
        computeCommandEncoder.setComputePipelineState(computePipelineState)

        for i in 0..<attribute.count {
            computeCommandEncoder.setBuffer(outputBufferList[i], offset: 0, index: i)
        }
        computeCommandEncoder.setBytes(&outputDataCount, length: MemoryLayout<Float>.size, index: attribute.count)

        let max    = computePipelineState.maxTotalThreadsPerThreadgroup
        let width  = computePipelineState.threadExecutionWidth
        let height = max / width
        let threadgroupsPerGrid = MTLSize(width: (xCount + width - 1) / width, height: (yCount + height - 1) / height, depth: 1)
        let threadsPerThreadgroup = MTLSize(width: width, height: height, depth: 1)
        print("threadgroupsPerGrid  : \(threadgroupsPerGrid)")
        print("threadsPerThreadgroup: \(threadsPerThreadgroup)")
        computeCommandEncoder.dispatchThreadgroups(threadgroupsPerGrid, threadsPerThreadgroup: threadsPerThreadgroup)

        computeCommandEncoder.endEncoding()
        commandBuffer.commit()
        commandBuffer.waitUntilCompleted()

        var resultDataList: [[Float]] = []
        for i in 0..<attribute.count {
            let result = Data(bytesNoCopy: outputBufferList[i].contents(), count: MemoryLayout<Float>.stride * outputDataList[i].count, deallocator: .none)
            resultDataList.append(
                result.withUnsafeBytes {
                    Array(UnsafeBufferPointer(start: $0.baseAddress!.assumingMemoryBound(to: Float.self ), count: $0.count / MemoryLayout<Float>.size))
                }
            )
        }

        for i in 0..<outputDataCount {
            var str = "index: \(i) ||"
            for j in 0..<attribute.count {
                str.append(" \(attribute[j]): \(resultDataList[j][i]), ")
            }
            print(str)
        }
    }
}
#include <metal_stdlib>
using namespace metal;

kernel void sample(device float* outputData0    [[ buffer(0) ]],
                   device float* outputData1    [[ buffer(1) ]],
                   device float* outputData2    [[ buffer(2) ]],
                   device float* outputData3    [[ buffer(3) ]],
                   device float* outputData4    [[ buffer(4) ]],
                   device float* outputData5    [[ buffer(5) ]],
                   device float* outputData6    [[ buffer(6) ]],
                   const device int& dataLength [[ buffer(7) ]],

                   uint  thread_execution_width         [[thread_execution_width]],         // ThreadGroupの横幅
                   uint2 thread_position_in_grid        [[thread_position_in_grid]],        // GridにおけるThreadの位置
                   uint2 thread_position_in_threadgroup [[thread_position_in_threadgroup]], // ThreadGroupにおけるThreadの位置
                   uint2 threadgroup_position_in_grid   [[threadgroup_position_in_grid]],   // GridにおけるThreadGroupの位置
                   uint2 threadgroups_per_grid          [[threadgroups_per_grid]],          // Grid毎のThreadGroup数
                   uint2 threads_per_grid               [[threads_per_grid]],               // Grid毎のThread数
                   uint2 threads_per_threadgroup        [[threads_per_threadgroup]])        // ThreadGroup毎のThread数
{
    int index = thread_position_in_grid.y * threads_per_grid.x + thread_position_in_grid.x;

    if (index > dataLength) { return; }
    outputData0[index] = thread_execution_width;
    outputData1[index] = thread_position_in_grid.x;
    outputData2[index] = thread_position_in_threadgroup.x;
    outputData3[index] = threadgroup_position_in_grid.x;
    outputData4[index] = threadgroups_per_grid.x;
    outputData5[index] = threads_per_grid.x;
    outputData6[index] = threads_per_threadgroup.x;
		// Y値の出力結果表示する際は、.xの部分を .yに置き換えております。
}

【スレッドとスレッドグループの値】

threadgroupsPerGrid : MTLSize(width: 32, height: 32, depth: 1)
threadsPerThreadgroup: MTLSize(width: 32, height: 32, depth: 1)

【出力結果(重要な部分のみピックアップ)】

・X値

indexthread
execution
width
thread
position
in
grid
thread
position
in
threadgroup
threadgroup
position
in
grid
threadgroups
per
grid
threads
per
grid
threads
per
threadgroup
032.00.00.00.032.01024.032.0
132.01.01.00.032.01024.032.0
232.02.02.00.032.01024.032.0
332.03.03.00.032.01024.032.0
3032.030.030.00.032.01024.032.0
3132.031.031.00.032.01024.032.0
3232.032.00.01.032.01024.032.0
3332.033.01.01.032.01024.032.0
3276632.01022.030.031.032.01024.032.0
3276732.01023.031.031.032.01024.032.0
3276832.00.00.00.032.01024.032.0
3276932.01.01.00.032.01024.032.0
99999632.0572.028.017.032.01024.032.0
99999732.0573.029.017.032.01024.032.0
99999832.0574.030.017.032.01024.032.0
99999932.0575.031.017.032.01024.032.0

thread_execution_widthからは、32が出力されました。
これは、Threadのwidthです。

thread_position_in_gridからは、0~1023が出力されました。
GridのWidthが、Threadのwidth(32) * ThreadGroupのwidth(32) = 1024 であるため、そのWidth間のThreadは0~1023となるためです。

thread_position_in_threadgroupからは、0~31が繰り返し出力されました。
ThreadGroupのWidthが32であるため、そのWidth間のThreadは0~31となるためです。

threadgroup_position_in_gridからは、32ごとにインクリメントされた値が出力されました。
ThreadGroupのWidthが32であるため、32ごとにインクリメントされます。なお、GridのWidthが1024であるため、1024ごとに0に戻ります。

threadgroups_per_gridからは、32が出力されました。
これは、dispatchThreadgroupsで設定した、threadgroupsPerGridのwidthです。

threads_per_gridからは、1024が出力されました。
これは、dispatchThreadgroupsで設定した、threadgroupsPerGridのwidth(32)と、threadsPerThreadgroupのwidth(32)をかけた値です。

threads_per_threadgroupからは、32が出力されました。
これは、dispatchThreadgroupsで設定した、threadsPerThreadgroupのwidthです。

・Y値

indexthread
execution
width
thread
position
in
grid
thread
position
in
threadgroup
threadgroup
position
in
grid
threadgroups
per
grid
threads
per
grid
threads
per
threadgroup
032.00.00.00.032.01024.032.0
132.00.00.00.032.01024.032.0
232.00.00.00.032.01024.032.0
332.00.00.00.032.01024.032.0
102232.00.00.00.032.01024.032.0
102332.00.00.00.032.01024.032.0
102432.01.01.00.032.01024.032.0
102532.01.01.00.032.01024.032.0
3276632.031.031.00.032.01024.032.0
3276732.031.031.00.032.01024.032.0
3276832.032.00.01.032.01024.032.0
3276932.032.00.01.032.01024.032.0
99999632.0976.016.030.032.01024.032.0
99999732.0976.016.030.032.01024.032.0
99999832.0976.016.030.032.01024.032.0
99999932.0976.016.030.032.01024.032.0

thread_execution_widthからは、32が出力されました。
これは、Threadのwidthです。

thread_position_in_gridからは、1024ごとにインクリメントされた値が出力されました。
GridのWidth(Threadのwidth(32) * ThreadGroupのwidth(32) = 1024)ごとにインクリメントされるためです。

thread_position_in_threadgroupからは、32ごとにインクリメントされた値が出力されました。
ThreadGroupのWidth(32)ごとにインクリメントされるためです。

threadgroup_position_in_gridからは、32768ごとにインクリメントされた値が出力されました。
GridのWidth(1024) * ThreadGroupのheight(32) = 32768ごとにインクリメントされるためです。

threadgroups_per_gridからは、32が出力されました。
これは、dispatchThreadgroupsで設定した、threadgroupsPerGridのheightです。

threads_per_gridからは、1024が出力されました。
これは、dispatchThreadgroupsで設定した、threadgroupsPerGridのheight(32)と、threadsPerThreadgroupのheight(32)をかけた値です。

threads_per_threadgroupからは、32が出力されました。
これは、dispatchThreadgroupsで設定した、threadsPerThreadgroupのheightです。

3Dの場合

続いて、3D(width * height * depth)の実装の場合の値についてです。

  • ViewController
  • Shader.metal
import UIKit
import Metal

class ViewController: UIViewController {

    private let device = MTLCreateSystemDefaultDevice()!
    private var library: MTLLibrary!
    private var commandQueue: MTLCommandQueue!
    private var computePipelineState: MTLComputePipelineState!

    private let xCount = 100
    private let yCount = 100
    private let zCount = 100
    private var outputDataCount = 0

    let attribute = ["thread_execution_width", "thread_position_in_grid", "thread_position_in_threadgroup", "threadgroup_position_in_grid", "threadgroups_per_grid", "threads_per_grid", "threads_per_threadgroup"];

    override func viewDidLoad() {
        super.viewDidLoad()

        outputDataCount = xCount * yCount * zCount

        initMetal()

        startGPU()
    }

    private func initMetal() {
        guard let libUrl = Bundle.main.url(forResource: "default.metallib", withExtension: nil) else { return }
        library = try! device.makeLibrary(URL: libUrl)

        let function = library.makeFunction(name: "sample")!
        computePipelineState = try! device.makeComputePipelineState(function: function)

        commandQueue = device.makeCommandQueue()!
    }

    private func startGPU() {
        var outputDataList: [[Float]] = []
        for _ in 0..<attribute.count {
            outputDataList.append([Float](repeating: 1, count: outputDataCount))
        }

        var outputBufferList: [MTLBuffer] = []
        for i in 0..<attribute.count {
            outputBufferList.append(device.makeBuffer(bytes: outputDataList[i], length: MemoryLayout<Float>.stride * outputDataList[i].count, options: [])!)
        }

        let commandBuffer = commandQueue.makeCommandBuffer()!
        let computeCommandEncoder = commandBuffer.makeComputeCommandEncoder()!
        computeCommandEncoder.setComputePipelineState(computePipelineState)

        for i in 0..<attribute.count {
            computeCommandEncoder.setBuffer(outputBufferList[i], offset: 0, index: i)
        }
        computeCommandEncoder.setBytes(&outputDataCount, length: MemoryLayout<Float>.size, index: attribute.count)

        let max    = computePipelineState.maxTotalThreadsPerThreadgroup
        let depth  = 4
        let width  = computePipelineState.threadExecutionWidth
        let height = max / width / depth
        let threadgroupsPerGrid = MTLSize(width: (xCount + width - 1) / width, height: (yCount + height - 1) / height, depth: (zCount + depth - 1) / depth)
        let threadsPerThreadgroup = MTLSize(width: width, height: height, depth: depth)
        print("threadgroupsPerGrid  : \(threadgroupsPerGrid)")
        print("threadsPerThreadgroup: \(threadsPerThreadgroup)")
        computeCommandEncoder.dispatchThreadgroups(threadgroupsPerGrid, threadsPerThreadgroup: threadsPerThreadgroup)

        computeCommandEncoder.endEncoding()
        commandBuffer.commit()
        commandBuffer.waitUntilCompleted()

        var resultDataList: [[Float]] = []
        for i in 0..<attribute.count {
            let result = Data(bytesNoCopy: outputBufferList[i].contents(), count: MemoryLayout<Float>.stride * outputDataList[i].count, deallocator: .none)
            resultDataList.append(
                result.withUnsafeBytes {
                    Array(UnsafeBufferPointer(start: $0.baseAddress!.assumingMemoryBound(to: Float.self ), count: $0.count / MemoryLayout<Float>.size))
                }
            )
        }

        for i in 0..<outputDataCount {
            var str = "index: \(i) ||"
            for j in 0..<attribute.count {
                str.append(" \(attribute[j]): \(resultDataList[j][i]), ")
            }
            print(str)
        }
    }
}
#include <metal_stdlib>
using namespace metal;

kernel void sample(device float* outputData0    [[ buffer(0) ]],
                   device float* outputData1    [[ buffer(1) ]],
                   device float* outputData2    [[ buffer(2) ]],
                   device float* outputData3    [[ buffer(3) ]],
                   device float* outputData4    [[ buffer(4) ]],
                   device float* outputData5    [[ buffer(5) ]],
                   device float* outputData6    [[ buffer(6) ]],
                   const device int& dataLength [[ buffer(7) ]],

                   uint  thread_execution_width         [[thread_execution_width]],         // ThreadGroupの横幅
                   uint3 thread_position_in_grid        [[thread_position_in_grid]],        // GridにおけるThreadの位置
                   uint3 thread_position_in_threadgroup [[thread_position_in_threadgroup]], // ThreadGroupにおけるThreadの位置
                   uint3 threadgroup_position_in_grid   [[threadgroup_position_in_grid]],   // GridにおけるThreadGroupの位置
                   uint3 threadgroups_per_grid          [[threadgroups_per_grid]],          // Grid毎のThreadGroup数
                   uint3 threads_per_grid               [[threads_per_grid]],               // Grid毎のThread数
                   uint3 threads_per_threadgroup        [[threads_per_threadgroup]])        // ThreadGroup毎のThread数
{
    int index = thread_position_in_grid.y * threads_per_grid.x + thread_position_in_grid.x + (thread_position_in_grid.z * threads_per_grid.y * threads_per_grid.x);

    if (index > dataLength) { return; }
    outputData0[index] = thread_execution_width;
    outputData1[index] = thread_position_in_grid.x;
    outputData2[index] = thread_position_in_threadgroup.x;
    outputData3[index] = threadgroup_position_in_grid.x;
    outputData4[index] = threadgroups_per_grid.x;
    outputData5[index] = threads_per_grid.x;
    outputData6[index] = threads_per_threadgroup.x;
		// Y値、Z値の出力結果表示する際は、.xの部分を .y、.zに置き換えております。
}

【スレッドとスレッドグループの値】

threadgroupsPerGrid : MTLSize(width: 4, height: 13, depth: 25)
threadsPerThreadgroup: MTLSize(width: 32, height: 8, depth: 4)

【出力結果(重要な部分のみピックアップ)】

・X値

indexthread
execution
width
thread
position
in
grid
thread
position
in
threadgroup
threadgroup
position
in
grid
threadgroups
per
grid
threads
per
grid
threads
per
threadgroup
032.00.00.00.04.0128.032.0
132.01.01.00.04.0128.032.0
232.02.02.00.04.0128.032.0
332.03.03.00.04.0128.032.0
3032.030.030.00.04.0128.032.0
3132.031.031.00.04.0128.032.0
3232.032.00.01.04.0128.032.0
3332.033.01.01.04.0128.032.0
12632.0126.030.03.04.0128.032.0
12732.0127.031.03.04.0128.032.0
12832.00.00.00.04.0128.032.0
12932.01.01.00.04.0128.032.0
99999632.060.028.01.04.0128.032.0
99999732.061.029.01.04.0128.032.0
99999832.062.030.01.04.0128.032.0
99999932.063.031.01.04.0128.032.0

thread_execution_widthからは、32が出力されました。
これは、Threadのwidthです。

thread_position_in_gridからは、0~127が出力されました。
GridのWidthが、Threadのwidth(32) * ThreadGroupのwidth(4) = 128 であるため、そのWidth間のThreadは0~128となるためです。

thread_position_in_threadgroupからは、0~31が繰り返し出力されました。
ThreadGroupのWidthが32であるため、そのWidth間のThreadは0~31となるためです。

threadgroup_position_in_gridからは、32ごとにインクリメントされた値が出力されました。
ThreadGroupのWidthが32であるため、32ごとにインクリメントされます。なお、GridのWidthが128であるため、128ごとに0に戻ります。

threadgroups_per_gridからは、4が出力されました。
これは、dispatchThreadgroupsで設定した、threadgroupsPerGridのwidthです。

threads_per_gridからは、128が出力されました。
これは、dispatchThreadgroupsで設定した、threadgroupsPerGridのwidth(4)と、threadsPerThreadgroupのwidth(32)をかけた値です。

threads_per_threadgroupからは、32が出力されました。
これは、dispatchThreadgroupsで設定した、threadsPerThreadgroupのwidthです。

・Y値

indexthread
execution
width
thread
position
in
grid
thread
position
in
threadgroup
threadgroup
position
in
grid
threadgroups
per
grid
threads
per
grid
threads
per
threadgroup
032.00.00.00.013.0104.08.0
132.00.00.00.013.0104.08.0
232.00.00.00.013.0104.08.0
332.00.00.00.013.0104.08.0
12632.00.00.00.013.0104.08.0
12732.00.00.00.013.0104.08.0
12832.01.01.00.013.0104.08.0
12932.01.01.00.013.0104.08.0
102232.07.07.00.013.0104.08.0
102332.07.07.00.013.0104.08.0
102432.08.00.01.013.0104.08.0
102532.08.00.01.013.0104.08.0
1331032.0103.07.012.013.0104.08.0
1331132.0103.07.012.013.0104.08.0
1331232.00.00.00.013.0104.08.0
1331332.00.00.00.013.0104.08.0
99999632.012.04.01.013.0104.08.0
99999732.012.04.01.013.0104.08.0
99999832.012.04.01.013.0104.08.0
99999932.012.04.01.013.0104.08.0

thread_execution_widthからは、32が出力されました。
これは、Threadのwidthです。

thread_position_in_gridからは、128ごとにインクリメントされた値が出力されました。
GridのWidth(Threadのwidth(32) * ThreadGroupのwidth(4) = 128)ごとにインクリメントされるためです。
なお、depthが進むタイミング(13312)ごとに、0に戻ります。
※ GridのWidth(32 * 4) * GridのHeight(8 * 13) = depthが進むタイミング(13312)

thread_position_in_threadgroupからは、128ごとにインクリメントされた値が出力されました。
ThreadGroupのWidth(32) * ThreadGroupのDepth(4) = 128ごとにインクリメントされるためです。
なお、ThreadGroupが切り替わるタイミング(32 * 8 * 4 = 1024)ごとに、0に戻ります。

threadgroup_position_in_gridからは、1024ごとにインクリメントされた値が出力されました。
GridのWidth(128) * ThreadGroupのheight(8) = 1024ごとにインクリメントされるためです。

threadgroups_per_gridからは、13が出力されました。
これは、dispatchThreadgroupsで設定した、threadgroupsPerGridのheightです。

threads_per_gridからは、104が出力されました。
これは、dispatchThreadgroupsで設定した、threadgroupsPerGridのheight(13)と、threadsPerThreadgroupのheight(8)をかけた値です。

threads_per_threadgroupからは、8が出力されました。
これは、dispatchThreadgroupsで設定した、threadsPerThreadgroupのheightです。

・Z値

indexthread
execution
width
thread
position
in
grid
thread
position
in
threadgroup
threadgroup
position
in
grid
threadgroups
per
grid
threads
per
grid
threads
per
threadgroup
032.00.00.00.025.0100.04.0
132.00.00.00.025.0100.04.0
232.00.00.00.025.0100.04.0
332.00.00.00.025.0100.04.0
1331032.00.00.00.025.0100.04.0
1331132.00.00.00.025.0100.04.0
1331232.01.01.00.025.0100.04.0
1331332.01.01.00.025.0100.04.0
5324632.03.03.00.025.0100.04.0
5324732.03.03.00.025.0100.04.0
5324832.04.00.01.025.0100.04.0
5324932.04.00.01.025.0100.04.0
99999632.075.03.018.025.0100.04.0
99999732.075.03.018.025.0100.04.0
99999832.075.03.018.025.0100.04.0
99999932.075.03.018.025.0100.04.0

thread_execution_widthからは、32が出力されました。
これは、Threadのwidthです。

thread_position_in_gridからは、13312ごとにインクリメントされた値が出力されました。
GridのWidth(4 * 32) * GridのHeight(13 * 8) = 13312 ごとにインクリメントされるためです。

thread_position_in_threadgroupからは、32ごとにインクリメントされた値が出力されました。
ThreadGroupのWidth(32)ごとにインクリメントされるためです。

threadgroup_position_in_gridからは、53248ごとにインクリメントされた値が出力されました。
GridのWidth(4 * 32) * GridのHeight(13 * 8) * ThreadGroupのDepth(4) = 53248ごとにインクリメントされるためです。

threadgroups_per_gridからは、25が出力されました。
これは、dispatchThreadgroupsで設定した、threadgroupsPerGridのdepthです。

threads_per_gridからは、100が出力されました。
これは、dispatchThreadgroupsで設定した、threadgroupsPerGridのdepth(25)と、threadsPerThreadgroupのdepth(4)をかけた値です。

threads_per_threadgroupからは、4が出力されました。
これは、dispatchThreadgroupsで設定した、threadsPerThreadgroupのdepthです。

まとめ

  • thread_execution_widthは、ThreadGoupの横幅
  • thread_position_in_gridは、GridにおけるThreadの位置
  • thread_position_in_threadgroupは、ThreadGroupにおけるThreadの位置
  • threadgroup_position_in_gridは、GridにおけるThreadGroupの位置
  • threadgroups_per_gridは、Grid毎のThreadGroup数
  • threads_per_gridは、Grid毎のThread数
  • threads_per_threadgroupは、ThreadGroup毎のThread数