【C++11】多线程并行化快速排序的实现

并发归并排序算法:利用多线程提升排序效率 随着数据规模的不断增长,传统的顺序排序算法在处理大数据集时面临效率瓶颈。本文深入研究并发归并排序算法,旨在利用多线程技术加速排序过程,充分发挥多核处理器的并行计算能力。文章详细阐述了并发归并排序的原理、算法设计、实现步骤以及性能分析,并通过实际案例展示了该算法相较于传统归并排序在处理大规模数据时的显著优势。 阅读详情

基础

并行化可以优化许多算法,前提是并行化的任务之间不会产生冲突。快速排序的核心逻辑为分治,通过一次遍历,使得区间分成了两个子区间,其中一个子区间的元素恒小于等于另一个区间的元素,再用同样的方法分别处理这两个子区间。可以发现,这两个子区间不重叠,并且后续的操作都是在一个区间内继续完成,不会跨区间,因此满足了并行化的条件。

通常的一种用多线程来解决问题的方式是使用线程池,将需要完成的操作细化成任务,传入线程池,空闲的线程会去完成这些任务。对于并行化快排来说,对较大的区间[p, q]进行partition操作分为两个子区间或对较小的区间直接进行快速排序就是它的任务。不过与普通的线程池操作不同,该任务的完成可能会创造新的任务,即这个任务可能有“返回值”,因此需要对通用线程池进行扩展,满足并行化快速排序的特定需求。具体实现细节见后文。

其他的一些优化

由于线程的切换存在时间开销,因此并行化任务不会无限对区间进行细分,当区间长度小于阈值时,使用串行化快排。

由于插入排序对小范围区间的排序处理效果更好,因此在区间范围小于16的情况下,不再使用快速排序,而是插入排序(STL里的sort也是这么做的)。

实现

Task包括一个需要排序的数组,和具体需要排序的区间 [p, q]

template <typename T>
class Task {
    public:
        Task(T &vec, int p, int q) : vec_(vec), p_(p), q_(q) {}
        T &vec_;
        int p_, q_;
};

任务队列主要的数据为一个包含Task的Queue,为了保证Queue操作的原子性,使用了mutex,condition_variable用于使得Get操作在Queue为空时,能够阻塞等待,直到新插入一个Task

template <typename T>
class TaskQueue {
    public:
        Task<T> Get();
        void Push(Task<T> &&task);
        bool Empty();

    private:
        std::queue<Task<T> > que_;
        std::mutex mutex_;
        std::condition_variable cond_var_;
};

template <typename T>
Task<T> TaskQueue<T>::Get() {
    std::unique_lock<std::mutex> lk(mutex_);
    while(que_.empty()) {
        cond_var_.wait(lk);
    }
    Task<T> ans = que_.front();
    que_.pop();
    return ans;
}

template <typename T>
void TaskQueue<T>::Push(Task<T> &&task) {
    std::lock_guard<std::mutex> lk(mutex_);
    que_.push(std::move(task));
    cond_var_.notify_one();
}

template <typename T>
bool TaskQueue<T>::Empty() {
    std::lock_guard<std::mutex> lk(mutex_);
    return que_.empty();
}

CQSort 为主要的并行排序类,整体逻辑类似一个线程池

void SequentialSort_(T &vec, int p, int q);

void InsertSort_(T &vec, int p, int q);

int Partition_(T &vec, int p, int q);

上述三个函数是基础的排序方法,分别为串行化快排、插入排序和快排的Partition操作

void ThreadFunc_() 为线程池中线程执行的入口,不断从Queue中取出Task执行直到排序完成

void ConcurrentSort_(T &vec) 为并行化快排开始的入口,创建一个Task并插入到Queue中

void doConcurrentSort_(T &vec, int p, int q) 具体执行快排的代码,在Partiton后继续执行其中一半,另一半存入任务队列。若待排序区间长度小于512,则使用串行化快排。

mThr_ 为线程的数量

idleThr_ 为空闲线程的数量

std::vector < std::future < void>> asyncVec 存储执行的线程

bool isEnd 为线程池结束的标识符,当所有线程都空闲,并且任务队列为空,则认为排序已完成

