Conversation
|
I am testing this branch with the following patch in order to cause a synchronization issue: diff --git a/ggml/src/ggml-cuda/ggml-cuda.cu b/ggml/src/ggml-cuda/ggml-cuda.cu
index e73a7b890..b9f449ac1 100644
--- a/ggml/src/ggml-cuda/ggml-cuda.cu
+++ b/ggml/src/ggml-cuda/ggml-cuda.cu
@@ -2468,10 +2468,10 @@ static bool ggml_backend_cuda_cpy_tensor_async(ggml_backend_t backend_src, ggml_
CUDA_CHECK(cudaEventCreateWithFlags(&cuda_ctx_src->copy_event, cudaEventDisableTiming));
}
- CUDA_CHECK(cudaEventRecord(cuda_ctx_src->copy_event, cuda_ctx_src->stream()));
+ //CUDA_CHECK(cudaEventRecord(cuda_ctx_src->copy_event, cuda_ctx_src->stream()));
// wait on dst stream for the copy to complete
- CUDA_CHECK(cudaStreamWaitEvent(cuda_ctx_dst->stream(), cuda_ctx_src->copy_event, 0));
+ //CUDA_CHECK(cudaStreamWaitEvent(cuda_ctx_dst->stream(), cuda_ctx_src->copy_event, 0));
} else {
// src and dst are on the same backend
CUDA_CHECK(cudaMemcpyAsync(dst->data, src->data, ggml_nbytes(dst), cudaMemcpyDeviceToDevice, cuda_ctx_src->stream()));Command: make -j && GGML_SCHED_SANITIZE=2 GGML_CUDA_DEVICES=4 ./bin/llama-perplexity -hf ggml-org/qwen3-0.6b-gguf:Q8_0 -f ./wikitext-2-raw/wiki.test.raw --chunks 16 -sm layer -dev CUDA0,CUDA1,CUDA2,CUDA3The sanitizer does not report any issue. Is this expected? |
|
Yes that's expected because the backend after this patch is incorrectly implementing the cpy_async. This sanitizer cannot catch those cases, it assumes the functions are implemented correctly. It will be able to catch all the event sync logic used in |
|
I was able to introduce a race in #21067 and the sanitizer was able to catch it. |
| backend->iface.event_record(backend, event); | ||
| ggml_san_event_record(event, backend); |
There was a problem hiding this comment.
The main concern for me is that implementing it like this, it is very prone for errors in the future where we forget to add the respective sanitizer call. Instead, it should be seamless - we don't have to remember to call the sanitizer every time we use the backend interface.
There was a problem hiding this comment.
I see, for actually seamless integration we would need to override backend->iface calls which do the sanitize and then call the actual function. Let me see how feasible that is
There was a problem hiding this comment.
we would need to override backend->iface calls
Or wrap them
There was a problem hiding this comment.
wrapping is not seamless, as the user can still call backend->iface directly?
There was a problem hiding this comment.
If we want just the scheduler logic to be sanitized, the scheduler can wrap them. If we want to sanitize even user calls, then we probably need a "sanitize backend" that wraps a backend.
ea681b5 to
0b56150
Compare
| san_state & state() { | ||
| static san_state * s = []() { | ||
| san_state * st = new san_state(); | ||
| st->actor_names.push_back("HOST"); | ||
| st->vc.emplace_back(SAN_MAX_ACTORS, 0); | ||
| return st; | ||
| }(); | ||
| return *s; | ||
| } |
There was a problem hiding this comment.
While it should be fine from a llama.cpp standpoint, from a ggml standpoint I think it is undesirable to have a global state like this.
|
I have not yet been able to do a full review but based on what I've read so far I think the code should be organized differently:
|
|
Yes good suggestions by @JohannesGaessler. Also, design the code in such a way that in the future we can implement other sanitizers. To achieve that, have a minimal internal scheduler callback API (f.ex declared in |
Overview
Add a sanitizer which catches "happens-before" races in the scheduler. To run you can use
GGML_SCHED_SANITIZE=1which would crash if finds a race.How it works - it takes the idea from Vector Clocks to identify happens-before relations. The idea is there is a race condition when there is an operation on a memory range M -
ggml_backend_t)clock_view[current][prev] < clock[prev])Additional information
ggml_cpy_asyncis inconsistent in ggml, we need to clarify the contract there.Requirements