C++ 并发性能优化实战:提升多线程应用的效率与稳定性
🧑 博主简介:CSDN博客专家、CSDN平台优质创作者,获得2024年博客之星荣誉证书,高级开发工程师,数学专业,拥有高级工程师证书;擅长C/C++、C#等开发语言,熟悉Java常用开发技术,能熟练应用常用数据库SQL server,Oracle,mysql,postgresql等进行开发应用,熟悉DICOM医学影像及DICOM协议,业余时间自学JavaScript,Vue,qt,python等,具备多种混合语言开发能力。撰写博客分享知识,致力于帮助编程爱好者共同进步。欢迎关注、交流及合作,提供技术支持与解决方案。
技术合作请加本人wx(注明来自csdn):xt20160813
C++ 并发性能优化实战:提升多线程应用的效率与稳定性
在现代软件开发中,多核处理器的普及使得并发编程成为提升应用性能的关键手段。C++ 作为一门高性能语言,提供了丰富的并发支持,但不当的使用同样可能导致性能瓶颈甚至程序错误。本文将深入探讨 C++ 并发性能优化的策略和实践,通过详细的示例,帮助开发者在项目中有效识别并解决并发带来的性能问题。
目录
- 并发编程基础
- 什么是并发与并行
- C++ 中的并发支持
- 识别并发性能瓶颈
- 常见的并发性能问题
- 性能分析工具
- 优化策略
- 1. 减少锁的粒度与使用
- 2. 使用无锁编程
- 3. 线程池的应用
- 4. 数据局部性优化
- 5. 避免竞态条件与死锁
- 6. 任务划分与负载均衡
- 7. 内存管理与缓存优化
- 实战案例:高性能并行图像处理
- 初始实现
- 优化步骤
- 优化后的实现
- 最佳实践与总结
- 参考资料
并发编程基础
什么是并发与并行
并发(Concurrency)指的是在同一时间段内,多个任务交替执行,以提高系统的吞吐量和资源利用率。而并行(Parallelism)则是指在同一时刻,多个任务同时执行,以缩短任务完成时间。虽然两者密切相关,但并发更强调任务的管理与调度,并行则强调同时执行。
C++中的并发支持
自 C++11 起,C++ 标准库引入了一系列并发支持,包括线程(std::thread
)、互斥锁(std::mutex
)、条件变量(std::condition_variable
)等。此外,C++17 引入了并行算法,C++20 更进一步增强了协程(Coroutines)等特性。这些工具为开发者提供了构建高性能并发应用的基础。
识别并发性能瓶颈
在优化并发程序之前,首先需要识别性能瓶颈。以下是常见的并发性能问题和识别方法。
常见的并发性能问题
- 过度锁竞争:多个线程频繁争用同一把锁,导致线程阻塞和上下文切换,降低系统吞吐量。
- 任务划分不合理:任务粒度过细或过粗,导致线程管理开销增加或资源利用率降低。
- 线程过多或过少:线程数量不匹配硬件资源,导致 CPU 核心空闲或频繁上下文切换。
- 缓存不友好:数据结构和访问模式导致缓存未命中率高,增加内存访问延迟。
- 死锁与竞态条件:不当的同步机制导致线程间相互等待或数据不一致。
性能分析工具
使用性能分析工具可以有效发现并发程序中的性能瓶颈。以下是几种常用的工具:
- Perf:Linux 系统下的强大性能分析工具,适用于 CPU 性能监控和分析。
- Valgrind:特别是 Callgrind 模块,可以进行详细的代码性能分析。
- Intel VTune Profiler:提供全面的性能分析,支持多种硬件架构。
- Visual Studio Profiler:集成在 Visual Studio 中,适用于 Windows 平台的性能分析。
- Google PerfTools:包括 CPU Profiler,可用于分析程序的 CPU 使用情况。
示例:使用 Perf 进行分析
-
编译程序时开启调试信息和优化选项
g++ -O2 -g -o my_app my_app.cpp -pthread
-
运行 Perf 进行性能分析
perf record -g ./my_app
-
生成报告
perf report
通过分析报告,可以识别出程序中消耗 CPU 时间较多的函数和代码段,进而定位性能瓶颈。
优化策略
针对上述常见的并发性能问题,以下是几种有效的优化策略。
1. 减少锁的粒度与使用
锁粒度指的是锁定的资源范围。锁粒度越细,允许的并发度越高,但管理锁的开销也可能增加。
优化方法:
- 细化锁粒度:将一个大锁拆分为多个小锁,锁定更具体的资源。
- 使用读写锁:对于读多写少的场景,使用共享锁(读锁)和独占锁(写锁)来提高并发度。
- 避免锁嵌套:尽量减少多个锁的嵌套使用,避免死锁风险。
示例:细化锁粒度
#include <vector>
#include <mutex>
#include <thread>class ThreadSafeVector {
public:void push_back(int value) {std::lock_guard<std::mutex> lock(mutex_);data_.push_back(value);}int get(size_t index) const {std::lock_guard<std::mutex> lock(mutex_);return data_.at(index);}private:std::vector<int> data_;mutable std::mutex mutex_;
};
优化:
将整个容器的锁拆分为多个段锁,每个段锁保护容器的一部分。
#include <vector>
#include <mutex>
#include <thread>
#include <shared_mutex>class SegmentedThreadSafeVector {
public:void push_back(int value) {std::unique_lock<std::mutex> lock(mutex_);data_.push_back(value);}int get(size_t index) const {std::unique_lock<std::mutex> lock(mutex_);return data_.at(index);}private:std::vector<int> data_;mutable std::mutex mutex_;
};
尽管在这个简单示例中锁粒度优化效果有限,但在复杂数据结构中,细化锁粒度可以显著提升并发性能。
2. 使用无锁编程
无锁编程通过原子操作和无锁数据结构,避免使用互斥锁,从而减少锁竞争和上下文切换的开销。
优化方法:
- 原子操作:使用
std::atomic
提供的原子操作,确保线程安全的同时避免锁的开销。 - 无锁数据结构:采用无锁队列、无锁栈等数据结构,提高并发性能。
示例:使用原子变量
#include <atomic>
#include <thread>
#include <vector>
#include <iostream>std::atomic<int> counter(0);void increment(int num_iterations) {for(int i = 0; i < num_iterations; ++i) {counter.fetch_add(1, std::memory_order_relaxed);}
}int main() {const int num_threads = 4;const int iterations = 1000000;std::vector<std::thread> threads;for(int i = 0; i < num_threads; ++i) {threads.emplace_back(increment, iterations);}for(auto& t : threads) {t.join();}std::cout << "Final counter value: " << counter.load() << std::endl;return 0;
}
说明:
通过使用 std::atomic<int>
,多个线程可以安全地对 counter
进行递增操作,无需互斥锁,显著提升性能。
3. 线程池的应用
频繁创建和销毁线程会带来较大的开销。使用线程池可以重用线程资源,减少线程管理的开销,提高任务处理效率。
优化方法:
- 固定大小线程池:预先创建一定数量的线程,处理任务队列中的任务。
- 动态调整线程池:根据任务负载动态调整线程池的大小,优化资源利用。
示例:简单线程池实现
#include <vector>
#include <thread>
#include <queue>
#include <functional>
#include <mutex>
#include <condition_variable>
#include <future>
#include <iostream>class ThreadPool {
public:ThreadPool(size_t num_threads);~ThreadPool();// 提交任务template<class F, class... Args>auto enqueue(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type>;private:// 工作者线程std::vector<std::thread> workers_;// 任务队列std::queue<std::function<void()>> tasks_;// 同步std::mutex queue_mutex_;std::condition_variable condition_;bool stop_;
};// 构造函数
ThreadPool::ThreadPool(size_t num_threads) : stop_(false) {for(size_t i = 0; i < num_threads; ++i) {workers_.emplace_back([this]() {while(true) {std::function<void()> task;{ // 获取任务std::unique_lock<std::mutex> lock(this->queue_mutex_);this->condition_.wait(lock, [this]() { return this->stop_ || !this->tasks_.empty(); });if(this->stop_ && this->tasks_.empty())return;task = std::move(this->tasks_.front());this->tasks_.pop();}// 执行任务task();}});}
}// 析构函数
ThreadPool::~ThreadPool() {{ std::unique_lock<std::mutex> lock(queue_mutex_);stop_ = true;}condition_.notify_all();for(std::thread &worker: workers_)worker.join();
}// 提交任务
template<class F, class... Args>
auto ThreadPool::enqueue(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type> {using return_type = typename std::result_of<F(Args...)>::type;auto task = std::make_shared< std::packaged_task<return_type()> >(std::bind(std::forward<F>(f), std::forward<Args>(args)...));std::future<return_type> res = task->get_future();{ std::unique_lock<std::mutex> lock(queue_mutex_);// 不允许在停止线程池后提交任务if(stop_)throw std::runtime_error("enqueue on stopped ThreadPool");tasks_.emplace([task]() { (*task)(); });}condition_.notify_one();return res;
}// 使用示例
int main() {ThreadPool pool(4);std::vector<std::future<int>> results;// 提交任务for(int i = 0; i < 8; ++i) {results.emplace_back(pool.enqueue([i]() -> int {std::this_thread::sleep_for(std::chrono::milliseconds(100));return i*i;}));}// 获取结果for(auto && result: results)std::cout << result.get() << ' ';std::cout << std::endl;return 0;
}
说明:
通过线程池,多个任务可以复用固定数量的线程执行,避免了频繁创建和销毁线程的开销,提升了并发性能。
4. 数据局部性优化
数据局部性指的是数据在内存中的分布对缓存性能的影响。在并发程序中,优化数据的缓存局部性,可以减少缓存未命中率,提高内存访问速度。
优化方法:
- 结构化数据存储:使用结构体数组(SoA)而非数组结构体(AoS),提高数据的连续性。
- 避免伪共享:不同线程访问的数据不应位于同一个缓存行,避免伪共享导致的性能下降。
示例:避免伪共享
#include <atomic>
#include <thread>
#include <vector>
#include <iostream>// 伪共享示例
struct SharedData {std::atomic<int> counter1;std::atomic<int> counter2;
};int main() {SharedData data;data.counter1 = 0;data.counter2 = 0;auto increment1 = [&data]() {for(int i = 0; i < 1000000; ++i) {data.counter1.fetch_add(1, std::memory_order_relaxed);}};auto increment2 = [&data]() {for(int i = 0; i < 1000000; ++i) {data.counter2.fetch_add(1, std::memory_order_relaxed);}};std::thread t1(increment1);std::thread t2(increment2);t1.join();t2.join();std::cout << "Counter1: " << data.counter1 << "\nCounter2: " << data.counter2 << std::endl;return 0;
}
优化:
通过填充无用数据避免 counter1
和 counter2
位于同一缓存行。
#include <atomic>
#include <thread>
#include <vector>
#include <iostream>// 避免伪共享的结构
struct SharedData {alignas(64) std::atomic<int> counter1;alignas(64) std::atomic<int> counter2;
};int main() {SharedData data;data.counter1 = 0;data.counter2 = 0;auto increment1 = [&data]() {for(int i = 0; i < 1000000; ++i) {data.counter1.fetch_add(1, std::memory_order_relaxed);}};auto increment2 = [&data]() {for(int i = 0; i < 1000000; ++i) {data.counter2.fetch_add(1, std::memory_order_relaxed);}};std::thread t1(increment1);std::thread t2(increment2);t1.join();t2.join();std::cout << "Counter1: " << data.counter1 << "\nCounter2: " << data.counter2 << std::endl;return 0;
}
说明:
通过使用 alignas(64)
,确保每个计数器位于不同的缓存行,避免多个线程同时访问相邻数据导致的伪共享问题。
5. 避免竞态条件与死锁
竞态条件和死锁不仅会导致程序错误,还会显著影响性能。良好的同步机制设计可以避免这些问题。
优化方法:
- 锁的获取顺序一致:确保多个线程获取多个锁的顺序一致,避免循环等待导致的死锁。
- 使用更高层次的同步机制:如使用条件变量、读写锁等,减少锁的争用。
示例:避免死锁的锁获取顺序
#include <mutex>
#include <thread>
#include <iostream>std::mutex mutex1;
std::mutex mutex2;void thread_a() {std::lock_guard<std::mutex> lock1(mutex1);std::lock_guard<std::mutex> lock2(mutex2);std::cout << "Thread A acquired both locks\n";
}void thread_b() {std::lock_guard<std::mutex> lock1(mutex1);std::lock_guard<std::mutex> lock2(mutex2);std::cout << "Thread B acquired both locks\n";
}int main() {std::thread t1(thread_a);std::thread t2(thread_b);t1.join();t2.join();return 0;
}
说明:
通过确保所有线程以相同的顺序获取锁,可以避免死锁的发生。
6. 任务划分与负载均衡
合理的任务划分和负载均衡可以确保所有线程都能充分利用 CPU 资源,避免某些线程空闲而其他线程过载。
优化方法:
- 动态任务调度:使用工作窃取(Work Stealing)等策略,动态调整各线程的任务负载。
- 合理划分任务粒度:任务粒度应适中,过细增加调度开销,过粗导致负载不均。
示例:使用线程池进行动态任务调度
在前述线程池示例中,任务被动态分配到空闲线程上,实现了负载均衡。
7. 内存管理与缓存优化
高效的内存管理和缓存优化可以显著减少内存访问延迟,提升并发程序的整体性能。
优化方法:
- 内存对齐:确保数据结构按照缓存行对齐,减少缓存未命中率。
- 预分配内存:提前分配必要的内存,避免在高并发时进行频繁内存分配。
- 使用缓存友好的数据结构:如数组和连续内存布局的数据结构,提升缓存局部性。
示例:使用内存池进行内存管理
#include <memory>
#include <vector>
#include <iostream>template<typename T>
class MemoryPool {
public:MemoryPool(size_t size = 1024) {allocate_block(size);}~MemoryPool() {for(auto block : blocks_)::operator delete[](block);}T* allocate() {if(free_list_.empty()) {allocate_block(block_size_);}T* obj = free_list_.back();free_list_.pop_back();return obj;}void deallocate(T* obj) {free_list_.push_back(obj);}private:void allocate_block(size_t size) {T* new_block = static_cast<T*>(::operator new[](size * sizeof(T)));blocks_.push_back(new_block);for(size_t i = 0; i < size; ++i)free_list_.push_back(new_block + i);}std::vector<T*> blocks_;std::vector<T*> free_list_;size_t block_size_ = 1024;
};// 使用示例
struct MyObject {int data;// ...
};int main() {MemoryPool<MyObject> pool;// 分配对象MyObject* obj1 = pool.allocate();obj1->data = 42;// 使用对象std::cout << "Object data: " << obj1->data << std::endl;// 释放对象pool.deallocate(obj1);return 0;
}
说明:
通过内存池管理对象的分配和释放,减少了频繁的堆分配操作,提高了内存管理效率,特别适用于高并发环境下的大量对象创建与销毁。
实战案例:高性能并行图像处理
为了更直观地展示上述优化策略的应用,以下将通过一个高性能并行图像处理的案例,详细说明优化过程。
初始实现
假设有一个简单的图像处理程序,需要对一幅大图像的每个像素进行亮度调整。
#include <vector>
#include <thread>
#include <mutex>
#include <iostream>struct Pixel {unsigned char r, g, b;
};class Image {
public:Image(size_t width, size_t height) : width_(width), height_(height), pixels_(width * height) {}Pixel& at(size_t x, size_t y) { return pixels_[y * width_ + x]; }size_t width() const { return width_; }size_t height() const { return height_; }private:size_t width_;size_t height_;std::vector<Pixel> pixels_;
};void adjust_brightness(Image& img, size_t start_y, size_t end_y, int brightness) {for(size_t y = start_y; y < end_y; ++y) {for(size_t x = 0; x < img.width(); ++x) {Pixel& p = img.at(x, y);p.r = std::min(static_cast<int>(p.r) + brightness, 255);p.g = std::min(static_cast<int>(p.g) + brightness, 255);p.b = std::min(static_cast<int>(p.b) + brightness, 255);}}
}int main() {size_t width = 4000;size_t height = 3000;Image img(width, height);// 初始化图像数据(简化)for(auto& p : img.pixels_) {p.r = p.g = p.b = 100;}int brightness = 50;size_t num_threads = std::thread::hardware_concurrency();std::vector<std::thread> threads;size_t rows_per_thread = height / num_threads;for(size_t i = 0; i < num_threads; ++i) {size_t start_y = i * rows_per_thread;size_t end_y = (i == num_threads - 1) ? height : (i + 1) * rows_per_thread;threads.emplace_back(adjust_brightness, std::ref(img), start_y, end_y, brightness);}for(auto& t : threads) {t.join();}std::cout << "Brightness adjustment completed.\n";return 0;
}
潜在问题:
- 锁的使用:在当前实现中,没有显式的锁,但如果在调整亮度时需要修改共享数据结构,可能引入锁。
- 数据局部性:访问图像像素的顺序若不连续,可能影响缓存命中率。
- 线程管理开销:频繁创建和销毁线程可能带来额外开销。
优化步骤
针对上述问题,可以进行以下优化:
- 使用线程池:避免频繁创建和销毁线程,通过线程池管理线程资源。
- 提高数据局部性:确保线程访问的内存范围连续,提高缓存命中率。
- 减少内存访问冲突:确保每个线程操作独立的图像区域,避免数据竞争。
优化后的实现
#include <vector>
#include <thread>
#include <mutex>
#include <iostream>
#include <future>
#include <algorithm>// 保持 Pixel 和 Image 结构不变
struct Pixel {unsigned char r, g, b;
};class Image {
public:Image(size_t width, size_t height) : width_(width), height_(height), pixels_(width * height) {}Pixel& at(size_t x, size_t y) { return pixels_[y * width_ + x]; }size_t width() const { return width_; }size_t height() const { return height_; }std::vector<Pixel>& get_pixels() { return pixels_; }private:size_t width_;size_t height_;std::vector<Pixel> pixels_;
};// 线程池类(简化)
class ThreadPool {
public:ThreadPool(size_t num_threads);~ThreadPool();template<class F>auto enqueue(F&& f) -> std::future<void>;private:std::vector<std::thread> workers_;std::queue<std::function<void()>> tasks_;std::mutex queue_mutex_;std::condition_variable condition_;bool stop_;
};// 线程池实现
ThreadPool::ThreadPool(size_t num_threads) : stop_(false) {for(size_t i = 0; i < num_threads; ++i) {workers_.emplace_back([this]() {while(true) {std::function<void()> task;{ std::unique_lock<std::mutex> lock(this->queue_mutex_);this->condition_.wait(lock, [this]() { return this->stop_ || !this->tasks_.empty(); });if(this->stop_ && this->tasks_.empty())return;task = std::move(this->tasks_.front());this->tasks_.pop();}task();}});}
}ThreadPool::~ThreadPool() {{ std::unique_lock<std::mutex> lock(queue_mutex_);stop_ = true;}condition_.notify_all();for(std::thread &worker: workers_)worker.join();
}template<class F>
auto ThreadPool::enqueue(F&& f) -> std::future<void> {auto task = std::make_shared< std::packaged_task<void()> >(std::forward<F>(f));std::future<void> res = task->get_future();{ std::unique_lock<std::mutex> lock(queue_mutex_);if(stop_)throw std::runtime_error("enqueue on stopped ThreadPool");tasks_.emplace([task]() { (*task)(); });}condition_.notify_one();return res;
}// 调整亮度函数
void adjust_brightness(Image& img, size_t start_y, size_t end_y, int brightness) {for(size_t y = start_y; y < end_y; ++y) {for(size_t x = 0; x < img.width(); ++x) {Pixel& p = img.at(x, y);p.r = std::min(static_cast<int>(p.r) + brightness, 255);p.g = std::min(static_cast<int>(p.g) + brightness, 255);p.b = std::min(static_cast<int>(p.b) + brightness, 255);}}
}int main() {size_t width = 4000;size_t height = 3000;Image img(width, height);// 初始化图像数据(简化)std::fill(img.get_pixels().begin(), img.get_pixels().end(), Pixel{100, 100, 100});int brightness = 50;size_t num_threads = std::thread::hardware_concurrency();ThreadPool pool(num_threads);std::vector< std::future<void> > futures;size_t rows_per_task = height / (num_threads * 4); // 分成更多任务for(size_t y = 0; y < height; y += rows_per_task) {size_t end_y = std::min(y + rows_per_task, height);futures.emplace_back(pool.enqueue([&img, y, end_y, brightness]() {adjust_brightness(img, y, end_y, brightness);}));}// 等待所有任务完成for(auto &fut : futures)fut.get();std::cout << "Brightness adjustment completed.\n";return 0;
}
优化效果分析:
- 使用线程池:通过线程池复用线程资源,减少了频繁创建和销毁线程的开销,提高了任务调度效率。
- 任务划分更细:将图像划分为更多的任务,使得线程可以更均匀地分配工作,避免某些线程过载而其他线程空闲。
- 数据局部性提升:每个任务处理连续的图像行,提升了内存访问的连续性和缓存命中率。
- 避免数据竞争:每个任务处理独立的图像区域,无需额外的同步机制,减少锁的使用和争用。
最佳实践与总结
通过上述优化策略和实战案例,我们可以总结出以下 C++ 并发性能优化的最佳实践:
- 合理使用锁:尽量减少锁的使用,细化锁的粒度,采用读写锁等高级同步机制,避免锁竞争和死锁。
- 采用无锁编程:利用原子操作和无锁数据结构,提升并发性能,减少上下文切换的开销。
- 使用线程池:避免频繁创建和销毁线程,通过线程池管理线程资源,提高任务处理效率。
- 优化数据局部性:确保数据访问的连续性和缓存友好性,减少缓存未命中率,提升内存访问速度。
- 任务划分合理:合理划分任务粒度,确保所有线程负载均衡,避免资源闲置和过载。
- 避免数据竞争:设计线程间数据访问的独立性,减少共享数据和同步需求,避免竞态条件。
- 进行性能分析:使用性能分析工具定位并发程序中的性能瓶颈,针对性地进行优化。
- 遵循并发模型:采用成熟的并发编程模型和设计模式,如生产者-消费者模式、任务并行等,提高代码的可维护性和性能。
总结:
C++ 并发编程在提升应用性能方面具有巨大潜力,但同时也带来了复杂性和挑战。通过理解并发基础、识别性能瓶颈、应用有效的优化策略,开发者可以构建高效、稳定的并发应用。最重要的是,持续进行性能分析和优化,确保应用在不同负载和环境下都能表现出色。
参考资料
- C++ 并发编程官方文档
- C++ Concurrency in Action - Anthony Williams
- Effective Modern C++ - Scott Meyers
- Intel VTune Profiler 文档
- Google PerfTools
标签
C++、并发编程、性能优化、多线程、线程池、无锁编程
版权声明
本文版权归作者所有,未经允许,请勿转载。
相关文章:
C++ 并发性能优化实战:提升多线程应用的效率与稳定性
🧑 博主简介:CSDN博客专家、CSDN平台优质创作者,获得2024年博客之星荣誉证书,高级开发工程师,数学专业,拥有高级工程师证书;擅长C/C、C#等开发语言,熟悉Java常用开发技术,…...
前端性能优化的全方位方案【待进一步结合项目】
以下是前端性能优化的全方位方案,结合代码配置和最佳实践,涵盖从代码编写到部署的全流程优化: 一、代码层面优化 1. HTML结构优化 <!-- 语义化标签减少嵌套 --> <header><nav>...</nav> </header> <main&…...
(undone) 并行计算 CS149 Lecture3 (现代多核处理器2 + ISPC编程抽象)
url: https://www.bilibili.com/video/BV1du17YfE5G?spm_id_from333.788.videopod.sections&vd_source7a1a0bc74158c6993c7355c5490fc600&p3 如上堂课,超线程技术通过储存不同线程的 execution context,能够在一个线程等待 IO 的时候低成本切换…...
DiffAD:自动驾驶的统一扩散建模方法
25年3月来自新加坡公司 Carion 和北航的论文“DiffAD: A Unified Diffusion Modeling Approach for Autonomous Driving”。 端到端自动驾驶 (E2E-AD) 已迅速成为实现完全自动驾驶的一种有前途的方法。然而,现有的 E2E-AD 系统通常采用传统的多任务框架,…...
QScrollArea 内部滚动条 QSS 样式失效问题及解决方案
在使用 Qt 进行 UI 开发时,我们经常希望通过 QSS(Qt Style Sheets)自定义控件的外观,比如为 QScrollArea 的内部滚动条设置特定的样式。然而,有开发者遇到了这样的问题:在 UI 设计器中预览 QSS 显示效果正常,但程序运行时却显示为系统默认样式。经过反复测试和调试,最终…...
换脸视频FaceFusion3.1.0-附整合包
2025版最强换脸软件FaceFusion来了(附整合包)超变态的AI换脸教程 2025版最强换脸软件FaceFusion来了(附整合包)超变态的AI换脸教程 整合包地址: 「Facefusion_V3.1.0」 链接:https://pan.quark.cn/s/f71601…...
Qt 入门 1 之第一个程序 Hello World
Qt 入门1之第一个程序 Hello World 直接上操作步骤从头开始认识,打开Qt Creator,创建一个新项目,并依次执行以下操作 在Qt Creator中,一个Kits 表示一个完整的构建环境,包括编译器、Qt版本、调试器等。在上图中可以直…...
无锁队列简介与实现示例
1. 简介 无锁队列是一种数据结构,旨在在多线程环境中实现高效的并发访问,而无需使用传统的锁机制(如互斥锁)。无锁队列通过使用原子操作(如CAS,Compare-And-Swap)来确保线程安全,从…...
SpringMVC与SpringCloud的区别
SpringMVC与SpringCloud的核心区别 功能定位 • SpringMVC: 基于Spring框架的Web层开发模块,采用MVC(Model-View-Controller)模式,专注于处理HTTP请求、路由分发(如DispatcherServlet)和视图…...
STM32F103C8T6单片机开发:简单说说单片机的外部GPIO中断(标准库)
目录 前言 如何使用STM32F1系列的标准库完成外部中断的抽象 初始化我们的GPIO为输入的一个模式 初识GPIO复用,开启GPIO的复用功能时钟 GPIO_EXTILineConfig和EXTI_Init配置外部中断参数 插入一个小知识——如何正确的配置结构体? 初始化中断&#…...
Python urllib3 全面指南:从基础到实战应用
欢迎来到涛涛的频道,今天用到了urllib3,和大家分享下。 1、介绍 urllib3 urllib3 是 Python 中一个功能强大且用户友好的 HTTP 客户端库,它提供了许多标准库 urllib 所不具备的高级特性。作为 Python 生态中最受欢迎的 HTTP 库之一…...
25.5 GLM-4优化RAG实战:0.1%参数实现准确率飙升30%,成本直降90%!
使用 GLM-4 优化 RAG 程序:基于标注数据的 Adapter 训练实战 关键词:GLM-4 优化, RAG 增强, 数据标注, Adapter 训练, 检索增强生成 1. RAG 系统的核心挑战与优化方向 传统 RAG(Retrieval-Augmented Generation)系统常面临以下瓶颈: graph LR A[用户提问] --> B[检…...
OrangePi入门教程(待更新)
快速上手指南 https://www.hiascend.com/developer/techArticles/20240301-1?envFlag1 教学课程(含开发板配置和推理应用开发) https://www.hiascend.com/developer/devboard 开发推理应用 https://www.hiascend.com/developer/techArticles/20240326-1?envFlag1...
基于SpringBoot+Vue实现的二手交易市场平台功能一
一、前言介绍: 1.1 项目摘要 随着社会的发展和人们生活水平的提高,消费者购买能力的提升导致产生了大量的闲置物品,这些闲置物品具有一定的经济价值。特别是在高校环境中,学生群体作为一个具有一定消费水平的群体,每…...
TC3xx芯片的UCB介绍
文章目录 前言一、UCB的定义及其功能简介二、UCB_BMHDx_ORIG and UCB_BMHDx_COPY (x 0 - 3)2.1 BMHD(Boot Mode Head) 三、UCB_SSW四、UCB_PFLASH_ORIG and UCB_PFLASH_COPY4.1 Password4.2 UCB Confirmation 前言 缩写全称UCBUser Configuration BlockBMHDBoot Mode Headers…...
Airflow量化入门系列:第四章 A股数据处理与存储优化
Airflow量化入门系列:第四章 A股数据处理与存储优化 本教程系统性地讲解了 Apache Airflow 在 A 股量化交易中的应用,覆盖从基础安装到高级功能的完整知识体系。通过八章内容,读者将掌握 Airflow 的核心概念、任务调度、数据处理、技术指标计…...
《海空重力测量理论方法及应用》之一重力仪系统组成及工作原理(下)
2、三轴稳定平台型 稳定平台的作用是隔离测量载体角运动对重力观测量的影响,确保重力传感器的敏感轴方向始终与重向保持一致。 当前主流的海空重力仪使用的稳定平台方案主要有4种: ①双轴阻尼陀螺平台: ②)双轴惯导加捷联方位平台: ③三轴惯导平台; ④捷联惯导…...
C++模板递归结构详解和使用
示例代码 template<typename _SourceIterator, typename _DestT> struct convert_pointer {typedef typename convert_pointer<typename _SourceIterator::pointer, _DestT>::type type; };1. 模板参数 _SourceIterator 是输入的类型,通常表示迭代器类…...
(八)PMSM驱动控制学习---无感控制之滑膜观测器
在FOC矢量控制中,我们需要实时得到转子的转速和位置 ,但在考虑到成本和使用场合的情况下,往往使用无感控制,因为无位置传感器克服了传统机械式传感器的很多缺点和不足。比如,机械式传感器对环境要求比较严格࿰…...
蓝桥杯真题-分糖果-题解
链接:https://www.lanqiao.cn/problems/4124/learning/ 题目 复述:两种糖果,分别有9和16,分给7人,每个人得到的最少2,最多5,必需全部分完,几种分法? 复习-深度优先搜索 …...
推荐系统(二十二):基于MaskNet和WideDeep的商品推荐CTR模型实现
在上一篇文章《推荐系统(二十一):基于MaskNet的商品推荐CTR模型实现》中,笔者基于 MaskNet 构建了一个简单的模型。笔者所经历的工业级实践证明,将 MaskNet 和 Wide&Deep 结合应用,可以取得不错的效果&…...
辅助查询是根据查询到的文档片段再去生成新的查询问题
💡 辅助查询是怎么来的? 它是基于你当前查询(query)检索到的某个文档片段(chunk_result),再去“反推”出新的相关问题(utility queries),这些问题的作用是&a…...
Spring Cloud 框架为什么能处理高并发
Spring Cloud框架能够有效处理高并发场景,核心在于其微服务架构设计及多组件的协同作用,具体机制如下: 一、分布式架构设计支撑高扩展性 服务拆分与集群部署 Spring Cloud通过微服务拆分将单体系统解耦为独立子服务,每个服务可独…...
Pseduo LiDAR(CVPR2019)
文章目录 AbstractIntroductionRelated WorkLiDAR-based 3D object detectionStereo- and monocular-based depth estimationImage-based 3D object detection MethodDepth estimationPseudo-LiDAR generationLiDAR vs. pseudo-LiDAR3D object detectionData representation ma…...
强化学习课程:stanford_cs234 学习笔记(3)introduction to RL
文章目录 前言7 markov 实践7.1 markov 过程再叙7.2 markov 奖励过程 MRP(markov reward process)7.3 markov 价值函数与贝尔曼方程7.4 markov 决策过程MDP(markov decision process)的 状态价值函数7.4.1 状态价值函数7.4.2 状态…...
前端精度计算:Decimal.js 基本用法与详解
一、Decimal.js 简介 decimal.js 是一个用于任意精度算术运算的 JavaScript 库,它可以完美解决浮点数计算中的精度丢失问题。 官方API文档:Decimal.js 特性: 任意精度计算:支持大数、小数的高精度运算。 链式调用:…...
来聊聊C++中的vector
一.vector简介 vector是什么 C 中的 vector 是一种序列容器,它允许你在运行时动态地插入和删除元素。 vector 是基于数组的数据结构,但它可以自动管理内存,这意味着你不需要手动分配和释放内存。 与 C 数组相比,vector 具有更多的…...
对比学习中的NCE(Noise-Contrastive Estimation)和InfoNCE(SimCLR)损失函数+案例(附SimSiam分析)
在对比学习(Contrastive Learning)中,NCE(Noise-Contrastive Estimation)和InfoNCE是两种常见的目标函数,它们都用于通过区分正样本和负样本来学习高质量的表示。 1. NCE(Noise-Contrastive Est…...
基于FAN网络的图像识别系统设计与实现
基于FAN网络的图像识别系统设计与实现 一、系统概述 本系统旨在利用FAN(Fourier Analysis Networks)网络架构实现高效的图像识别功能,并通过Python语言设计一个直观的用户界面,方便用户操作与使用。FAN网络在处理周期性特征方面具有独特优势,有望提升图像识别在复杂场景…...
【瑞萨 RA-Eco-RA2E1-48PIN-V1.0 开发板测评】PWM
【瑞萨 RA-Eco-RA2E1-48PIN-V1.0 开发板测评】PWM 本文介绍了瑞萨 RA2E1 开发板使用内置时钟和定时器实现 PWM 输出以及呼吸灯的项目设计。 项目介绍 介绍了 PWM 和 RA2E1 的 PWM 资源。 PWM 脉冲宽度调制(Pulse Width Modulation, PWM)是一种对模拟…...
NDK开发:开发环境
NDK开发环境 一、NDK简介 1.1 什么是NDK NDK(Native Development Kit)是Android提供的一套工具集,允许开发者在Android应用中使用C/C++代码。它包含了: 交叉编译器构建工具调试器系统头文件和库示例代码和文档1.2 NDK的优势 性能优化:直接使用底层代码,提高性能代码保…...
设计模式简述(三)工厂模式
工厂模式 描述简单工厂(静态工厂)工厂方法模式 抽象工厂增加工厂管理类使用 描述 工厂模式用以封装复杂的实例初始化过程,供外部统一调用 简单工厂(静态工厂) 如果对象创建逻辑简单且一致,可以使用简单工…...
通过Postman和OAuth 2.0连接Dynamics 365 Online的详细步骤
🌟 引言 在企业应用开发中,Dynamics 365 Online作为微软的核心CRM平台,提供了强大的Web API接口。本文将教你如何通过Postman和OAuth 2.0认证实现与Dynamics 365的安全连接,轻松调用数据接口。 📝 准备工作 工具安装…...
LlamaIndex实现RAG增强:上下文增强检索/重排序
面向文档检索的上下文增强技术 文章目录 面向文档检索的上下文增强技术概述技术背景核心组件方法详解文档预处理向量存储创建上下文增强检索检索对比技术优势结论导入库和环境变量读取文档创建向量存储和检索器数据摄取管道使用句子分割器的摄取管道使用句子窗口的摄取管道查询…...
AI比人脑更强,因为被植入思维模型【43】蝴蝶效应思维模型
giszz的理解:蝴蝶效应我们都熟知,就是说一个微小的变化,能带动整个系统甚至系统的空间和时间的远端,产生巨大的链式反应。我学习后的启迪,简单的说,就是不要忽视任何微小的问题,更多时候&#x…...
程序化广告行业(62/89):DSP系统的媒体与PDB投放设置探秘
程序化广告行业(62/89):DSP系统的媒体与PDB投放设置探秘 大家好!在之前的学习中,我们对程序化广告的DSP系统有了一定了解。今天还是带着和大家共同进步的想法,深入探索DSP系统中媒体设置以及PDB投放设置的…...
Java项目之基于ssm的怀旧唱片售卖系统(源码+文档)
项目简介 怀旧唱片售卖系统实现了以下功能: 用户信息管理: 用户信息新增:添加新用户的信息。 用户信息修改:对现有用户信息进行修改。 商品信息管理: 商品信息添加:增加新的商品(唱片&#x…...
程序化广告行业(61/89):DSP系统活动设置深度剖析
程序化广告行业(61/89):DSP系统活动设置深度剖析 大家好!在程序化广告的学习道路上,我们已经探索了不少重要内容。今天依旧本着和大家一起学习进步的想法,深入解析DSP系统中活动设置的相关知识。这部分内容…...
Altshuller矛盾矩阵查询:基于python和streamlit
基于python和streamlit实现的Altshuller矛盾矩阵查询 import streamlit as st import json# 加载数据 st.cache_resource def load_data():with open(parameter.json, encodingutf-8) as f:parameters json.load(f)with open(way.json, encodingutf-8) as f:contradictions …...
FreeRTOS的空闲任务
在 FreeRTOS 中,空闲任务(Idle Task) 是操作系统自动创建的一个特殊任务,其作用和管理方式如下: 1. 空闲任务创建 FreeRTOS 内核自动创建:当调用 vTaskStartScheduler() 启动调度器时,内核会自…...
【代码模板】如何用FILE操作符打开文件?fopen、fclose
#include "stdio.h" #include "unistd.h"int main(int argc, char *argv[]) {FILE *fp fopen("1.log", "wb");if (!fp) {perror("Failed open 1.log");return -1;}fclose(fp); }关于权限部分参考兄弟篇【代码模板】C语言中…...
[特殊字符] Pandas 常用操作对比:Python 运算符 vs Pandas 函数
在 Pandas 中,许多操作可以直接使用 Python 的比较运算符(如 、!、>、< 等),而不需要调用 Pandas 的专门函数(如 eq()、ne()、gt() 等)。这些运算符在 Pandas 中已经被重载,代码更简洁。以…...
I.MX6ULL开发板与linux互传文件的方法--NFS,SCP,mount
1、内存卡或者U盘 方法比较简单,首先在linux系统中找到u盘对应的文件夹,随后使用cp指令将文件拷贝进u盘。 随后将u盘插入开发板中,找到u盘对应的设备文件。一般u盘对应的设备文件在/dev下,以sda开头,可以使用命令列出所…...
图解AUTOSAR_SWS_FlashEEPROMEmulation
AUTOSAR Flash EEPROM Emulation (FEE) 详解 基于AUTOSAR规范的Flash EEPROM Emulation模块分析 目录 1. 概述2. 架构设计 2.1 模块位置与接口2.2 内部状态管理2.3 配置结构3. API接口 3.1 接口功能分类3.2 错误管理4. 操作流程 4.1 写入操作序列5. 总结1. 概述 Flash EEPROM …...
Unity:Simple Follow Camera(简单相机跟随)
为什么需要Simple Follow Camera? 在游戏开发中,相机(Camera)是玩家的“眼睛”。它的作用是决定玩家看到游戏世界的哪一部分。很多游戏需要相机自动跟随玩家角色,让玩家始终可以看到角色及其周围的环境,而…...
[项目总结] 在线OJ刷题系统项目总结与分析(二): 技术应用(上)
🌸个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 🏵️热门专栏: 🧊 Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm1001.2014.3001.5482 🍕 Collection与…...
针对Ansible执行脚本时报错“可执行文件格式错误”,以下是详细的解决步骤和示例
针对Ansible执行脚本时报错“可执行文件格式错误”,以下是详细的解决步骤和示例: 目录 一、错误原因分析二、解决方案1. 检查并添加可执行权限2. 修复Shebang行3. 转换文件格式(Windows → Unix)4. 检查脚本内容兼容性5. 显式指定…...
从 Dense LLM 到 MoE LLM:以 DeepSeek MoE 为例讲解 MoE 的基本原理
写在前面 大多数 LLM 均采用 Dense(密集) 架构。这意味着,在处理每一个输入 Token 时,模型所有的参数都会被激活和计算。想象一下,为了回答一个简单的问题,你需要阅读整部大英百科全书的每一个字——这显然效率低下。 为了突破 Dense 模型的瓶颈,一种名为 Mixture of …...
未来已来:探索AI驱动的HMI设计新方向
在科技浪潮的持续冲击下,人工智能(AI)正以势不可挡的姿态重塑各个领域的格局,其中人机交互(HMI,Human - Machine Interaction)设计领域深受其影响,正经历着深刻的变革。AI 技术的融入…...
5天速成ai agent智能体camel-ai之第1天:camel-ai安装和智能体交流消息讲解(附源码,零基础可学习运行)
嗨,朋友们!👋 是不是感觉AI浪潮铺天盖地,身边的人都在谈论AI Agent、大模型,而你看着那些密密麻麻的代码,感觉像在读天书?🤯 别焦虑!你不是一个人。很多人都想抓住AI的风…...