template <typename T>
class CQSort {
public:
    CQSort(int mthr);
    ~CQSort();
    void operator()(T &vec);

private:
    void SequentialSort_(T &vec, int p, int q);
    void InsertSort_(T &vec, int p, int q);
    int Partition_(T &vec, int p, int q);
    void ConcurrentSort_(T &vec);
    void doConcurrentSort_(T &vec, int p, int q);
    void ThreadFunc_();

private:
    const int mThr_;
    std::mutex idleMutex_;
    int idleThr_;
    std::vector<std::future<void>> asyncVec;
    TaskQueue<T> taskQue;
    std::mutex endMutex_;
    std::condition_variable endCond_;
    bool isEnd;
};

template<typename T>
CQSort<T>::CQSort(int mthr) : mThr_(mthr), idleThr_(mthr), isEnd(false) { //mthr 线程数量
    for(int i = 0; i < mThr_; ++i) {
        asyncVec.push_back(std::async(std::launch::async, bind(&CQSort<T>::ThreadFunc_, this)));
    }
}

// 正式开始排序
template <typename T>    
void CQSort<T>::operator()(T &vec) {
    if(mThr_ <= 1) {
        SequentialSort_(vec, 0, vec.size() - 1);
    }
    else {
        ConcurrentSort_(vec);
    }
}

template <typename T>
CQSort<T>::~CQSort() {
    if(mThr_ <= 1) {
        return;
    }
    T tmp;
    int n = mThr_;
    while((n--) != 0) {
        taskQue.Push(Task<T>{tmp, 0, 0});
    }
    while(!asyncVec.empty()) {
        asyncVec.pop_back();
    }
}

template <typename T>
void CQSort<T>::SequentialSort_(T &vec, int p, int q) {
    if(p < q) {
        if(q - p < 16) {
            InsertSort_(vec, p, q);
        } else {
            int mid = Partition_(vec, p, q);
            SequentialSort_(vec, p, mid - 1);
            SequentialSort_(vec, mid + 1, q);
        }
    }
}

template <typename T>
void CQSort<T>::InsertSort_(T &vec, int p, int q) {
    for(int i = p + 1; i <= q; ++i) {
        typename T::value_type tmp = vec[i];
        int j = i - 1;
        while(j >= p && vec[j] > tmp) {
            vec[j + 1] = vec[j];
            --j;
        }
        vec[j + 1] = tmp;
    }
}

// 并行化快排开始的入口,创建一个Task并插入到Queue中,并等待排序的完成
template <typename T>
void CQSort<T>::ConcurrentSort_(T &vec) {
    if(vec.size() < ConcurrentThreshold) {
        return SequentialSort_(vec, 0, vec.size() - 1);
    }
    isEnd = false;
    taskQue.Push(Task<T>{vec, 0, (int)vec.size() - 1});
    {
        std::unique_lock<std::mutex> lk(endMutex_);
        while(!isEnd) endCond_.wait(lk);
    }
}

// 具体执行并行化快排的代码,在Partiton后继续执行其中一半,另一半存入任务队列。若待排序区间长度小于512,则使用串行化快排。
template <typename T>
void CQSort<T>::doConcurrentSort_(T &vec, int p, int q) {
    if(q - p < ConcurrentThreshold) {
        return SequentialSort_(vec, p , q);
    }
    int mid = Partition_(vec, p, q);
    if(p < mid - 1 && q > mid + 1) {
        taskQue.Push(Task<T>{vec, p, mid - 1});
        doConcurrentSort_(vec, mid + 1, q);
    } else if(p < mid - 1) {
        doConcurrentSort_(vec, p, mid - 1);
    }
    else if(q > mid + 1) {
        doConcurrentSort_(vec, mid + 1, q);
    }
}

// 线程池中线程执行的入口,不断从Queue中取出Task执行直到排序完成
template <typename T>
void CQSort<T>::ThreadFunc_() {
    while(true) {
        Task<T> task = taskQue.Get();
        if(task.p_ >= task.q_) {
            return;
        }
        {
            std::lock_guard<std::mutex> lk(idleMutex_);
            --idleThr_;
        }
        doConcurrentSort_(task.vec_, task.p_, task.q_);
        {
            std::lock_guard<std::mutex> lk1(idleMutex_);
            // check finsh
            if(++idleThr_ == mThr_ && taskQue.Empty()) {
                std::lock_guard<std::mutex> lk2(endMutex_);
                isEnd = true;
                endCond_.notify_one();
            }
        }
    }
}

