Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
138 commits
Select commit Hold shift + click to select a range
d4700e5
[NATIVECPU] faster enqueue for larger ranges
uwedolinsky Feb 27, 2025
b3f2215
[NATIVECPU] use size_t, reserve vector size
uwedolinsky Aug 28, 2024
780588c
[NATIVECPU] added threadpool file to CMakeList
uwedolinsky Oct 22, 2024
db924f0
[NATIVECPU] Simple TBB backend
uwedolinsky Oct 22, 2024
1509655
[NATIVECPU] more shared code
uwedolinsky Oct 23, 2024
45ee46c
[NATIVECPU] update oneTBB tag
uwedolinsky Oct 24, 2024
aa7dec8
[NATIVECPU] added required include not needed by Windows
uwedolinsky Oct 24, 2024
29d11f9
[NATIVECPU] added system headers first
uwedolinsky Oct 24, 2024
e202f8d
[NATIVECPU] cmake fix
uwedolinsky Oct 24, 2024
fe8d099
[NATIVECPU] removed GIT_SHALLOW
uwedolinsky Oct 25, 2024
c2a3f57
[NATIVECPU] turn CMAKE_INCLUDE_CURRENT_DIR off for tbb
uwedolinsky Nov 1, 2024
be5b134
[NATIVECPU] workaround for oneTBB casting away const qualifiers
uwedolinsky Nov 1, 2024
b18401f
[NATIVECPU] workaround for oneTBB casting away const qualifiers
uwedolinsky Nov 1, 2024
4bff038
[NATIVECPU] remove potentially unneeded cmake
uwedolinsky Nov 1, 2024
eacf522
[NATIVECPU] oneTBB disabled by default
uwedolinsky Nov 4, 2024
c2996eb
[NATIVECPU] tbb to oneTBB
uwedolinsky Nov 4, 2024
91a6a49
[NATIVECPU] improved comment
uwedolinsky Nov 4, 2024
c1745c7
[NATIVECPU] tbb to oneTBB
uwedolinsky Nov 4, 2024
488504c
[NATIVECPU] tbb to oneTBB
uwedolinsky Nov 4, 2024
53013d4
[NATIVECPU] num_threads with oneTBB
uwedolinsky Nov 4, 2024
e8d8ff4
[NATIVECPU] added comment to cmake
uwedolinsky Nov 6, 2024
99c76c9
[NATIVECPU] using old task ids with tbb (WIP)
uwedolinsky Nov 6, 2024
9b40081
[NATIVECPU] fixed merge from main
uwedolinsky Nov 13, 2024
07c178d
[NATIVECPU] fix merge with events update
uwedolinsky Nov 14, 2024
aee938a
[NATIVECPU] revert noise
uwedolinsky Nov 14, 2024
59d731a
[NATIVECPU] fix integer size warnings
uwedolinsky Nov 14, 2024
e0341ef
[NATIVECPU] update oneTBB tag
uwedolinsky Nov 26, 2024
e719ec0
[NATIVECPU] use oneTBB UXL github
uwedolinsky Nov 29, 2024
81c3c82
[NATIVECPU] undefine _DEBUG in release builds for tbb
uwedolinsky Dec 12, 2024
ecaf51b
[NATIVECPU] oneTBB bump
uwedolinsky Jan 27, 2025
f5d6547
[NATIVECPU] clang-format and removed one inline
uwedolinsky Jan 28, 2025
e975e77
[NATIVECPU] clang-format
uwedolinsky Jan 28, 2025
26a5bd0
[NATIVECPU] removed inline
uwedolinsky Jan 28, 2025
38a91f7
[NATIVECPU] renamed wait to wait_all
uwedolinsky Jan 28, 2025
b31bd44
[NATIVECPU] move
uwedolinsky Feb 3, 2025
960b1d5
[NATIVECPU] removed unused groups
uwedolinsky Feb 28, 2025
04bd48a
[NATIVECPU] added async memcpy
uwedolinsky Mar 27, 2025
45c76d9
Merge remote-tracking branch 'origin/sycl' into uwe/fasternativecpuen…
uwedolinsky Mar 27, 2025
7985e95
Merge remote-tracking branch 'origin/sycl' into uwe/fasternativecpuen…
uwedolinsky Apr 10, 2025
7008b8b
[NATIVECPU] added non-blocking invoker, removed unused variable
uwedolinsky Apr 11, 2025
a3f4ea0
Merge remote-tracking branch 'origin/sycl' into uwe/fasternativecpuen…
uwedolinsky Apr 11, 2025
2f1b3fe
[NATIVECPU] waiting for events in threads
uwedolinsky Apr 16, 2025
d5aa0cf
[NATIVECPU] resolved merge
uwedolinsky Apr 16, 2025
8efb1e4
[NATIVECPU] ndrange enqueue with less work for main thread
uwedolinsky Apr 22, 2025
67e9995
Merge remote-tracking branch 'origin/sycl' into uwe/fasternativecpuen…
uwedolinsky Apr 22, 2025
2c52186
[NATIVECPU] static_assert for pointer type
uwedolinsky Apr 22, 2025
57bff8e
[NATIVECPU] resolved merge
uwedolinsky Apr 22, 2025
5348490
[NATIVECPU] added anonymous namespace
uwedolinsky Apr 22, 2025
1de1251
[NATIVECPU] separated out Invokers for enqueues
uwedolinsky Apr 22, 2025
9173f5e
[NATIVECPU] made more memops async
uwedolinsky Apr 23, 2025
7cd7caa
[NATIVECPU] memop pointer check outside worker lambda
uwedolinsky Apr 23, 2025
849ba98
Merge remote-tracking branch 'origin/sycl' into uwe/nativecpu_eventswait
uwedolinsky Apr 23, 2025
32ecf09
[NATIVECPU] moved inEvents
uwedolinsky Apr 23, 2025
c77454e
[NATIVECPU] fixed merge
uwedolinsky Apr 23, 2025
6142549
Merge remote-tracking branch 'origin/uwe/nativecpu_eventswait' into u…
uwedolinsky Apr 24, 2025
4b05062
[NATIVECPU] use unique_ptr for WaitInfo
uwedolinsky Apr 28, 2025
2722cad
[NATIVECPU] async memcopy
uwedolinsky Apr 28, 2025
24a0da3
[NATIVECPU] fixed merge
uwedolinsky Apr 28, 2025
22898b4
[NATIVECPU] code reuse for memcopies
uwedolinsky Apr 28, 2025
bed18b6
Merge remote-tracking branch 'origin/sycl' into uwe/fasternativecpuen…
uwedolinsky Apr 28, 2025
5d12b7a
[NATIVECPU] removed invoker
uwedolinsky Apr 29, 2025
400ba0d
[NATIVECPU] removed unneeded function
uwedolinsky Apr 29, 2025
40f7270
[NATIVECPU] async wait in noop copy
uwedolinsky Apr 29, 2025
bd161bc
[NATIVECPU] async membuffer ops
uwedolinsky Apr 29, 2025
870754a
[NATIVECPU] quick fix for in-order queues
uwedolinsky Apr 30, 2025
e11f596
[NATIVECPU] construct state inside thread
uwedolinsky Apr 30, 2025
b4069d1
[NATIVECPU] update comments
uwedolinsky May 1, 2025
e83715c
Merge remote-tracking branch 'origin/sycl' into uwe/fasternativecpuen…
uwedolinsky May 1, 2025
ee2d232
Merge remote-tracking branch 'origin/sycl' into uwe/onetbb_integratio…
uwedolinsky May 2, 2025
dfc67d8
[NATIVECPU] removed nullptr check for pHEventWaitList
uwedolinsky May 2, 2025
a25b2c7
[NATIVECPU] updated oneTBB tag
uwedolinsky May 2, 2025
3074b16
[NATIVECPU] removed unneeded mutable
uwedolinsky May 6, 2025
070f0cf
[NATIVECPU] moved lambda code from enqueueMemBufferReadWriteRect_impl…
uwedolinsky May 6, 2025
eb64e5d
[NATIVECPU] resolved merge with events
uwedolinsky May 6, 2025
3207ffa
[NATIVECPU] simplified event generation
uwedolinsky May 7, 2025
106a31f
[MNATIVECPU] fixed merge with async branch
uwedolinsky May 7, 2025
6e1f722
[NATIVECPU] added interface to disable waiting in threads (for oneTBB)
uwedolinsky May 7, 2025
2a557f9
[NATIVECPU] removed the now unneeded std::function wrapper for oneTBB
uwedolinsky May 7, 2025
29c201c
[NATIVECPU] revert accidental filemode change
uwedolinsky May 7, 2025
941932b
[NATIVECPU] replaced function pointer template parameter
uwedolinsky May 9, 2025
4c5700d
Merge remote-tracking branch 'origin/sycl' into uwe/fasternativecpuen…
uwedolinsky May 12, 2025
1532779
[NATIVECPU] simplified WaitInfo
uwedolinsky May 13, 2025
0204d11
Merge remote-tracking branch 'origin/sycl' into uwe/fasternativecpuen…
uwedolinsky May 21, 2025
ffe66d0
[NATIVECPU] added mutex to backend queue
uwedolinsky May 26, 2025
3505c76
Merge remote-tracking branch 'origin/sycl' into uwe/fasternativecpuen…
uwedolinsky May 28, 2025
c95ebe7
[NATIVECPU] renamed flag to lock mutex
uwedolinsky Jun 2, 2025
67d77da
Merge remote-tracking branch 'origin/sycl' into uwe/nativecpu_queuemutex
uwedolinsky Jun 2, 2025
73cf574
Merge remote-tracking branch 'origin/sycl' into uwe/fasternativecpuen…
uwedolinsky Jun 4, 2025
6fcea0f
[NATIVECPU] launch ranges with number of work items that is multiple …
uwedolinsky Jun 4, 2025
788cf69
[NATIVECPU] merge with events branch
uwedolinsky Jun 5, 2025
d86f429
[NATIVECPU] used lock_guard
uwedolinsky Jun 5, 2025
ddb908f
[NATIVECPU] removed unused local
uwedolinsky Jun 5, 2025
22ab082
[NATIVECPU] fixed merge with uwe/nativecpu_queuemutex
uwedolinsky Jun 9, 2025
8b20c39
Merge remote-tracking branch 'origin/uwe/fasternativecpuenqueue_async…
uwedolinsky Jun 9, 2025
c57b68a
Merge remote-tracking branch 'origin/uwe/nativecpu_queuemutex' into u…
uwedolinsky Jun 9, 2025
1d62903
[NATIVECPU] removed reference captures in enqueue lambdas
uwedolinsky Jun 9, 2025
aced1a4
Merge remote-tracking branch 'origin/uwe/fasternativecpuenqueue_async…
uwedolinsky Jun 9, 2025
11ebe05
[NATIVECPU] bump oneTBB version
uwedolinsky Jun 11, 2025
666f2ae
Merge remote-tracking branch 'origin/sycl' into uwe/fasternativecpuen…
uwedolinsky Jun 12, 2025
37ccfca
[NATIVECPU] added option to turn off waiting in threads for oneTBB
uwedolinsky Jun 12, 2025
dadfcd3
[NATIVECPU] added tbb::parallel_for for ranges
uwedolinsky Jun 13, 2025
3e978db
[NATIVECPU] all kernel launches now use tbb::parallel_for
uwedolinsky Jun 13, 2025
0e28a6e
[NATIVECPU] added function to get thread id from tbb
uwedolinsky Jun 13, 2025
50e0720
Merge remote-tracking branch 'origin/sycl' into uwe/fasternativecpuen…
uwedolinsky Jul 3, 2025
5fcea55
Merge remote-tracking branch 'origin/sycl' into uwe/fasternativecpuen…
uwedolinsky Jul 21, 2025
4ad9ee7
[NATIEVCPU] resolved merge
uwedolinsky Jul 23, 2025
4386697
Merge branch 'uwe/fasternativecpuenqueue_async_ops_eventswait_onetbb_…
uwedolinsky Jul 23, 2025
2fd6b37
[NATIVECPU] resolved merge with sycl branch
uwedolinsky Jul 24, 2025
5e0b99d
[NATIVECPU] removed unneeded capture
uwedolinsky Jul 24, 2025
f05bba1
[NATIVECPU] removed mutable from task lambda
uwedolinsky Jul 24, 2025
8548f6a
[NATIVECPU] merge with uwe/fasternativecpuenqueue_async_ops_eventswait
uwedolinsky Jul 24, 2025
58ffb89
[NATIVECPU] clang-format
uwedolinsky Jul 24, 2025
aecf330
[NATIVECPU] merge with uwe/fasternativecpuenqueue_async_ops_eventswait
uwedolinsky Jul 24, 2025
f6b68dc
[NATIVECPU] clang-format
uwedolinsky Jul 24, 2025
faa03d2
[SYCL][NATIVECPU] update docs for oneTBB integration
uwedolinsky Nov 5, 2024
cfcc325
[SYCL][NATIVECPU] fixed heading for oneTBB integration
uwedolinsky Nov 5, 2024
159db63
Merge remote-tracking branch 'origin/sycl' into uwe/fasternativecpuen…
uwedolinsky Jul 28, 2025
a26eb58
[NATIVECPU] removed unused code
uwedolinsky Jul 28, 2025
271cf93
[NATIVECPU] revert to size_t
uwedolinsky Jul 28, 2025
5784a93
[NATIVECPU] remove inline
uwedolinsky Jul 28, 2025
49942d2
Merge remote-tracking branch 'origin/sycl' into uwe/fasternativecpuen…
uwedolinsky Jul 29, 2025
5bcb27c
Merge remote-tracking branch 'origin/uwe/fasternativecpuenqueue_async…
uwedolinsky Aug 5, 2025
6a6f19f
Merge remote-tracking branch 'origin/sycl' into uwe/fasternativecpuen…
uwedolinsky Aug 6, 2025
ecf52a5
Merge remote-tracking branch 'origin/uwe/fasternativecpuenqueue_async…
uwedolinsky Aug 7, 2025
fc9b330
[NATIVECPU] resolved merge with sycl
uwedolinsky Aug 18, 2025
0e0b454
[NATIVECPU] remove comment
uwedolinsky Aug 18, 2025
a009bd2
[NATIVECPU] removed unused function
uwedolinsky Aug 18, 2025
02450dc
Merge remote-tracking branch 'origin/sycl' into uwe/fasternativecpuen…
uwedolinsky Aug 19, 2025
a44fc99
[NATIVECPU] update oneTBB
uwedolinsky Aug 19, 2025
6a5f9d1
[NATIVECPU] add -Wno-stringop-overflow for oneTBB
uwedolinsky Aug 19, 2025
87f3e17
[NATIVECPU] add -Wno-unknown-warning-option for oneTBB
uwedolinsky Aug 19, 2025
c3ff19f
[NATIVECPU] merge with oneTBB branche
uwedolinsky Aug 21, 2025
1b294bb
[NATIVECPU] remove unneeded code
uwedolinsky Aug 21, 2025
53d5b87
[NATIEVCPU] sharing kernel invoke
uwedolinsky Aug 21, 2025
ea19145
[NATIVECPU] supporting tbb:parallel_for for 1D/2D/3D
uwedolinsky Aug 22, 2025
320169f
[NATIVECPU] moved function
uwedolinsky Aug 22, 2025
e1961df
[NATIVECPU] merge with sycl branch
uwedolinsky Sep 2, 2025
e1cdbdc
Merge remote-tracking branch 'origin/sycl' into uwe/fasternativecpuen…
uwedolinsky Sep 12, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 149 additions & 15 deletions unified-runtime/source/adapters/native_cpu/enqueue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,113 @@ static inline native_cpu::state getState(const native_cpu::NDRDescT &ndr) {
return resized_state;
}

