1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
/*******************************************************************************
* Copyright 2017 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/
#include <memory>
#include "engine.hpp"
#include "utils.hpp"
#if DNNL_CPU_RUNTIME != DNNL_RUNTIME_NONE
#include "cpu/cpu_engine.hpp"
#endif
#include "scratchpad.hpp"
namespace dnnl {
namespace impl {
namespace {
memory_storage_t *create_scratchpad_memory_storage(
engine_t *engine, size_t size) {
// XXX: if engine is a non-native CPU engine (read: SYCL) then create
// scratchpad through other, native CPU engine.
//
// SYCL CPU engine has asynchronous execution, and the library has to
// extend (if needed) primitive lifetime until a kernel is completed.
// For that, the library implements a reference-counting mechanism for
// primitives (including internal scratchpads). In some cases a
// scratchpad has to be destroyed from inside a kernel. This doesn't
// play well with SYCL runtime, so switching to native CPU engine for such
// cases.
engine_t *mem_engine = nullptr;
#if DNNL_CPU_RUNTIME != DNNL_RUNTIME_NONE
mem_engine = (engine->kind() == engine_kind::cpu
&& !is_native_runtime(engine->runtime_kind()))
? cpu::get_service_engine()
: engine;
#else
mem_engine = engine;
#endif
memory_storage_t *mem_storage = nullptr;
auto status = mem_engine->create_memory_storage(&mem_storage, size);
MAYBE_UNUSED(status);
return mem_storage;
}
} // namespace
/*
Implementation of the scratchpad_t interface that is compatible with
a concurrent execution
*/
struct concurrent_scratchpad_t : public scratchpad_t {
concurrent_scratchpad_t(engine_t *engine, size_t size) : size_(size) {
auto *mem_storage = create_scratchpad_memory_storage(engine, size);
if (mem_storage == nullptr) size_ = 0;
mem_storage_.reset(mem_storage);
}
const memory_storage_t *get_memory_storage() const override {
return mem_storage_.get();
}
size_t size() const override { return size_; }
private:
std::unique_ptr<memory_storage_t> mem_storage_;
size_t size_;
DNNL_DISALLOW_COPY_AND_ASSIGN(concurrent_scratchpad_t);
};
/*
Implementation of the scratchpad_t interface that uses a global
scratchpad
*/
struct global_scratchpad_t : public scratchpad_t {
global_scratchpad_t(engine_t *engine, size_t size) {
// TODO: check if engine is the same
if (size > size_) {
delete mem_storage_;
// Try to expand the global scratchpad to the necessary size
mem_storage_ = create_scratchpad_memory_storage(engine, size);
if (mem_storage_ == nullptr) {
// Recreate scratchpad with original capacity
mem_storage_ = create_scratchpad_memory_storage(engine, size_);
if (mem_storage_ == nullptr) size_ = 0;
} else
size_ = size;
}
reference_count_++;
}
~global_scratchpad_t() override {
reference_count_--;
if (reference_count_ == 0) {
delete mem_storage_;
mem_storage_ = nullptr;
size_ = 0;
}
}
const memory_storage_t *get_memory_storage() const override {
return mem_storage_;
}
size_t size() const override { return size_; }
private:
DNNL_DISALLOW_COPY_AND_ASSIGN(global_scratchpad_t);
thread_local static memory_storage_t *mem_storage_;
thread_local static size_t size_;
thread_local static unsigned int reference_count_;
};
// CAVEAT: avoid having non-trivially-constructed thread-local objects. Their
// construction order may depends on the program execution and the final
// destruction order may be such that a thread-local object is destroyed
// before all its users are destroyed thus causing a crash at exit.
// Tested by tests/gtests/test_global_scratchad.cpp
thread_local memory_storage_t *global_scratchpad_t::mem_storage_ = nullptr;
thread_local size_t global_scratchpad_t::size_ = 0;
thread_local unsigned int global_scratchpad_t::reference_count_ = 0;
/*
Scratchpad creation routine
*/
scratchpad_t *create_scratchpad(
engine_t *engine, size_t size, bool use_global_scratchpad) {
#ifndef DNNL_ENABLE_CONCURRENT_EXEC
/*
* TODO: global scratchpad should be able to handle memory
* from different engines.
* lock global scratchpad to work with CPU engine only.
*/
if (use_global_scratchpad && engine->kind() == engine_kind_t::dnnl_cpu)
return new global_scratchpad_t(engine, size);
else
return new concurrent_scratchpad_t(engine, size);
#else
UNUSED(use_global_scratchpad);
return new concurrent_scratchpad_t(engine, size);
#endif
}
} // namespace impl
} // namespace dnnl