template <typename T>
int CQSort<T>::Partition_(T &vec, int p, int q) {
    // three nums pick mid
    int mid = (p + q) >> 1;
    int pivot = p;
    const typename T::value_type &pv = vec[p], &midv = vec[mid], &qv = vec[q];
    if(pv > qv) {
        if(pv > midv) {
            pivot = midv < qv ? q : mid;
        } else { 
            pivot = p;
        }
    } else {
        if(qv > midv) {
            pivot = midv < pv ? p : mid;
        } else {
            pivot = q;
        }
    }
    // partation
    typename T::value_type tmp = vec[pivot];
    vec[pivot] = vec[p];
    while(p < q) {
        while(p < q && vec[q] >= tmp) {
            --q;
        }
        vec[p] = vec[q];
        while(p < q && vec[p] <= tmp) {
            ++p;
        }
        vec[q] = vec[p];
    }
    vec[p] = tmp;
    return p;
}

完整代码:

// cqsort.hpp
#ifndef CQSORT_HPP
#define CQSORT_HPP
#include <algorithm>
#include <thread>
#include <future>
#include <atomic>
#include <iostream>
#include <mutex>
#include <queue>
#include <cassert>
#include <functional>
#include <condition_variable>

const int ConcurrentThreshold = 512;

// Task包括一个需要排序的数组,和具体需要排序的区间 [p, q]
template <typename T>
class Task {
    public:
        Task(T &vec, int p, int q) : vec_(vec), p_(p), q_(q) {}
        T &vec_;
        int p_, q_;
};
// 任务队列主要的数据为一个包含Task的Queue,为了保证Queue操作的原子性,使用了mutex,
// condition_variable用于使得Get操作在Queue为空时,能够阻塞等待,直到新插入一个Task
template <typename T>
class TaskQueue {
    public:
        Task<T> Get();
        void Push(Task<T> &&task);
        bool Empty();

    private:
        std::queue<Task<T> > que_;
        std::mutex mutex_;
        std::condition_variable cond_var_;
};

template <typename T>
Task<T> TaskQueue<T>::Get() {
    std::unique_lock<std::mutex> lk(mutex_);
    while(que_.empty()) {
        cond_var_.wait(lk);
    }
    Task<T> ans = que_.front();
    que_.pop();
    return ans;
}

template <typename T>
void TaskQueue<T>::Push(Task<T> &&task) {
    std::lock_guard<std::mutex> lk(mutex_);
    que_.push(std::move(task));
    cond_var_.notify_one();
}

template <typename T>
bool TaskQueue<T>::Empty() {
    std::lock_guard<std::mutex> lk(mutex_);
    return que_.empty();
}
/*
    CQSort 为主要的并行排序类,整体逻辑类似一个线程池
    
    void SequentialSort_(T &vec, int p, int q);
    void InsertSort_(T &vec, int p, int q);
    int Partition_(T &vec, int p, int q);
    上述三个函数是基础的排序方法,分别为串行化快排、插入排序和快排的Partition操作

    void ThreadFunc_() 为线程池中线程执行的入口,不断从Queue中取出Task执行直到排序完成
    void ConcurrentSort_(T &vec) 为并行化快排开始的入口,创建一个Task并插入到Queue中
    void doConcurrentSort_(T &vec, int p, int q) 具体执行快排的代码,在Partiton后继续执行其中一半,另一半存入任务队列。若待排序区间长度小于512,则使用串行化快排。

    mThr_ 为线程的数量
    idleThr_ 为空闲线程的数量
    std::vector<std::future<void>> asyncVec 存储执行的线程
    bool isEnd 为线程池结束的标识符,当所有线程都空闲,并且任务队列为空,则认为排序已完成


*/ 
template <typename T>
class CQSort {
public:
    CQSort(int mthr);
    ~CQSort();
    void operator()(T &vec);

private:
    void SequentialSort_(T &vec, int p, int q);
    void InsertSort_(T &vec, int p, int q);
    int Partition_(T &vec, int p, int q);
    void ConcurrentSort_(T &vec);
    void doConcurrentSort_(T &vec, int p, int q);
    void ThreadFunc_();

private:
    const int mThr_;
    std::mutex idleMutex_;
    int idleThr_;
    std::vector<std::future<void>> asyncVec;
    TaskQueue<T> taskQue;
    std::mutex endMutex_;
    std::condition_variable endCond_;
    bool isEnd;
};

