Project
Loading...
Searching...
No Matches
GPUReconstructionMetalIncludesHost.h
Go to the documentation of this file.
1// Copyright 2019-2025 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
12#ifndef GPURECONSTRUCTIONOMETALINCLUDESHOST_H
13#define GPURECONSTRUCTIONOMETALINCLUDESHOST_H
14
15#ifdef GPUCOMMONDEF_H
16#error "this header establishes GPUCA_GPUTYPE_METAL and __METAL_HOST__, so it must be included before any other GPU header"
17#endif
18
19#define GPUCA_GPUTYPE_METAL
20
21#import <Metal/Metal.h>
22#ifndef __METAL_VERSION__
23// __METAL_VERSION__ is only defined in device code.
24#define __METAL_HOST__
25// The parameter table picks MSL's `half` for the device layouts this translation
26// unit has to agree with; _Float16 is the same IEEE binary16 type.
27using half = _Float16;
28#endif
29
30#import <Foundation/Foundation.h>
31
32#include <vector>
33#include <string>
34#include <memory>
35#include <mutex>
36#include <condition_variable>
37#include <unordered_map>
38#include <cstdio>
39#include <mach/mach_time.h>
40#include "GPULogging.h"
41
44#include "GPUCommonHelpers.h"
45
46using namespace o2::gpu;
47
48#include <cstring>
49#include <unistd.h>
50#include <typeinfo>
51#include <cstdlib>
52
53namespace o2::gpu
54{
55
58
59 std::array<id<MTLCommandQueue>, constants::GPU_MAX_STREAMS> commandQueues;
60 // The buffer currently open for encoding, plus the ones already committed and
61 // not yet waited for. A Metal command buffer takes one commit and cannot be
62 // encoded into afterwards, so the two cannot be the same object.
63 std::array<id<MTLCommandBuffer>, constants::GPU_MAX_STREAMS> commandBuffers;
64 std::array<std::vector<id<MTLCommandBuffer>>, constants::GPU_MAX_STREAMS> submittedBuffers;
65 // Signalled by each committed buffer and waited for by the next one, see CommitStream
66 std::array<id<MTLSharedEvent>, constants::GPU_MAX_STREAMS> streamOrder;
67 std::array<uint64_t, constants::GPU_MAX_STREAMS> streamOrderValue;
68 // Orders the encoders within the open command buffer of a stream, which
69 // collects the stream's work until something has to wait for it.
70 std::array<id<MTLFence>, constants::GPU_MAX_STREAMS> streamFence;
71 std::array<bool, constants::GPU_MAX_STREAMS> streamHasWork;
72 // Set while the device is torn down, when a failed command buffer is no longer fatal
73 bool exiting = false;
74 // Opt-in command-buffer timeline, GPUCA_METAL_TIMELINE=<file>: what each open
75 // buffer contains (kernels, blits, waits, signals), written out once it completes
76 FILE* timeline = nullptr;
77 std::array<std::string, constants::GPU_MAX_STREAMS> timelineOps;
78 std::array<uint32_t, constants::GPU_MAX_STREAMS> timelineKernels;
80 // Opt-in limits on the GPU work in flight, 0 = none, so that no stretch of GPU
81 // work runs long enough for the macOS GPU watchdog: GPUCA_METAL_MAX_KERNELS_PER_BUFFER
82 // commits a stream's buffer after that many kernels, GPUCA_METAL_MAX_STREAMS_IN_FLIGHT
83 // is how many streams may have committed buffers that are not complete yet.
86 std::array<uint32_t, constants::GPU_MAX_STREAMS> streamKernels{};
87 std::array<uint32_t, constants::GPU_MAX_STREAMS> streamBuffersInFlight{};
89 std::mutex inFlightMutex;
90 std::condition_variable inFlightDone;
91
92 std::vector<id<MTLFunction>> functions;
93 std::vector<id<MTLComputePipelineState>> pipelines;
94 std::vector<uint32_t> maxThreadsPerGroup;
95
99 // Opt-in, GPUCA_METAL_CANARY=1: a kernel that reads a word past the end of the
100 // device pool, where the host put canaryMagic, and counts the reads that return
101 // something else. canaryBad is nil when it is off.
102 static constexpr uint32_t canaryMagic = 0xA11CE0A5;
105
107
108 // The value each event was last recorded with. An MTLSharedEvent only counts
109 // up, so recording signals the next value and waiting asks for the last one,
110 // which makes waiting for a never recorded event a no-op, as in CUDA.
111 std::unordered_map<void*, uint64_t> eventValues;
112 std::mutex eventMutex;
113};
114// An encoder of a stream starts after the previous one of the same stream and
115// leaves its work in the open command buffer.
116template <class E>
117inline void metalBeginEncoder(GPUReconstructionMetalInternals* internals, E encoder, int32_t stream)
118{
119 [encoder waitForFence:internals->streamFence[stream]];
120}
121// A failed command buffer does not signal its events, and the work waiting for
122// them would wait forever: signal them from the host, so that everything
123// completes and the failure is reported when the host synchronises.
125{
126 [cb addCompletedHandler:^(id<MTLCommandBuffer> c) {
127 if (c.status == MTLCommandBufferStatusError && event.signaledValue < value) {
128 event.signaledValue = value;
129 }
130 }];
131}
132// Before a buffer of an idle stream is committed, wait until fewer than the
133// limit of streams have work in flight. This cannot deadlock: a buffer only waits
134// for events that buffers committed before it signal (RecordMarker commits the
135// signal at once), so the work in flight completes without the host's help.
137{
138 std::unique_lock<std::mutex> lock(internals->inFlightMutex);
139 if (internals->streamBuffersInFlight[stream]++ == 0) {
140 internals->inFlightDone.wait(lock, [internals] { return internals->streamsInFlight < internals->maxStreamsInFlight; });
141 internals->streamsInFlight++;
142 }
143 [cb addCompletedHandler:^(id<MTLCommandBuffer>) {
144 std::lock_guard<std::mutex> l(internals->inFlightMutex);
145 if (--internals->streamBuffersInFlight[stream] == 0) {
146 internals->streamsInFlight--;
147 internals->inFlightDone.notify_all();
148 }
149 }];
150}
151// In the time base of the command buffers' GPUStartTime and GPUEndTime
152inline double metalHostTime()
153{
154 static const double scale = [] { mach_timebase_info_data_t tb; mach_timebase_info(&tb); return 1e-9 * tb.numer / tb.denom; }();
155 return mach_absolute_time() * scale;
156}
157inline void metalTimelineAdd(GPUReconstructionMetalInternals* internals, int32_t stream, const char* op, const void* id, uint64_t value)
158{
159 if (internals->timeline) {
160 char buf[96];
161 snprintf(buf, sizeof(buf), " %s:%p=%llu", op, id, (unsigned long long)value);
162 internals->timelineOps[stream] += buf;
163 }
164}
165// Runs after each kernel in its encoder, so a kernel that ran while the pool
166// was not mapped for the GPU is counted and the run fails instead of going on
167// with the results it did not produce.
169{
170 [encoder setComputePipelineState:internals->canaryPipeline];
171 [encoder setBuffer:internals->canaryBad offset:0 atIndex:0];
172 [encoder setBytes:&word length:sizeof(word) atIndex:1];
173 [encoder dispatchThreadgroups:MTLSizeMake(1, 1, 1) threadsPerThreadgroup:MTLSizeMake(1, 1, 1)];
174}
175template <class E>
176inline void metalEndEncoder(GPUReconstructionMetalInternals* internals, E encoder, int32_t stream)
177{
178 [encoder updateFence:internals->streamFence[stream]];
179 [encoder endEncoding];
180 internals->streamHasWork[stream] = true;
181}
182} // namespace o2::gpu
183
184#endif // GPURECONSTRUCTIONOMETALINCLUDESHOST_H
unsigned int uint32_t
uint32_t op
Timeframe settings Average distance in cm of events merged into Randomize distance around average distance of merged events Simulate correct bunch interactions instead of placing only the average number of events A value[n] sets TFlen for[n] collisions in Number of bunches per trainsort Number of bunch trains Instantaneous interaction rate Do not simulate border Total number of collisions to be placed in the interior of all time Do not select random but walk over array of events in stride steps h Event generator settings h configStandalone Use CPU for GPU Force usage of the specified GPU device no CPU fallback Continue processing after an error First event to Number of events to Number of iterations to Number of iterations to Number of initial iterations excluded from const char Directory with events to Events directory is and not inside events Show standalone event Run event generator maximum time bin of continuous for triggered for automatic continuous for automatic continuous triggered uint64_t
Timeframe settings Average distance in cm of events merged into Randomize distance around average distance of merged events Simulate correct bunch interactions instead of placing only the average number of events A value[n] sets TFlen for[n] collisions in Number of bunches per trainsort Number of bunch trains Instantaneous interaction rate Do not simulate border Total number of collisions to be placed in the interior of all time Do not select random but walk over array of events in stride steps h Event generator settings h configStandalone Use CPU for GPU Force usage of the specified GPU device no CPU fallback Continue processing after an error First event to Number of events to Number of iterations to Number of iterations to Number of initial iterations excluded from const char e
uint32_t c
Definition RawData.h:2
struct _cl_event * event
Definition glcorearb.h:2982
GLsizei const GLfloat * value
Definition glcorearb.h:819
GLintptr offset
Definition glcorearb.h:660
GLuint GLsizei GLsizei * length
Definition glcorearb.h:790
GLuint GLuint stream
Definition glcorearb.h:1806
GLenum GLuint GLenum GLsizei const GLchar * buf
Definition glcorearb.h:2514
void metalLimitStreamsInFlight(GPUReconstructionMetalInternals *internals, id< MTLCommandBuffer > cb, int32_t stream)
void metalEndEncoder(GPUReconstructionMetalInternals *internals, E encoder, int32_t stream)
void metalTimelineAdd(GPUReconstructionMetalInternals *internals, int32_t stream, const char *op, const void *id, uint64_t value)
void metalEncodeCanary(GPUReconstructionMetalInternals *internals, id< MTLComputeCommandEncoder > encoder, uint64_t word)
void metalBeginEncoder(GPUReconstructionMetalInternals *internals, E encoder, int32_t stream)
void metalSignalOnError(id< MTLCommandBuffer > cb, id< MTLSharedEvent > event, uint64_t value)
std::array< uint32_t, constants::GPU_MAX_STREAMS > streamBuffersInFlight
std::array< id< MTLCommandQueue >, constants::GPU_MAX_STREAMS > commandQueues
std::array< std::vector< id< MTLCommandBuffer > >, constants::GPU_MAX_STREAMS > submittedBuffers
std::array< uint32_t, constants::GPU_MAX_STREAMS > streamKernels
std::array< uint64_t, constants::GPU_MAX_STREAMS > streamOrderValue
std::array< id< MTLCommandBuffer >, constants::GPU_MAX_STREAMS > commandBuffers
std::array< std::string, constants::GPU_MAX_STREAMS > timelineOps
std::array< bool, constants::GPU_MAX_STREAMS > streamHasWork
std::array< uint32_t, constants::GPU_MAX_STREAMS > timelineKernels
std::array< id< MTLFence >, constants::GPU_MAX_STREAMS > streamFence
std::array< id< MTLSharedEvent >, constants::GPU_MAX_STREAMS > streamOrder
std::vector< id< MTLComputePipelineState > > pipelines