static inline void invoke_kernel(native_cpu::state &state,
const ur_kernel_handle_t_ &kernel, size_t g0,
size_t g1, size_t g2,
size_t numParallelThreads, size_t threadId,
const native_cpu::NDRDescT &ndr) {
#ifdef NATIVECPU_USE_OCK
state.update(g0, g1, g2);
kernel._subhandler(kernel.getArgs(numParallelThreads, threadId).data(),
&state);
(void)ndr;
#else
for (size_t local2 = 0; local2 < ndr.LocalSize[2]; ++local2) {
for (size_t local1 = 0; local1 < ndr.LocalSize[1]; ++local1) {
for (size_t local0 = 0; local0 < ndr.LocalSize[0]; ++local0) {
state.update(g0, g1, g2, local0, local1, local2);
kernel._subhandler(kernel.getArgs(numParallelThreads, threadId).data(),
&state);
}
}
}
#endif
}

#ifdef NATIVECPU_WITH_ONETBB

#define NATIVECPU_WITH_ONETBB_PARALLELFOR

using IndexT = std::array<size_t, 3>;
using RangeT = native_cpu::NDRDescT::RangeT;

static inline void execute_range(native_cpu::state &state,
const ur_kernel_handle_t_ &hKernel,
IndexT first, IndexT lastPlusOne,
size_t numParallelThreads, size_t threadId,
const native_cpu::NDRDescT &ndr) {
for (size_t g2 = first[2]; g2 < lastPlusOne[2]; g2++) {
for (size_t g1 = first[1]; g1 < lastPlusOne[1]; g1++) {
for (size_t g0 = first[0]; g0 < lastPlusOne[0]; g0 += 1) {
invoke_kernel(state, hKernel, g0, g1, g2, numParallelThreads, threadId,
ndr);
}
}
}
}