template<typename T>
CQSort<T>::CQSort(int mthr) : mThr_(mthr), idleThr_(mthr), isEnd(false) { //mthr 线程数量
    for(int i = 0; i < mThr_; ++i) {
        asyncVec.push_back(std::async(std::launch::async, bind(&CQSort<T>::ThreadFunc_, this)));
    }
}

// 正式开始排序
template <typename T>    
void CQSort<T>::operator()(T &vec) {
    if(mThr_ <= 1) {
        SequentialSort_(vec, 0, vec.size() - 1);
    }
    else {
        ConcurrentSort_(vec);
    }
}

template <typename T>
CQSort<T>::~CQSort() {
    if(mThr_ <= 1) {
        return;
    }
    T tmp;
    int n = mThr_;
    while((n--) != 0) {
        taskQue.Push(Task<T>{tmp, 0, 0});
    }
    while(!asyncVec.empty()) {
        asyncVec.pop_back();
    }
}

template <typename T>
void CQSort<T>::SequentialSort_(T &vec, int p, int q) {
    if(p < q) {
        if(q - p < 16) {
            InsertSort_(vec, p, q);
        } else {
            int mid = Partition_(vec, p, q);
            SequentialSort_(vec, p, mid - 1);
            SequentialSort_(vec, mid + 1, q);
        }
    }
}

template <typename T>
void CQSort<T>::InsertSort_(T &vec, int p, int q) {
    for(int i = p + 1; i <= q; ++i) {
        typename T::value_type tmp = vec[i];
        int j = i - 1;
        while(j >= p && vec[j] > tmp) {
            vec[j + 1] = vec[j];
            --j;
        }
        vec[j + 1] = tmp;
    }
}

template <typename T>
void CQSort<T>::ConcurrentSort_(T &vec) {
    if(vec.size() < ConcurrentThreshold) {
        return SequentialSort_(vec, 0, vec.size() - 1);
    }
    isEnd = false;
    taskQue.Push(Task<T>{vec, 0, (int)vec.size() - 1});
    {
        std::unique_lock<std::mutex> lk(endMutex_);
        while(!isEnd) endCond_.wait(lk);
    }
}

template <typename T>
void CQSort<T>::doConcurrentSort_(T &vec, int p, int q) {
    if(q - p < ConcurrentThreshold) {
        return SequentialSort_(vec, p , q);
    }
    int mid = Partition_(vec, p, q);
    if(p < mid - 1 && q > mid + 1) {
        taskQue.Push(Task<T>{vec, p, mid - 1});
        doConcurrentSort_(vec, mid + 1, q);
    } else if(p < mid - 1) {
        doConcurrentSort_(vec, p, mid - 1);
    }
    else if(q > mid + 1) {
        doConcurrentSort_(vec, mid + 1, q);
    }
}

template <typename T>
void CQSort<T>::ThreadFunc_() {
    while(true) {
        Task<T> task = taskQue.Get();
        if(task.p_ >= task.q_) {
            return;
        }
        {
            std::lock_guard<std::mutex> lk(idleMutex_);
            --idleThr_;
        }
        doConcurrentSort_(task.vec_, task.p_, task.q_);
        {
            std::lock_guard<std::mutex> lk1(idleMutex_);
            // check finsh
            if(++idleThr_ == mThr_ && taskQue.Empty()) {
                std::lock_guard<std::mutex> lk2(endMutex_);
                isEnd = true;
                endCond_.notify_one();
            }
        }
    }
}

template <typename T>
int CQSort<T>::Partition_(T &vec, int p, int q) {
    // three nums pick mid
    int mid = (p + q) >> 1;
    int pivot = p;
    const typename T::value_type &pv = vec[p], &midv = vec[mid], &qv = vec[q];
    if(pv > qv) {
        if(pv > midv) {
            pivot = midv < qv ? q : mid;
        } else { 
            pivot = p;
        }
    } else {
        if(qv > midv) {
            pivot = midv < pv ? p : mid;
        } else {
            pivot = q;
        }
    }
    // partation
    typename T::value_type tmp = vec[pivot];
    vec[pivot] = vec[p];
    while(p < q) {
        while(p < q && vec[q] >= tmp) {
            --q;
        }
        vec[p] = vec[q];
        while(p < q && vec[p] <= tmp) {
            ++p;
        }
        vec[q] = vec[p];
    }
    vec[p] = tmp;
    return p;
}

