假设我们有 2 个长度为 n 的向量 V 和 W。我在 SYCL 中启动了一个内核,它对 V 的每个实体执行 3 次 for 循环迭代。 for 循环的描述如下:
首先,循环根据当前迭代中 V 的 4 个随机值计算索引 (W[idx]) 处的 W 值。即,W[idx] = sum (V[a] + V[b] + V[c]+ V[d])。其中 a、b、c 和 d 不是连续的,而是为每个 idx 定义的。
根据 W[idx] 更新 V[idx]。但是,只有在步骤 1 中使用 V[idx] 处的值来计算 W 之后,才应该对 V[idx] 进行更新。
假设我在内核中有 3 次 for 循环迭代。如果一个线程在迭代 1 中并尝试使用迭代 1 的 V[2] 在迭代 1 中计算 W[idx = 18]。另一个线程假设在迭代 2 中并尝试在迭代 2 中计算 W[2] a,b,c,d 并在迭代 2 中计算 V[2]。
如果第二个线程领先于第一个线程,则第二个线程将在迭代 2 时更新 V[2] 的值。在这种情况下,当第一个线程想要使用第一个迭代的 V[2] 时,我该怎么做确保这是 Syncd。在 SYCL 中。在这种情况下将使用atomic_ref帮助,考虑到第二个线程的目标是仅在线程 [1] 使用 V[2] 之后才写入它。还要注意的是,第一次迭代的这个 V[2] 也需要在第一次迭代中计算一些其他的 W 以及在其他一些线程中运行的第一次迭代。如何确保第二次迭代中的 V[2] 的值在第二次迭代中得到更新,只有当第一次迭代的 V[2] 已在所有必需的实例中使用时?这是源代码:
void jacobi_relaxation(cl::sycl::queue& q, ProblemVar& obj, int current_level) {
for (int iterations = 1; iterations <= mu1; iterations++) {
// TODO => v(k+1) = [(1 - omega) x I + omega x D^-1 x(-L-U)] x v(k) + omega x
// D^-1
// x
// f
//
// step 1 => v* = (-L-U) x v
// step 2 => v* = D^-1 x (v* + f)
// step 3 => v = (1-omega) x v + omega x v*
q.submit([&](cl::sycl::handler& h) {
// Accessor for current_level matrix CSR values
auto row = obj.A_sp_dict[current_level].row.get_access<cl::sycl::access::mode::read>(h);
auto col = obj.A_sp_dict[current_level].col.get_access<cl::sycl::access::mode::read>(h);
auto val = obj.A_sp_dict[current_level].values.get_access<cl::sycl::access::mode::read>(h);
auto diag_indices
= obj.A_sp_dict[current_level].diag_index.get_access<cl::sycl::access::mode::read>(h);
auto vec = obj.vecs_dict[current_level].get_access<cl::sycl::access::mode::read>(h);
auto f = obj.b_dict[current_level].get_access<cl::sycl::access::mode::read>(h);
cl::sycl::accessor<double, 1, cl::sycl::access::mode::write> vec_star{
obj.temp_dict[current_level], h, cl::sycl::noinit};
// Require 2 kernels as we perform Jacobi Relaxations
h.parallel_for(
cl::sycl::range<1>{obj.num_dofs_per_level[current_level]}, [=](cl::sycl::id<1> idx) {
// double diag_multiplier = 0.0;
vec_star[idx[0]] = 0.0;
for (std::int32_t i = row[idx[0]]; i < row[idx[0] + 1]; i++) {
vec_star[idx[0]] += -1.0 * val[i] * vec[col[i]];
}
vec_star[idx[0]] = (1.0 / val[diag_indices[idx[0]]]) * (vec_star[idx[0]] + f[idx[0]])
+ vec[idx[0]]; // step 2
});
});
q.wait();
q.submit([&](cl::sycl::handler& h) {
// Accessor for current_level vector
auto vec = obj.vecs_dict[current_level].get_access<cl::sycl::access::mode::read_write>(h);
auto vec_star
= obj.temp_dict[current_level].get_access<cl::sycl::access::mode::read_write>(h);
h.parallel_for(cl::sycl::range<1>{obj.num_dofs_per_level[current_level]},
[=](cl::sycl::id<1> idx) {
vec[idx[0]] = (1.0 - omega) * vec[idx[0]] + omega * vec_star[idx[0]]; // step
// 3
vec_star[idx[0]] = 0.0;
});
});
q.wait();
}
}
如果您看到,对于每次迭代,我都被迫启动 2 个内核,以便我可以在 2 个计算之间创建一个同步点。并且在第二次计算结束时。我想找到一种方法,只创建一个内核,并在存在同步的情况下在该内核内执行迭代。