namespace native_cpu {

class nativecpu_tbb_executor {
const native_cpu::NDRDescT ndr;

protected:
const ur_kernel_handle_t_ &hKernel;
const size_t numParallelThreads;

void execute(IndexT first, IndexT last_plus_one) const {
auto state = getState(ndr);
auto threadId = native_cpu::getTBBThreadID();
execute_range(state, hKernel, first, last_plus_one, numParallelThreads,
threadId, ndr);
}

public:
void operator()(const tbb::blocked_range3d<size_t> &r) const {
execute({r.pages().begin(), r.rows().begin(), r.cols().begin()},
{r.pages().end(), r.rows().end(), r.cols().end()});
}
void operator()(const tbb::blocked_range2d<size_t> &r) const {
execute({r.rows().begin(), r.cols().begin(), 0},
{r.rows().end(), r.cols().end(), 1});
}
void operator()(const tbb::blocked_range<size_t> &r) const {
execute({r.begin(), 0, 0}, {r.end(), 1, 1});
}
nativecpu_tbb_executor(const native_cpu::NDRDescT &n,
const ur_kernel_handle_t_ &k,
const size_t numParallelThreads)
: ndr(n), hKernel(k), numParallelThreads(numParallelThreads) {}
};

using tbb_nd_executor = nativecpu_tbb_executor;

template <template <class> class RangeTpl, class... T>
static inline void invoke_tbb_parallel_for(const tbb_nd_executor &tbb_ex,
T... inits) {
RangeTpl<size_t> range(inits...);
tbb::parallel_for(range, tbb_ex);
}

static inline void invoke_tbb_parallel_for(size_t workDim,
const nativecpu_tbb_executor &tbb_ex,
IndexT first, IndexT last) {
if (workDim == 3) {
native_cpu::invoke_tbb_parallel_for<tbb::blocked_range3d>(
tbb_ex, first[0], last[0], first[1], last[1], first[2], last[2]);
} else if (workDim == 2) {
native_cpu::invoke_tbb_parallel_for<tbb::blocked_range2d>(
tbb_ex, first[0], last[0], first[1], last[1]);
} else {
native_cpu::invoke_tbb_parallel_for<tbb::blocked_range>(tbb_ex, first[0],
last[0]);
}
}

} // namespace native_cpu