测试代码:

// cqbench.cpp
#include <string>
#include <fstream>
#include <vector>
#include <chrono>
#include <random>
#include <tuple>
#include <iomanip>
#include "cqsort.hpp"

using namespace std;
using chrono::steady_clock;
using chrono::duration;
using chrono::duration_cast;

vector<int> vec1, vec2, vec3;

int main()
{
    default_random_engine rand;
    rand.seed((unsigned int)time(NULL));
    
    const double INF = 1e10;
    CQSort<vector<int> > cq1(0), cq2(7);
    int listSize = 1;
    fstream fout("benchResult", ios::out);
    fout << "List Size       Sequential      Sort      Time (s)            Concurrent      Sort      Time (s)" << endl;
    fout << "              min            max            average         min            max            average" << endl;
    fout << "-----------   ------------   ------------   ------------    ------------   ------------   ------------" << endl;
    for(int i = 0; i < 8; ++i, listSize *= 10) {
        double seqMin = INF, seqMax = -INF, seqAvg = 0.0;
        double conMin = INF, conMax = -INF, conAvg = 0.0;
        for(int j = 0; j < 10; ++j) {
            vec1.resize(listSize);
            for(auto &val : vec1) {
                val = rand();
            }
            vec2 = vec1;
            vec3 = vec1;
            steady_clock::time_point t1 = steady_clock::now();
            cq1(vec1);
            steady_clock::time_point t2 = steady_clock::now();
            cq2(vec2);
            steady_clock::time_point t3 = steady_clock::now();

            duration<double> ts1 = duration_cast<duration<double> >(t2 - t1);
            duration<double> ts2 = duration_cast<duration<double> >(t3 - t2);
            seqMin = min(seqMin, ts1.count());
            seqMax = max(seqMax, ts1.count());
            seqAvg += ts1.count();
            conMin = min(conMin, ts2.count());
            conMax = max(conMax, ts2.count());
            conAvg += ts2.count();
        }
        seqAvg /= 10.0;
        conAvg /= 10.0;
        fout << setiosflags(ios::left);
        fout << setw(9) << listSize << "     ";
        fout << setw(12) << seqMin << "   " << setw(12) << seqMax << "   " << setw(12) << seqAvg << "    ";
        fout << setw(12) << conMin << "   " << setw(12) << conMax << "   " << setw(12) << conAvg << endl;
    }
    return EXIT_SUCCESS;
}

实验结果:

List Size       Sequential      Sort      Time (s)            Concurrent      Sort      Time (s)
              min            max            average         min            max            average
-----------   ------------   ------------   ------------    ------------   ------------   ------------
1             0              4e-07          7e-08           0              1e-07          7e-08       
10            2e-07          4e-07          3e-07           2e-07          3e-07          2.8e-07     
100           5.4e-06        6.1e-06        5.68e-06        4.2e-06        4.8e-06        4.45e-06    
1000          8.14e-05       9.97e-05       8.436e-05       9.28e-05       0.0002154      0.0001312   
10000         0.0010873      0.0014095      0.00115039      0.0004317      0.0010223      0.00060445  
100000        0.0137637      0.0145         0.0139018       0.0037528      0.0051069      0.00443984  
1000000       0.165105       0.203724       0.172742        0.0347065      0.0477473      0.0386616   

如果大家对文章内容有疑问或建议,欢迎评论区留言,我们可以一起讨论,共同提高
转载请标明原作者:https://blog.youkuaiyun.com/qq_36622751

几种比较创新的排序算法,多线程排序 多线程 为什么要用多线程 很显然,多线程能够同时执行多个任务。举个例子,你打开某视频播放器,点击下载某个视频,然后你发现这个时候一直在下载,其他啥都干不了。所以在这种情况下,可以使用多线程,让下载任务继续,同时也能继续其他操作。 作为一个包工头,一堆砖要搬,但是就一个人,可是你只能搬这么多,怎么办?多找几个人一起搬,但是其他人就也需要付工钱,没关系,能早点干完也就行了,反正总体工钱差不多。 同样的,如果有一个任务特别耗时,而这个任务可以拆分为多个任务,那么就可以让每个线程去执行一个任务,这样任务就可以更快地 阅读详情

