Skip to content

Commit 80e9aa4

Browse files
committed
feat: 优化多线程扫描性能和线程池同步
- 改进了多线程特征码扫描的效率,通过在找到结果时提前退出,并返回最小地址。 - 调整了 `scan_dynamic_anchor` 中的 NEON 优化逻辑,以更好地处理边界条件。 - 增强了多线程操作中的内存顺序,以确保更好的内存可见性。 - 优化了线程池的任务调度和等待机制,通过精确跟踪任务数量和使用 `notify_one` 减少不必要的唤醒。
1 parent 5ef77ab commit 80e9aa4

2 files changed

Lines changed: 58 additions & 26 deletions

File tree

include/ur/signature.hpp

Lines changed: 51 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -275,27 +275,38 @@ class runtime_signature : public std::enable_shared_from_this<runtime_signature>
275275
std::optional<uintptr_t> scan_multithreaded(std::span<const std::byte> memory_range, scanner_func_t core_scanner) const {
276276
const unsigned int num_threads = std::thread::hardware_concurrency();
277277
const size_t chunk_size = 65536 * 4;
278-
const size_t min_range_for_multithread = chunk_size * num_threads;
278+
const size_t min_range_for_multithread = chunk_size;
279+
279280
if (num_threads <= 1 || memory_range.size() < min_range_for_multithread) {
280281
return (this->*core_scanner)(memory_range, nullptr);
281282
}
283+
282284
auto& pool = get_pool();
283285
std::vector<std::future<std::optional<uintptr_t>>> futures;
284286
auto found_flag = std::make_shared<std::atomic<bool>>(false);
285287
const size_t overlap = pattern_.size() > 1 ? pattern_.size() - 1 : 0;
288+
286289
for (size_t start = 0; start < memory_range.size(); start += chunk_size) {
290+
if (found_flag->load(std::memory_order_acquire)) {
291+
break;
292+
}
287293
size_t end = std::min(start + chunk_size + overlap, memory_range.size());
288294
if (start >= end || (end - start) < pattern_.size()) continue;
295+
289296
std::span<const std::byte> chunk = memory_range.subspan(start, end - start);
290297
futures.push_back(pool.enqueue(core_scanner, shared_from_this(), chunk, found_flag));
291298
}
299+
300+
std::optional<uintptr_t> first_result;
292301
for (auto& fut : futures) {
293302
if (auto result = fut.get(); result.has_value()) {
294-
found_flag->store(true, std::memory_order_relaxed);
295-
return result;
303+
if (!first_result.has_value() || result.value() < first_result.value()) {
304+
first_result = result;
305+
}
296306
}
297307
}
298-
return std::nullopt;
308+
309+
return first_result;
299310
}
300311
#endif
301312

