enum ggml_status ggml_backend_graph_compute(ggml_backend_t backend, struct ggml_cgraph * cgraph) {
enum ggml_status err = ggml_backend_graph_compute_async(backend, cgraph);
ggml_backend_synchronize(backend);
return err;
}
結局のところ、いろいろ経由したものの長い旅を経て、ようやくここまでたどり着いた…w
enum ggml_status ggml_backend_graph_compute_async(ggml_backend_t backend, struct ggml_cgraph * cgraph) {
GGML_ASSERT(backend);
return backend->iface.graph_compute(backend, cgraph);
}
void ggml_backend_synchronize(ggml_backend_t backend) {
GGML_ASSERT(backend);
if (backend->iface.synchronize == NULL) {
return;
}
backend->iface.synchronize(backend);
}
ちなみにCPUだとiface.synchronizeがNULLなので同期処理は無視されている。そもそもSYCLでもiGPUだと1個なので無視してもいい気がしないでもないが…さらに掘り下げていくと、
ifaceはggml_backend_buffer_i型
ggml/src/ggml-backend-impl.h
struct ggml_backend_i {
const char * (*get_name)(ggml_backend_t backend);
void (*free)(ggml_backend_t backend);
// (optional) asynchronous tensor data access
void (*set_tensor_async) (ggml_backend_t backend, struct ggml_tensor * tensor, const void * data, size_t offset, size_t size);
void (*get_tensor_async) (ggml_backend_t backend, const struct ggml_tensor * tensor, void * data, size_t offset, size_t size);
void (*set_tensor_2d_async)(ggml_backend_t backend, struct ggml_tensor * tensor, const void * data, size_t offset, size_t size, size_t n_copies, size_t stride_tensor, size_t stride_data);
void (*get_tensor_2d_async)(ggml_backend_t backend, const struct ggml_tensor * tensor, void * data, size_t offset, size_t size, size_t n_copies, size_t stride_tensor, size_t stride_data);
bool (*cpy_tensor_async)(ggml_backend_t backend_src, ggml_backend_t backend_dst, const struct ggml_tensor * src, struct ggml_tensor * dst);
// (optional) complete all pending operations (required if the backend supports async operations)
void (*synchronize)(ggml_backend_t backend);
// (optional) graph plans (not used currently)
// compute graph with a plan
ggml_backend_graph_plan_t (*graph_plan_create) (ggml_backend_t backend, const struct ggml_cgraph * cgraph);
void (*graph_plan_free) (ggml_backend_t backend, ggml_backend_graph_plan_t plan);
// update the plan with a new graph - this should be faster than creating a new plan when the graph has the same topology
void (*graph_plan_update) (ggml_backend_t backend, ggml_backend_graph_plan_t plan, const struct ggml_cgraph * cgraph);
// compute the graph with the plan
enum ggml_status (*graph_plan_compute)(ggml_backend_t backend, ggml_backend_graph_plan_t plan);
// compute graph (always async if supported by the backend)
enum ggml_status (*graph_compute) (ggml_backend_t backend, struct ggml_cgraph * cgraph);
// (optional) event synchronization
// record an event on this stream
void (*event_record)(ggml_backend_t backend, ggml_backend_event_t event);
// wait for an event on on a different stream
void (*event_wait) (ggml_backend_t backend, ggml_backend_event_t event);
// (optional) sort/optimize the nodes in the graph
void (*graph_optimize) (ggml_backend_t backend, struct ggml_cgraph * cgraph);
};
と、定義されていて SYCLはggml/src/ggml-sycl/ggml-sycl.cpp で
static ggml_backend_i ggml_backend_sycl_interface = {
/* .get_name = */ ggml_backend_sycl_get_name,
/* .free = */ ggml_backend_sycl_free,
/* .set_tensor_async = */ ggml_backend_sycl_set_tensor_async,
/* .get_tensor_async = */ ggml_backend_sycl_get_tensor_async,
/* .set_tensor_2d_async = */ NULL,
/* .get_tensor_2d_async = */ NULL,
/* .cpy_tensor_async = */ NULL, // ggml_backend_sycl_cpy_tensor_async,
// // TODO: update for the new
// interface
/* .synchronize = */ ggml_backend_sycl_synchronize,
/* .graph_plan_create = */ NULL,
/* .graph_plan_free = */ NULL,
/* .graph_plan_update = */ NULL,
/* .graph_plan_compute = */ NULL,
/* .graph_compute = */ ggml_backend_sycl_graph_compute,
/* .event_record = */ ggml_backend_sycl_event_record,
/* .event_wait = */ ggml_backend_sycl_event_wait,
/* .graph_optimize = */ NULL,
};
と、定義されている。当初の目的通り、ggml_backend_sycl_synchronizeが実装されているので、ここで同期処理が行われている。
で、実際の ggml_backend_sycl_synchronizeは
static void ggml_backend_sycl_synchronize(ggml_backend_t backend) try {
GGML_SYCL_DEBUG("[SYCL] call %s\n", __func__);
ggml_backend_sycl_context * sycl_ctx = (ggml_backend_sycl_context *)backend->context;
const queue_ptr stream = sycl_ctx->stream(sycl_ctx->device, 0);
SYCL_CHECK(CHECK_TRY_ERROR((stream)->wait()));
GGML_UNUSED(backend);
}
と、なっている。が、ざっくりと検索すると
https://www.intel.com/content/www/us/en/developer/articles/guide/oneapi-dpcpp-library-manual-migration.html
でサンプルコードで
Is migrated to:
dpct::queue_ptr stream;
stream = dpct::get_current_device().create_queue();
stream->fill(d_out, oneapi::dpl::reduce(oneapi::dpl::execution::device_policy(*stream),
d_in, d_in + num_items, typename std::iterator_traits<decltype(d_out)>::value_type{}), 1).wait();
となっている。 dpct::queue_ptrって? 分からんのでさらにざっくり検索
dpct queue_ptr stream
https://www.isus.jp/wp-content/uploads/dpct/2023/dpcpp_compatibility_tool_user_guide/diagnostic_ref/dpct1026.html
この中で、 queue_prt は dpct::queue_ptr使えってなってるんだよな…なんだかわからんが、変えてみるか?
書き換えてみて、実際にgrepで目視チェックを行って、漏れがないのを確認し、make中…
[ 23%] Building CXX object ggml/src/ggml-sycl/CMakeFiles/ggml-sycl.dir/template-instances/fattn-vec-instance-q4_1-q8_0.cpp.o
いつも放置してるだけだと気にならなかったけど、結構時間かかる…w
ちなみに上記のリンクのドキュメントを見ると、CUDAのコードから自動変換ツールを使えば9割方は自動的にSYCLのコードにおこしてくれるらしい。まぁこの手のツールは信用できないけどw
ただ、日本語のドキュメントとして存在してくれてるのはありがたい…
テンプレートインスタンスがなげぇ…コンパイル通ってくれるのかな?普通に考えて同期処理なんて1ソースファイルから外には跨がすなんてことはしないと思うのだが…不安だ…
[ 33%] Building C object ggml/src/CMakeFiles/ggml-cpu.dir/ggml-cpu/arch/x86/quants.c.o
[ 33%] Building CXX object ggml/src/CMakeFiles/ggml-cpu.dir/ggml-cpu/arch/x86/repack.cpp.o
[ 34%] Linking CXX static library libggml-cpu.a
[ 34%] Built target ggml-cpu
[ 34%] Building CXX object ggml/src/CMakeFiles/ggml.dir/ggml-backend-dl.cpp.o
[ 34%] Building CXX object ggml/src/CMakeFiles/ggml.dir/ggml-backend-reg.cpp.o
[ 34%] Linking CXX static library libggml.a
[ 34%] Built target ggml
[ 35%] Building CXX object CMakeFiles/stable-diffusion.dir/src/convert.cpp.o
お…ggml抜けた 17:38
[100%] Linking CXX executable ../../bin/sd-server
[100%] Built target sd-server
siriuth@b4turbo-2:~/stable-diffusion.cpp/build-sycl32$
おわったー17:44
さて…実行してみる。sdの-vだけでデバッグログはいいかなとりあえず。
[DEBUG] ggml_extend.hpp:2471 - execure_graph return optput
[DEBUG] stable-diffusion.cpp:1700 - if output_opt.empty()
[DEBUG] stable-diffusion.cpp:1706 - step_cache.after_condition()
[DEBUG] stable-diffusion.cpp:1708 - retrun output_opt
[DEBUG] stable-diffusion.cpp:1727 - if !uncond.empty()
[DEBUG] stable-diffusion.cpp:1741 - if img_cond.empty()
[DEBUG] stable-diffusion.cpp:1749 - is_skiplayer_step
[DEBUG] stable-diffusion.cpp:1753 - if is_skiplayer_step
[DEBUG] stable-diffusion.cpp:1766 - sample sd::Tensor<float> latent_result
|============> | 1/4 - 313.79s/it[DEBUG] stable-diffusion.cpp:1681 - sample auto run_condition
[DEBUG] stable-diffusion.cpp:1712 - if start_merge_step
[DEBUG] stable-diffusion.cpp:1692 - auto run_condition cached_output
[DEBUG] stable-diffusion.cpp:1698 - auto output_opt
[DEBUG] flux.hpp:1481 - sd::Tensor<float> compute() start
[DEBUG] flux.hpp:1487 - sd::Tensor<float> compute() auto get_graph
[DEBUG] flux.hpp:1491 - sd::Tensor<float> compute() auto result
[DEBUG] ggml_extend.hpp:2692 - compute() start
[DEBUG] ggml_extend.hpp:2719 - compute() return execute_graph
[DEBUG] ggml_extend.hpp:2374 - execure_graph start
[DEBUG] ggml_extend.hpp:2414 - call copy_data_to_backend_tensor()
[DEBUG] ggml_extend.hpp:2422 - call ggml_backend_graph_compute()
7分経過。1024x1024の画像を出してるのであれですが…
あ…消費電力が4Wに下がってCPUスレッドが100%に…落ちやがったw
変わってねぇ…
てか、コードが標準っぽい書き方じゃダメっぽいwくっ。
ちまちま書き換えていきますか…とりあえず dpct::queue_ptr が使えそうなのが分かった…
0 件のコメント:
コメントを投稿