Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
PaddlePaddle
Paddle
提交
0a5fbb06
P
Paddle
项目概览
PaddlePaddle
/
Paddle
大约 1 年 前同步成功
通知
2299
Star
20931
Fork
5422
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
1423
列表
看板
标记
里程碑
合并请求
543
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
P
Paddle
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
1,423
Issue
1,423
列表
看板
标记
里程碑
合并请求
543
合并请求
543
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
提交
0a5fbb06
编写于
12月 29, 2017
作者:
D
dangqingqing
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Refine code struct.
上级
f03e73c8
变更
4
隐藏空白更改
内联
并排
Showing
4 changed file
with
154 addition
and
150 deletion
+154
-150
paddle/platform/device_context.h
paddle/platform/device_context.h
+0
-12
paddle/platform/profiler.cc
paddle/platform/profiler.cc
+124
-25
paddle/platform/profiler.h
paddle/platform/profiler.h
+24
-107
paddle/platform/profiler_test.cc
paddle/platform/profiler_test.cc
+6
-6
未找到文件。
paddle/platform/device_context.h
浏览文件 @
0a5fbb06
...
@@ -115,18 +115,6 @@ class CUDNNDeviceContext : public CUDADeviceContext {
...
@@ -115,18 +115,6 @@ class CUDNNDeviceContext : public CUDADeviceContext {
cudnnHandle_t
cudnn_handle_
;
cudnnHandle_t
cudnn_handle_
;
};
};
class
DeviceGuard
{
public:
explicit
DeviceGuard
(
int
device
)
{
original_device_
=
platform
::
GetCurrentDeviceId
();
platform
::
SetDeviceId
(
device
);
}
~
DeviceGuard
()
{
platform
::
SetDeviceId
(
original_device_
);
}
private:
int
original_device_
;
};
#endif
#endif
/*! \brief device context pool singleton */
/*! \brief device context pool singleton */
...
...
paddle/platform/profiler.cc
浏览文件 @
0a5fbb06
...
@@ -17,34 +17,133 @@ limitations under the License. */
...
@@ -17,34 +17,133 @@ limitations under the License. */
namespace
paddle
{
namespace
paddle
{
namespace
platform
{
namespace
platform
{
ProfilerState
kState
=
ProfilerState
::
kDisabled
;
// The profiler state, the initial value is ProfilerState::kDisabled
uint32_t
kNextThreadId
=
0
;
static
ProfilerState
g_state
=
ProfilerState
::
kDisabled
;
std
::
mutex
kAllEventListsMutex
;
// The thread local event list only can be accessed by the specific thread
std
::
list
<
std
::
shared_ptr
<
EventList
>>
kAllEventLists
;
// The thread index of each thread
thread_local
std
::
shared_ptr
<
EventList
>
kEventList
;
static
thread_local
int32_t
g_thread_id
;
thread_local
int32_t
kThreadId
;
// The g_next_thread_id is a global counter for threads, by the g_thread_id and
// g_next_thread_id, we can know how many threads have created EventList.
static
uint32_t
g_next_thread_id
=
0
;
// The global mutex
static
std
::
mutex
g_all_event_lists_mutex
;
// The total event lists of all threads
static
std
::
list
<
std
::
shared_ptr
<
EventList
>>
g_all_event_lists
;
// The thread local event list only can be accessed by the specific thread
static
thread_local
std
::
shared_ptr
<
EventList
>
g_event_list
;
inline
uint64_t
GetTimeInNsec
()
{
using
clock
=
std
::
conditional
<
std
::
chrono
::
high_resolution_clock
::
is_steady
,
std
::
chrono
::
high_resolution_clock
,
std
::
chrono
::
steady_clock
>::
type
;
return
std
::
chrono
::
duration_cast
<
std
::
chrono
::
nanoseconds
>
(
clock
::
now
().
time_since_epoch
())
.
count
();
}
Event
::
Event
(
EventKind
kind
,
std
::
string
name
,
uint32_t
thread_id
,
DeviceContext
*
dev_ctx
)
:
kind_
(
kind
),
name_
(
std
::
move
(
name
)),
thread_id_
(
thread_id
),
has_cuda_
(
false
)
{
#ifdef PADDLE_WITH_CUDA
auto
*
cuda_dev_ctx
=
static_cast
<
const
CUDADeviceContext
*>
(
dev_ctx
);
if
(
cuda_dev_ctx
)
{
PADDLE_ENFORCE
(
cudaGetDevice
(
&
device_
));
PADDLE_ENFORCE
(
cudaEventCreate
(
&
event_
));
auto
stream
=
cuda_dev_ctx
->
stream
();
PADDLE_ENFORCE
(
cudaEventRecord
(
event_
,
stream
));
has_cuda_
=
true
;
}
#endif
cpu_ns_
=
GetTimeInNsec
();
}
std
::
string
Event
::
kind
()
const
{
switch
(
kind_
)
{
case
EventKind
::
kMark
:
return
"mark"
;
case
EventKind
::
kPushRange
:
return
"push"
;
case
EventKind
::
kPopRange
:
return
"pop"
;
}
PADDLE_THROW
(
"Unknown EventKind."
);
}
double
Event
::
CpuElapsedUs
(
const
Event
&
e
)
const
{
return
(
e
.
cpu_ns_
-
cpu_ns_
)
/
(
1000.0
);
}
double
Event
::
CudaElapsedUs
(
const
Event
&
e
)
const
{
#ifdef PADDLE_WITH_CUDA
PADDLE_ENFORCE
(
e
.
has_cuda
()
&&
has_cuda
());
PADDLE_ENFORCE
(
e
.
device
()
==
device
());
PADDLE_ENFORCE
(
cudaEventSynchronize
(
event_
));
PADDLE_ENFORCE
(
cudaEventSynchronize
(
e
.
event
()));
float
ms
;
PADDLE_ENFORCE
(
cudaEventElapsedTime
(
&
ms
,
event_
,
e
.
event
()));
return
ms
*
1000.0
;
#else
PADDLE_THROW
(
"CUDA is not enabled"
);
#endif
}
#ifdef PADDLE_WITH_CUDA
static
void
ForEachDevice
(
std
::
function
<
void
(
int
)
>
func
)
{
auto
original_device
=
GetCurrentDeviceId
();
int
count
=
GetCUDADeviceCount
();
for
(
int
i
=
0
;
i
<
count
;
i
++
)
{
SetDeviceId
(
i
);
func
(
i
);
}
SetDeviceId
(
original_device
);
}
#endif
inline
EventList
&
GetEventList
()
{
if
(
!
g_event_list
)
{
std
::
lock_guard
<
std
::
mutex
>
guard
(
g_all_event_lists_mutex
);
g_event_list
=
std
::
make_shared
<
EventList
>
();
g_thread_id
=
g_next_thread_id
++
;
g_all_event_lists
.
emplace_front
(
g_event_list
);
}
return
*
g_event_list
;
}
void
Mark
(
const
std
::
string
&
name
,
DeviceContext
*
dev_ctx
)
{
GetEventList
().
Record
(
EventKind
::
kMark
,
std
::
move
(
name
),
g_thread_id
,
dev_ctx
);
}
RecordEvent
::
RecordEvent
(
const
std
::
string
&
name
,
DeviceContext
*
dev_ctx
)
{
if
(
g_state
==
ProfilerState
::
kDisabled
)
return
;
dev_ctx_
=
dev_ctx
;
GetEventList
().
Record
(
EventKind
::
kPushRange
,
std
::
move
(
name
),
g_thread_id
,
dev_ctx_
);
}
RecordEvent
::~
RecordEvent
()
{
if
(
g_state
==
ProfilerState
::
kDisabled
)
return
;
GetEventList
().
Record
(
EventKind
::
kPopRange
,
std
::
string
(),
g_thread_id
,
dev_ctx_
);
}
void
EnableProfiler
(
ProfilerState
state
)
{
void
EnableProfiler
(
ProfilerState
state
)
{
PADDLE_ENFORCE
(
state
!=
ProfilerState
::
kDisabled
,
PADDLE_ENFORCE
(
state
!=
ProfilerState
::
kDisabled
,
"Can't enbale profling, since the input state is "
,
"Can't enbale profling, since the input state is "
,
"ProfilerState::kDisabled"
);
"ProfilerState::kDisabled"
);
PADDLE_ENFORCE
(
kS
tate
==
ProfilerState
::
kDisabled
,
PADDLE_ENFORCE
(
g_s
tate
==
ProfilerState
::
kDisabled
,
"The profiling state should be disabled when calling "
,
"The profiling state should be disabled when calling "
,
"EnableProfiler."
);
"EnableProfiler."
);
kS
tate
=
state
;
g_s
tate
=
state
;
#ifdef PADDLE_WITH_CUDA
#ifdef PADDLE_WITH_CUDA
auto
ForEachDevice
=
[](
std
::
function
<
void
(
int
)
>
op
)
{
if
(
g_state
==
ProfilerState
::
kCUDA
)
{
int
count
=
GetCUDADeviceCount
();
for
(
int
i
=
0
;
i
<
count
;
i
++
)
{
DeviceGuard
dev_guard
(
i
);
op
(
i
);
}
};
if
(
kState
==
ProfilerState
::
kCUDA
)
{
// Generate some dummy evenets first to reduce the startup overhead.
// Generate some dummy evenets first to reduce the startup overhead.
for
(
int
i
=
0
;
i
<
5
;
i
++
)
{
for
(
int
i
=
0
;
i
<
5
;
i
++
)
{
ForEachDevice
([](
int
d
)
{
ForEachDevice
([](
int
d
)
{
DeviceContext
*
dev_ctx
=
new
CUDADeviceContext
(
GPU
Place
(
d
));
DeviceContext
*
dev_ctx
=
new
CUDADeviceContext
(
CUDA
Place
(
d
));
Mark
(
"_cuda_startup_"
,
dev_ctx
);
Mark
(
"_cuda_startup_"
,
dev_ctx
);
dev_ctx
->
Wait
();
dev_ctx
->
Wait
();
});
});
...
@@ -52,20 +151,20 @@ void EnableProfiler(ProfilerState state) {
...
@@ -52,20 +151,20 @@ void EnableProfiler(ProfilerState state) {
}
}
#endif
#endif
// Mark the profiling start.
// Mark the profiling start.
Mark
(
"_start_profiler_"
);
Mark
(
"_start_profiler_"
,
nullptr
);
}
}
std
::
vector
<
std
::
vector
<
Event
>>
DisableProfiler
()
{
std
::
vector
<
std
::
vector
<
Event
>>
DisableProfiler
()
{
PADDLE_ENFORCE
(
kS
tate
!=
ProfilerState
::
kDisabled
,
PADDLE_ENFORCE
(
g_s
tate
!=
ProfilerState
::
kDisabled
,
"Can't disable profiling, since it's not starting."
);
"Can't disable profiling, since it's not starting."
);
// Mark the profiling stop.
// Mark the profiling stop.
Mark
(
"_stop_profiler_"
);
Mark
(
"_stop_profiler_"
,
nullptr
);
kS
tate
=
ProfilerState
::
kDisabled
;
g_s
tate
=
ProfilerState
::
kDisabled
;
std
::
vector
<
std
::
vector
<
Event
>>
result
;
std
::
vector
<
std
::
vector
<
Event
>>
result
;
std
::
lock_guard
<
std
::
mutex
>
guard
(
kAllEventListsM
utex
);
std
::
lock_guard
<
std
::
mutex
>
guard
(
g_all_event_lists_m
utex
);
for
(
auto
it
=
kAllEventLists
.
begin
();
it
!=
kAllEventLists
.
end
();
++
it
)
{
for
(
auto
it
=
g_all_event_lists
.
begin
();
it
!=
g_all_event_lists
.
end
();
auto
&
list
=
*
it
;
++
it
)
{
result
.
emplace_back
(
list
->
Reduce
());
result
.
emplace_back
(
(
*
it
)
->
Reduce
());
}
}
return
result
;
return
result
;
}
}
...
...
paddle/platform/profiler.h
浏览文件 @
0a5fbb06
...
@@ -24,76 +24,24 @@ namespace platform {
...
@@ -24,76 +24,24 @@ namespace platform {
enum
EventKind
{
kMark
,
kPushRange
,
kPopRange
};
enum
EventKind
{
kMark
,
kPushRange
,
kPopRange
};
inline
uint64_t
GetTimeInNsec
()
{
// using std::chrono;
using
clock
=
std
::
conditional
<
std
::
chrono
::
high_resolution_clock
::
is_steady
,
std
::
chrono
::
high_resolution_clock
,
std
::
chrono
::
steady_clock
>::
type
;
return
std
::
chrono
::
duration_cast
<
std
::
chrono
::
nanoseconds
>
(
clock
::
now
().
time_since_epoch
())
.
count
();
}
class
Event
{
class
Event
{
public:
public:
// the DeviceContext is used to get the cuda stream.
// The DeviceContext is used to get the cuda stream.
// If CPU profiling mode, can pass nullptr.
Event
(
EventKind
kind
,
std
::
string
name
,
uint32_t
thread_id
,
Event
(
EventKind
kind
,
std
::
string
name
,
uint32_t
thread_id
,
const
platform
::
DeviceContext
*
dev_ctx
=
nullptr
)
DeviceContext
*
dev_ctx
);
:
kind_
(
kind
),
name_
(
std
::
move
(
name
)),
thread_id_
(
thread_id
)
{
has_cuda_
=
false
;
#ifdef PADDLE_WITH_CUDA
auto
*
cuda_dev_ctx
=
static_cast
<
const
platform
::
CUDADeviceContext
*>
(
dev_ctx
);
if
(
cuda_dev_ctx
)
{
PADDLE_ENFORCE
(
cudaGetDevice
(
&
device_
));
PADDLE_ENFORCE
(
cudaEventCreate
(
&
event_
));
auto
stream
=
cuda_dev_ctx
->
stream
();
PADDLE_ENFORCE
(
cudaEventRecord
(
event_
,
stream
));
has_cuda_
=
true
;
}
#endif
cpu_ns_
=
GetTimeInNsec
();
}
std
::
string
kind
()
const
{
switch
(
kind_
)
{
case
EventKind
::
kMark
:
return
"mark"
;
case
EventKind
::
kPushRange
:
return
"push"
;
case
EventKind
::
kPopRange
:
return
"pop"
;
}
PADDLE_THROW
(
"Unknown EventKind."
);
}
std
::
string
kind
()
const
;
std
::
string
name
()
const
{
return
name_
;
}
std
::
string
name
()
const
{
return
name_
;
}
bool
has_cuda
()
const
{
return
has_cuda_
;
}
bool
has_cuda
()
const
{
return
has_cuda_
;
}
#ifdef PADDLE_WITH_CUDA
#ifdef PADDLE_WITH_CUDA
cudaEvent_t
event
()
const
{
return
event_
;
}
cudaEvent_t
event
()
const
{
return
event_
;
}
int
device
()
const
{
return
device_
;
}
int
device
()
const
{
return
device_
;
}
#endif
#endif
double
CpuElapsedUs
(
const
Event
&
e
)
const
{
double
CpuElapsedUs
(
const
Event
&
e
)
const
;
return
(
e
.
cpu_ns_
-
cpu_ns_
)
/
(
1000.0
);
double
CudaElapsedUs
(
const
Event
&
e
)
const
;
}
double
CudaElapsedUs
(
const
Event
&
e
)
const
{
#ifdef PADDLE_WITH_CUDA
PADDLE_ENFORCE
(
e
.
has_cuda
()
&&
has_cuda
());
PADDLE_ENFORCE
(
e
.
device
()
==
device
());
PADDLE_ENFORCE
(
cudaEventSynchronize
(
event_
));
PADDLE_ENFORCE
(
cudaEventSynchronize
(
e
.
event
()));
float
ms
;
PADDLE_ENFORCE
(
cudaEventElapsedTime
(
&
ms
,
event_
,
e
.
event
()));
return
ms
*
1000.0
;
#else
PADDLE_THROW
(
"CUDA is not enabled"
);
#endif
}
private:
private:
EventKind
kind_
;
EventKind
kind_
;
...
@@ -108,11 +56,11 @@ class Event {
...
@@ -108,11 +56,11 @@ class Event {
};
};
struct
EventList
{
struct
EventList
{
constexpr
static
s
td
::
s
ize_t
kMB
=
1024
*
1024
;
constexpr
static
size_t
kMB
=
1024
*
1024
;
constexpr
static
s
td
::
s
ize_t
kEventBlockSize
=
16
*
kMB
;
constexpr
static
size_t
kEventBlockSize
=
16
*
kMB
;
constexpr
static
s
td
::
s
ize_t
kEventSize
=
sizeof
(
Event
);
constexpr
static
size_t
kEventSize
=
sizeof
(
Event
);
constexpr
static
s
td
::
s
ize_t
kEventAlign
=
alignof
(
Event
);
constexpr
static
size_t
kEventAlign
=
alignof
(
Event
);
constexpr
static
s
td
::
s
ize_t
kNumBlock
=
constexpr
static
size_t
kNumBlock
=
kEventBlockSize
/
kEventBlockSize
/
((
kEventSize
+
kEventAlign
-
1
)
/
kEventAlign
*
kEventAlign
);
((
kEventSize
+
kEventAlign
-
1
)
/
kEventAlign
*
kEventAlign
);
...
@@ -139,58 +87,27 @@ struct EventList {
...
@@ -139,58 +87,27 @@ struct EventList {
};
};
enum
ProfilerState
{
enum
ProfilerState
{
kDisabled
,
kDisabled
,
// disabled state
kCPU
,
kCPU
,
// CPU profiling state
kCUDA
,
kCUDA
,
// GPU profiling state
};
};
// The profiler state, the initial value is ProfilerState::kDisabled
void
Mark
(
const
std
::
string
&
name
,
DeviceContext
*
dev_ctx
);
extern
ProfilerState
kState
;
// The global mutex
extern
std
::
mutex
kAllEventListsMutex
;
// The total event lists of all threads
extern
std
::
list
<
std
::
shared_ptr
<
EventList
>>
kAllEventLists
;
// The thread local event list only can be accessed by the specific thread
extern
thread_local
std
::
shared_ptr
<
EventList
>
kEventList
;
// The thread index of each thread
extern
thread_local
int32_t
kThreadId
;
// The kNextThreadId is a global counter for threads, by the kThreadId and
// kNextThreadId, we can know how many threads have created EventList.
extern
uint32_t
kNextThreadId
;
inline
EventList
&
GetEventList
()
{
if
(
!
kEventList
)
{
std
::
lock_guard
<
std
::
mutex
>
guard
(
kAllEventListsMutex
);
kEventList
=
std
::
make_shared
<
EventList
>
();
kThreadId
=
kNextThreadId
++
;
kAllEventLists
.
emplace_front
(
kEventList
);
}
return
*
kEventList
;
}
inline
void
Mark
(
const
std
::
string
name
,
const
platform
::
DeviceContext
*
dev_ctx
=
nullptr
)
{
GetEventList
().
Record
(
EventKind
::
kMark
,
std
::
move
(
name
),
kThreadId
,
dev_ctx
);
}
struct
RecordEvent
{
struct
RecordEvent
{
explicit
RecordEvent
(
const
std
::
string
name
,
explicit
RecordEvent
(
const
std
::
string
&
name
,
DeviceContext
*
dev_ctx
);
platform
::
DeviceContext
*
dev_ctx
=
nullptr
)
{
if
(
kState
==
ProfilerState
::
kDisabled
)
return
;
dev_ctx_
=
dev_ctx
;
GetEventList
().
Record
(
EventKind
::
kPushRange
,
std
::
move
(
name
),
kThreadId
,
dev_ctx_
);
}
~
RecordEvent
()
{
~
RecordEvent
();
if
(
kState
==
ProfilerState
::
kDisabled
)
return
;
GetEventList
().
Record
(
EventKind
::
kPopRange
,
std
::
string
(),
kThreadId
,
// The device context is used by Event to get the current cuda stream.
dev_ctx_
);
DeviceContext
*
dev_ctx_
;
}
platform
::
DeviceContext
*
dev_ctx_
;
};
};
// Enable the profiling function.
void
EnableProfiler
(
ProfilerState
state
);
void
EnableProfiler
(
ProfilerState
state
);
// Return the event list of all threads. Asummed the returned value calls
// event_lists, event_lists[i][j] represents the j-th Event of i-th thread.
std
::
vector
<
std
::
vector
<
Event
>>
DisableProfiler
();
std
::
vector
<
std
::
vector
<
Event
>>
DisableProfiler
();
}
// namespace platform
}
// namespace platform
...
...
paddle/platform/profiler_test.cc
浏览文件 @
0a5fbb06
...
@@ -19,13 +19,13 @@ TEST(Event, CpuElapsedTime) {
...
@@ -19,13 +19,13 @@ TEST(Event, CpuElapsedTime) {
using
paddle
::
platform
::
Event
;
using
paddle
::
platform
::
Event
;
using
paddle
::
platform
::
EventKind
;
using
paddle
::
platform
::
EventKind
;
Event
start_event
(
EventKind
::
kPushRange
,
"test"
,
0
);
Event
start_event
(
EventKind
::
kPushRange
,
"test"
,
0
,
nullptr
);
EXPECT_TRUE
(
start_event
.
has_cuda
()
==
false
);
EXPECT_TRUE
(
start_event
.
has_cuda
()
==
false
);
int
counter
=
0
;
int
counter
=
0
;
while
(
counter
!=
1000
)
{
while
(
counter
!=
1000
)
{
counter
++
;
counter
++
;
}
}
Event
stop_event
(
EventKind
::
kPopRange
,
"test"
,
0
);
Event
stop_event
(
EventKind
::
kPopRange
,
"test"
,
0
,
nullptr
);
EXPECT_GT
(
start_event
.
CpuElapsedUs
(
stop_event
),
0
);
EXPECT_GT
(
start_event
.
CpuElapsedUs
(
stop_event
),
0
);
}
}
...
@@ -33,11 +33,11 @@ TEST(Event, CpuElapsedTime) {
...
@@ -33,11 +33,11 @@ TEST(Event, CpuElapsedTime) {
TEST
(
Event
,
CudaElapsedTime
)
{
TEST
(
Event
,
CudaElapsedTime
)
{
using
paddle
::
platform
::
DeviceContext
;
using
paddle
::
platform
::
DeviceContext
;
using
paddle
::
platform
::
CUDADeviceContext
;
using
paddle
::
platform
::
CUDADeviceContext
;
using
paddle
::
platform
::
GPU
Place
;
using
paddle
::
platform
::
CUDA
Place
;
using
paddle
::
platform
::
Event
;
using
paddle
::
platform
::
Event
;
using
paddle
::
platform
::
EventKind
;
using
paddle
::
platform
::
EventKind
;
DeviceContext
*
dev_ctx
=
new
CUDADeviceContext
(
GPU
Place
(
0
));
DeviceContext
*
dev_ctx
=
new
CUDADeviceContext
(
CUDA
Place
(
0
));
Event
start_event
(
EventKind
::
kPushRange
,
"test"
,
0
,
dev_ctx
);
Event
start_event
(
EventKind
::
kPushRange
,
"test"
,
0
,
dev_ctx
);
EXPECT_TRUE
(
start_event
.
has_cuda
()
==
true
);
EXPECT_TRUE
(
start_event
.
has_cuda
()
==
true
);
int
counter
=
0
;
int
counter
=
0
;
...
@@ -60,10 +60,10 @@ TEST(RecordEvent, RecordEvent) {
...
@@ -60,10 +60,10 @@ TEST(RecordEvent, RecordEvent) {
DeviceContext
*
dev_ctx
=
nullptr
;
DeviceContext
*
dev_ctx
=
nullptr
;
#ifdef PADDLE_WITH_CUDA
#ifdef PADDLE_WITH_CUDA
using
paddle
::
platform
::
CUDADeviceContext
;
using
paddle
::
platform
::
CUDADeviceContext
;
using
paddle
::
platform
::
GPU
Place
;
using
paddle
::
platform
::
CUDA
Place
;
state
=
ProfilerState
::
kCUDA
;
state
=
ProfilerState
::
kCUDA
;
dev_ctx
=
dev_ctx
=
new
paddle
::
platform
::
CUDADeviceContext
(
paddle
::
platform
::
GPU
Place
(
0
));
new
paddle
::
platform
::
CUDADeviceContext
(
paddle
::
platform
::
CUDA
Place
(
0
));
#endif
#endif
EnableProfiler
(
state
);
EnableProfiler
(
state
);
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录