相关推荐

C++11多线程实现归并排序:分治策略的并行化改造

Tn2Tn/2On总时间复杂度 = 层数 × 每层工作量 = O(n log n)

weixin_69882801的博客 1064

多线程实现快速排序

多线程排序,主要是将整个排序的序列分成若干份,每一个线程排序一份,所以线程排序完成之后,就进行归并,相当于多个有序序列合并成一个有序序列。 这里就需要用到线程屏障,也就是pthread_barrier 系列函数。 屏障,通俗的说就是一个比赛跑步的过程,所以队员就绪了,才能进行比赛。 多线程排序也是,需要每个线程都是排序完成后,才能进行合并的过程。 代码: #include &lt...

Nick 7232

基于C++11 CPU多线程的 奇偶排序(没有CUDA)

写的不一定对!错了求指正!前言这篇是用C++11 的 thread 做的。 编译大概是g++ sort -O3 -pthread -std=c++14 -o sort其实还没学c++14最近开始学CUDA,感觉线程具体怎么用和硬件是直接相关的, 不同架构啊不同精度啊使用线程的方式都应该不同。这篇博客就是个实验,用CPU实现一下奇偶排序为日后与CUDA做比较一直是CPU和GPU一起学的。二者都支持

xsfl1234的博客 957

快速排序(多线程)实现方式