@@ -416,55 +427,76 @@ namespace detail {
416427

417428
inline std::optional<uintptr_t> runtime_signature::scan_dynamic_anchor(std::span<const std::byte> memory_range, std::shared_ptr<std::atomic<bool>> found_flag) const {
418429
if (memory_range.size() < pattern_.size()) return std::nullopt;
430+
419431
auto frequencies = detail::calculate_dynamic_rarity(memory_range);
420432
auto props = detail::find_best_anchor_and_build_props(pattern_, frequencies);
421433
if (!props.has_anchor) {
422434
return scan_forward_anchor(memory_range, found_flag);
423435
}
436+
424437
const uint8x16_t v_anchor = vdupq_n_u8(static_cast<uint8_t>(props.anchor_byte));
425438
const uint8x16_t v_pattern16 = vld1q_u8(reinterpret_cast<const uint8_t*>(props.pattern16.data()));
426439
const uint8x16_t v_mask16 = vld1q_u8(reinterpret_cast<const uint8_t*>(props.mask16.data()));
440+
427441
const std::byte* current_pos = memory_range.data();
428-
const std::byte* const end_pos = memory_range.data() + memory_range.size() - pattern_.size();
429-
const std::byte* const fast_scan_end_pos = memory_range.data() + memory_range.size() - 16;
442+
const std::byte* const range_end = memory_range.data() + memory_range.size();
443+
const std::byte* const end_pos = range_end - pattern_.size();
444+
const std::byte* const fast_scan_end_pos = (range_end >= memory_range.data() + 16) ? range_end - 16 : memory_range.data();
445+
430446
while (current_pos <= fast_scan_end_pos) {
431-
if (found_flag && found_flag->load(std::memory_order_relaxed)) return std::nullopt;
447+
if (found_flag && found_flag->load(std::memory_order_acquire)) return std::nullopt;
448+
432449
#ifdef UR_ENABLE_HARDWARE_PREFETCH
433450
__builtin_prefetch(current_pos + 64, 0, 0);
434451
#endif
435452
const uint8x16_t v_mem = vld1q_u8(reinterpret_cast<const uint8_t*>(current_pos));
436453
const uint8x16_t v_cmp_result = vceqq_u8(v_mem, v_anchor);
454+
437455
if (vmaxvq_u8(v_cmp_result) == 0) {
438456
current_pos += 16;
439457
continue;
440458
}
459+
441460
uint8_t result_bytes[16];
442461
vst1q_u8(result_bytes, v_cmp_result);
443462
for (int i = 0; i < 16; ++i) {
444463
if (result_bytes[i] == 0xFF) {
445464
const std::byte* potential_start = current_pos + i - props.anchor_offset;
446465
if (potential_start < memory_range.data() || potential_start > end_pos) continue;
447-
const uint8x16_t v_mem16 = vld1q_u8(reinterpret_cast<const uint8_t*>(potential_start));
448-
const uint8x16_t v_masked_mem = vandq_u8(v_mem16, v_mask16);
449-
const uint8x16_t v_verify_result = vceqq_u8(v_masked_mem, v_pattern16);
450-
if (vminvq_u8(v_verify_result) == 0xFF) {
451-
if (pattern_.size() <= 16 || full_match_at(potential_start)) {
452-
if (found_flag) found_flag->store(true, std::memory_order_relaxed);
466+
if (potential_start + pattern_.size() > range_end) continue;
467+
468+
if (potential_start + 16 <= range_end) {
469+
const uint8x16_t v_mem16 = vld1q_u8(reinterpret_cast<const uint8_t*>(potential_start));
470+
const uint8x16_t v_masked_mem = vandq_u8(v_mem16, v_mask16);
471+
const uint8x16_t v_verify_result = vceqq_u8(v_masked_mem, v_pattern16);
472+
473+
if (vminvq_u8(v_verify_result) == 0xFF) {
474+
if (pattern_.size() <= 16 || full_match_at(potential_start)) {
475+
if (found_flag) found_flag->store(true, std::memory_order_release);
476+
return reinterpret_cast<uintptr_t>(potential_start);
477+
}
478+
}
479+
} else {
480+
if (full_match_at(potential_start)) {
481+
if (found_flag) found_flag->store(true, std::memory_order_release);
453482
return reinterpret_cast<uintptr_t>(potential_start);
454483
}
455484
}
456485
}
457486
}
458487
current_pos += 16;
459488
}
460-
std::span<const std::byte> tail_span{current_pos, static_cast<size_t>(memory_range.data() + memory_range.size() - current_pos)};
461-
const auto scan_end_tail = tail_span.data() + tail_span.size() - pattern_.size();
462-
for (const std::byte* p = tail_span.data(); p <= scan_end_tail; ++p) {
463-
if (full_match_at(p)) {
464-
if (found_flag) found_flag->store(true, std::memory_order_relaxed);
465-
return reinterpret_cast<uintptr_t>(p);
489+
490+
const auto scan_end_tail = range_end - pattern_.size();
491+
while(current_pos <= scan_end_tail) {
492+
if (found_flag && found_flag->load(std::memory_order_acquire)) return std::nullopt;
493+
if (full_match_at(current_pos)) {
494+
if (found_flag) found_flag->store(true, std::memory_order_release);
495+
return reinterpret_cast<uintptr_t>(current_pos);
466496
}
497+
current_pos++;
467498
}
499+
468500
return std::nullopt;
469501
}
470502
#endif

include/ur/thread_pool.hpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,13 @@ class ThreadPool {
112112
throw std::runtime_error("enqueue on stopped ThreadPool");
113113
}
114114

115+
tasks_in_flight_.fetch_add(1);
116+
115117
// Distribute tasks to worker queues round-robin
116118
size_t queue_idx = submission_idx_.fetch_add(1) % thread_count_;
117119
queues_[queue_idx].push([task]() { (*task)(); });
118120

119-
condition_.notify_all();
121+
condition_.notify_one();
120122
return res;
121123
}
122124

@@ -129,6 +131,7 @@ class ThreadPool {
129131

130132
// First, try to pop a task from our own queue.
131133
if (queues_[id].pop(task)) {
134+
tasks_in_flight_.fetch_sub(1);
132135
task();
133136
continue;
134137
}
@@ -137,6 +140,7 @@ class ThreadPool {
137140
bool stolen = false;
138141
for (size_t i = 1; i < thread_count_; ++i) {
139142
if (queues_[(id + i) % thread_count_].steal(task)) {
143+
tasks_in_flight_.fetch_sub(1);
140144
stolen = true;
141145
break;
142146
}
@@ -148,12 +152,7 @@ class ThreadPool {
148152
// If no task was found, wait for a notification.
149153
std::unique_lock<std::mutex> lock(wait_mutex_);
150154
condition_.wait(lock, [this] {
151-
if (stop_.load()) return true;
152-
// Wake up if any queue has tasks. This prevents missed notifications.
153-
for (size_t i = 0; i < thread_count_; ++i) {
154-
if (!queues_[i].empty()) return true;
155-
}
156-
return false;
155+
return stop_.load() || tasks_in_flight_.load() > 0;
157156
});
158157
}
159158
}
@@ -165,6 +164,7 @@ class ThreadPool {
165164

166165
std::atomic<bool> stop_;
167166
std::atomic<size_t> submission_idx_{0};
167+
std::atomic<size_t> tasks_in_flight_{0};
168168

169169
std::mutex wait_mutex_;
170170
std::condition_variable condition_;

0 commit comments

Comments
 (0)