#endif // NATIVECPU_WITH_ONETBB

UR_APIEXPORT ur_result_t UR_APICALL urEnqueueKernelLaunch(
ur_queue_handle_t hQueue, ur_kernel_handle_t hKernel, uint32_t workDim,
const size_t *pGlobalWorkOffset, const size_t *pGlobalWorkSize,
Expand Down Expand Up @@ -175,6 +282,7 @@ UR_APIEXPORT ur_result_t UR_APICALL urEnqueueKernelLaunch(
auto InEvents =
native_cpu::getWaitInfo(numEventsInWaitList, phEventWaitList, Tasks);

#ifndef NATIVECPU_WITH_ONETBB_PARALLELFOR
const size_t numWG = numWG0 * numWG1 * numWG2;
const size_t numWGPerThread = numWG / numParallelThreads;
const size_t remainderWG = numWG - numWGPerThread * numParallelThreads;
Expand All @@ -196,21 +304,8 @@ UR_APIEXPORT ur_result_t UR_APICALL urEnqueueKernelLaunch(
for (size_t g0 = rangeStart[0], g1 = rangeStart[1], g2 = rangeStart[2],
g3 = rangeStart[3];
g3 < rangeEnd; ++g3) {
#ifdef NATIVECPU_USE_OCK
state.update(g0, g1, g2);
kernel._subhandler(kernel.getArgs(numParallelThreads, threadId).data(),
&state);
#else
for (size_t local2 = 0; local2 < ndr.LocalSize[2]; ++local2) {
for (size_t local1 = 0; local1 < ndr.LocalSize[1]; ++local1) {
for (size_t local0 = 0; local0 < ndr.LocalSize[0]; ++local0) {
state.update(g0, g1, g2, local0, local1, local2);
kernel._subhandler(
kernel.getArgs(numParallelThreads, threadId).data(), &state);
}
}
}
#endif
invoke_kernel(state, kernel, g0, g1, g2, numParallelThreads, threadId,
ndr);
if (++g0 == numWG0) {
g0 = 0;
if (++g1 == numWG1) {
Expand All @@ -222,6 +317,45 @@ UR_APIEXPORT ur_result_t UR_APICALL urEnqueueKernelLaunch(
});
rangeStart = rangeEnd;
}
#else
const IndexT numWG = {numWG0, numWG1, numWG2};
IndexT groupsPerThread;
size_t dim = 0;
for (size_t t = 0; t < 3; t++)
groupsPerThread[t] = numWG[t] / numParallelThreads;
if (groupsPerThread[0] == 0) {
if (groupsPerThread[1])
dim = 1;
else if (groupsPerThread[2])
dim = 2;
}
IndexT first = {0, 0, 0}, last = numWG;
size_t wg_start = 0;
const native_cpu::tbb_nd_executor tbb_ex(ndr, *kernel, numParallelThreads);
auto invoke_parallel_for = [workDim, &tbb_ex, &first, &last]() {
if (workDim == 3) {
native_cpu::invoke_tbb_parallel_for<tbb::blocked_range3d>(
tbb_ex, first[0], last[0], first[1], last[1], first[2], last[2]);
} else if (workDim == 2) {
native_cpu::invoke_tbb_parallel_for<tbb::blocked_range2d>(
tbb_ex, first[0], last[0], first[1], last[1]);
} else {
native_cpu::invoke_tbb_parallel_for<tbb::blocked_range>(tbb_ex, first[0],
last[0]);
}
};

if (groupsPerThread[dim]) {
native_cpu::invoke_tbb_parallel_for(workDim, tbb_ex, first, last);
wg_start = groupsPerThread[dim] * numParallelThreads;
}
if (wg_start < numWG[dim]) {
first[dim] = wg_start;
last[dim] = numWG[dim];
native_cpu::invoke_tbb_parallel_for(workDim, tbb_ex, first, last);
}
#endif // NATIVECPU_WITH_ONETBB_PARALLELFOR

event->set_tasksinfo(Tasks.getMovedTaskInfo());

if (phEvent) {
Expand Down
11 changes: 8 additions & 3 deletions unified-runtime/source/adapters/native_cpu/threadpool.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -268,15 +268,20 @@ class TBB_TasksInfo {
TBB_TasksInfo(TBB_threadpool &t) : tp(&t) {}
};

inline auto getTBBThreadID() {
auto thread_id = tbb::this_task_arena::current_thread_index();
assert(thread_id >= 0 &&
thread_id < oneapi::tbb::info::default_concurrency());
return thread_id;
}

template <>
struct Scheduler<TBB_threadpool>
: Scheduler_base<TBB_threadpool, TBB_TasksInfo> {
using Scheduler_base<TBB_threadpool, TBB_TasksInfo>::Scheduler_base;
template <class T> void schedule(T &&task_) {
ref.Tasks().run([task = std::move(task_)]() {
auto thread_id = tbb::this_task_arena::current_thread_index();
assert(thread_id >= 0 &&
thread_id < oneapi::tbb::info::default_concurrency());
auto thread_id = getTBBThreadID();
task(thread_id);
});
}
Expand Down