记录快速排序和多线程快速排序性能耗时 10次排序取均值 public class QuickSortUtils { public static void main(String[] args) throws Exception { int total = 4000000; int[] list = new int[total]; for (int i = 0; i < total; i++) { int rand =

a972188163的博客 1318

C++多线程_std::future与std::promise

在并发编程中,我们通常会用到一组非阻塞的模型:promise\future。在python、js、java中都提供future\promise,是现代语言常用的非阻塞编程模型。

惊鸿一博 2514

RecursiveTask可递归的多线程(多线程快速排序)

前言 首先这个灵感来源于查找API文档的时候,我找到了这个东西 然后我想起来先前经典小算法之快排的时候我说过,你就是左右两边开线程也是可以没有影响滴,所以现在我们就是用这个来实现一个多线程的快速排序。 那么在这里我们先简单说一下这个RecursiveTask 有哪些注意点。 RecursiveTask使用 这个其实没啥就两个,Fork与Join,当然还有Compute。 使用结构 我们先来看看这个玩意使用的结构,官方给的是一个斐波那契的例子。 调用 现在我们可以尝试调用了,这个调用稍微复杂一点。 我们需

`or 1 or 不正经の泡泡 3043

c++11 版多线程 快速排序

// First.cpp : 定义控制台应用程序的入口点。 #include "stdafx.h" #include #include #include #include #include #include #include #include using namespace std; /** @ return the position of x  */ in

yiranlun3的专栏 1398

Star CCM+ 实战:旋风分离器网格加密策略与湍流模拟优化

本文针对Star CCM+中旋风分离器模拟精度不足的常见问题,深入剖析了网格质量的关键影响。文章重点介绍了通过局部网格加密策略,精准优化旋流核心区与入口区域的网格密度,从而在保证计算效率的前提下,显著提升湍流模拟的准确性与流场细节的捕捉能力,为工程实践提供了一套有效的优化方案。

water的专栏 535

CPU作业之openMp实现快速排序

博客原文 欢迎关注博客:GaoMing’s blog 项目介绍 利用OpenMp设计多线程进行快速排序 openMP相关知识 什么是openMP OpenMP是由OpenMP Architecture Review Board牵头提出的,并已被广泛接受的、用于共享内存并行系统的多线程程序设计的一套编译指令 (Compiler Directive)。 OpenMP支持、C、C++和Fortran;而支持OpenMP的编译器包括Sun Compiler、GNU Compiler和Intel Compile

qq_45769678的博客 4764

在mdk文件移植太多_XCubeClassB代码 的移植

前言ST在www.st.com官网上提供了针对IEC60730/IEC60335 ClassB认证的软件包。软件包分为三个,分别适用STM8,STM32(基于标准外设库)和STM32(基于HAL库):STM8-SafeCLASSB,STM32-CLASSB-SPL,X-CUBE-CLASSB。本文以X-CUBE-CLASSB为例介绍如何做代码的移植以及经常遇到的问题的解决办法。X-CUB...

weixin_28898707的博客 1832

DFT实训教程笔记2(bibili版本)- Scan synthesis practice

文章目录Scan synthesis practice 本博文是博主记录DFT实训教程的笔记版本,此笔记并没有对所有的知识进行记录,仅仅以自身的认知水平,来记录了一些部分笔记并加上了自己的理解。 Scan synthesis practice How to design scan clock structure? 假设一块chip里面有好几十个clock domain,如果每一个clock domain都用一个scan clock虽然会非常清晰,但是IO数目肯定不够用。 clock group: 1、no

ciscomonkey的博客 9082

C++ 实现多线程快排

没想到多线程快排这么简单... 抛弃了分配,由各线程自己去竞争任务... 相当多的时间消耗在了锁上.... 多线程必须量大才能突出优势..比如超过500万

KD的疯狂实验室 1475

Qwen3-Embedding选型指南:0.6B/4B/8B三版本性能对比与场景匹配建议

本文深入解析阿里开源的Qwen3-Embedding系列模型(0.6B/4B/8B)的性能差异与适用场景,提供从边缘计算到云端部署的实战选型建议。通过实测数据对比响应延迟、内存占用和多语言精度等核心指标,帮助技术团队根据业务需求选择最优模型版本,并分享边缘设备优化与云端成本测算策略。

qsc901234567的博客 892

C++多线程排序通用模板

C++多线程排序函数实现 设计 实现方式 各个线程对分配给它的数据分块进行快速排序(std::sort)。 主线程排序完分配给自己的数据分块后,需要等待(std::future,std::promise)各个线程完成。 主线程多路归并排序(需要自己实现,标准库只有二路归并)。 实现注意 本实现大量使用C++11特性。 c语言的qsort内部用到了静态变量,所以不是线程安全的。可以通过在主线程...

Skuaka的博客 1737

双参数威布尔分布(Weibull分布)参数计算(根据故障数据,求双参数威布尔分布的参数详细步骤)

根据故障数据求威布尔分布参数步骤 根据众多文献研究,设备故障数据大多服从指数分布、威布尔分布或对数正态分布。其中威布尔分布较为常用,本文主要介绍威布尔分布的简单应用,并根据某设备故障数据为例简单介绍威布尔分布参数的计算方法。 参考文献11参考文献22 1.双参数威布尔分布的概率密度函数、概率分布函数、可靠度函数: 2.预处理故障数据 将故障数据按时间排序,假设设备分别在以下时间点失效:11.1 、23.5、58.4……则将此时间从小到大排序。并计算中位秩。中位秩的计算公式如下: 其中i表示第几个数据,n

biaochengxiaobai的博客 4万+

黑猫带你学UFS协议第23篇:UFS固件升级操作详解(FFU)

摘要:本文介绍了UFS固件更新(FFU)的实现方法与注意事项。根据UFS3.1 JEDEC协议,FFU通过write buffer命令(MODE=0Eh)下载FW固件,随后必须进行power cycle或硬件复位生效。文章详细阐述了FFU操作的10条规则,包括数据分片传输要求、寄存器访问限制等,同时指出协议未明确的厂商实现差异。关键寄存器包括bFFUTimeout、bDeviceFFUStatus和fPermanentlyDisableFwUpdate。

黑猫奥利奥的博客 1万+

使用 std::future 实现线程间数据传递和简化同步

通过使用和std::async,我们可以轻松地实现并行版本的快速排序。结合 Functional Programming 风格,代码更加简洁和易于理解。然而,在实际应用中,需要根据具体情况调整并行化的粒度,以获得最佳的性能。

捕鲸叉的专栏 535

RK3588在线更新系统内核的方法

《RK3588设备在线升级系统内核方法》 本文介绍了RK3588设备在线升级系统内核的简易方法。通过将新内核文件boot.img拷贝至目标主机后,使用dd命令配合关键参数(fsync确保写入磁盘、notrunc避免截断分区)进行安全更新,更新后需执行sync同步操作并重启生效。文章特别强调操作过程中断电风险,并扩展说明了Uboot的类似更新方法。该方法避免了现场设备拆卸的麻烦,为嵌入式系统维护提供了实用解决方案。

荻夜的博客 1049

排序算法 C++ 11 实现对比:插入、快排、堆排序 10万数据性能实测

本文通过C++11实现了插入排序、快速排序和堆排序三种经典算法,并在10万量级数据上进行了性能实测。结果显示,快速排序在随机数据中表现最优,而堆排序在内存占用和稳定性上更具优势。文章还探讨了工程优化技巧,如混合排序策略和并行化,为开发者提供了实用的排序算法选择指南。

weixin_34320159的博客 390

AI Agent开发第84课-paddleocr-vl全网唯一最全本地化布署教程

本文详解 PaddleOCR-VL 本地化布署全过程,指出为何在真实生产环境中 PaddleOCR-VL 更稳定、更易部署、更贴近工程落地。同时还会详细比较与 DeepSeek-OCR 的核心差异,为全网独家介绍paddleocr-vl的本地部署实战指南。

打造全国最全的AI Agent开发知识领域的博客 5968

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

c++多线程编程_多线程排序真香!

来源:公众号【编程珠玑】作者:守望先生ID:shouwangxiansheng在《系统编程-多线程》中已经了解了多线程的一些特点,其中包括快!那么今天就来看看如何利用多线程来排序。思路我们的思路是这样的:假设有N个线程,则将数组数M据分为N组每个线程对其中的一组数据使用库函数提供的快速排序算法所有线程排序完成后,将每组排序好的数组合并举个例子,使用4个线程对11个数据进行排序:12,10...

weixin_39644611的博客 368

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

随机枢轴快速排序:原理、实现与优化

快速排序作为经典的O(n log n)分治算法,其核心在于通过枢轴(pivot)将数据分区。传统实现选择固定位置枢轴,在面对有序数据时性能会退化到O(n²)。随机化枢轴选择通过引入概率均衡,确保算法在任何输入下都保持期望O(n log n)复杂度,极大提升了工程实践的稳定性。现代C++工程中常结合Lomuto分区方案和三路分区优化,处理重复元素和并行化场景。测试表明,随机化版本在有序数据上比传统实现快45倍,且内存访问模式对CPU缓存友好,成为标准库实现的基础。

weixin_33462167的博客 72

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

C++快速排序递归实现:Hoare分区+随机pivot+尾递归优化

快速排序是分治算法的经典代表,其核心在于通过基准元素(pivot)将数组划分为左右子问题并递归求解。理解分区原理、避免最坏时间复杂度O(n²)、控制递归栈深,是工程落地的关键。本文聚焦C++语言环境下的高鲁棒性实现,采用Hoare双指针分区法提升均衡性与缓存友好性,结合随机化pivot选择抵御恶意输入,辅以尾递归优化将栈空间压缩至O(log n)。适用于算法学习、LeetCode刷题及嵌入式/高频交易等对稳定性与性能双敏感的实战场景。

weixin_33806859的博客 274

C++ 重点(智能指针,多线程,GDB调试等)

c++重点知识梳理

weixin_41645749的博客 1736

C++多线程(四):无锁并发

有时候我们线程1对A的store操作采用release内存序,而线程2对B的load采用acquire内存序,并不能保证A 一定比 B先执行。分区(partitioning):重新排列数组,把比基准元素小的元素放在它的左边,把比基准元素大的元素放在它的右边。选择基准元素的方式有很多种,常见的是选择数组的第一个元素或最后一个元素。我们提供并行方式的函数式编程,可以极大的利用cpu多核的优势,这在并行计算中很常见。顺序,其他的顺序都不能保证原子变量修改的值在其他多线程中看到的顺序是一致的。

2403_87381789的博客 886

C++ 常见面试题汇总

(按值)提供强异常安全?若拷贝失败,原对象不受影响。面试点:说明 UB 会让编译器基于假设做优化,从而产生难以预期的行为。面试点:区分 forwarding reference(模板。resize 可能触发移动还是拷贝(取决于元素是否可。预分配容量以避免多次 realloc(均摊复杂度)。直接在容器末构造对象(避免一次临时拷贝/移动)。容器复杂度与迭代器失效规则(面试常问)。基本概念:定制内存分配策略(进阶题)。(见 Buffer 示例)面试点:能解释为什么对。、先构造新对象再替换。

qq_36812406的博客 5850
上一篇: gcc利用likely()优化分支跳转结构
下一篇: 从零开始设计键值数据库(KEY-VALUE STORE)
Liever18
博客等级 码龄10年 55粉丝 13原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值