C++;使用线程合并排序 我在C++中实现了一个归并排序算法。 在算法内部,它检查数组的大小是否大于min\u size\u to\u thread,如果大于,则使用线程递归调用函数

C++;使用线程合并排序 我在C++中实现了一个归并排序算法。 在算法内部,它检查数组的大小是否大于min\u size\u to\u thread,如果大于,则使用线程递归调用函数,c++,algorithm,performance,sorting,C++,Algorithm,Performance,Sorting,但是当我将min\u size\u增加到\u thread:这会减少正在使用的线程数,函数会变得更快。即使是从1到2个线程 我的假设是,函数速度会随着线程数的增加而增加到某一点,然后又开始下降。这对我来说毫无意义,所以我开始相信我的实现是错误的 template <typename T> void merge_sort(T S[], int S_size, int min_size_to_thread) { if (S_size < 2) return; //

但是当我将
min\u size\u增加到\u thread
:这会减少正在使用的线程数,函数会变得更快。即使是从1到2个线程

我的假设是,函数速度会随着线程数的增加而增加到某一点,然后又开始下降。这对我来说毫无意义,所以我开始相信我的实现是错误的

template <typename T>
void merge_sort(T S[], int S_size, int min_size_to_thread)
{
    if (S_size < 2) return;

    // Left Sequence
    int L_size = S_size / 2;
    T* L = new T[L_size];
    for (int i = 0; i < L_size; i++)
    {
        L[i] = S[i];
    }

    // Right Sequence
    int R_size = (S_size + 1) / 2;
    T* R = new T[R_size];
    for (int i = 0; i < R_size; i++)
    {
        R[i] = S[i + L_size];
    }

    if (S_size > min_size_to_thread)
    {
        std::thread thread_left(merge_sort<T>, L, L_size, min_size_to_thread);
        std::thread thread_right(merge_sort<T>, R, R_size, min_size_to_thread);
        thread_right.join();
        thread_left.join();
    }
    else
    {
        merge_sort<T>(L, L_size, min_size_to_thread);
        merge_sort<T>(R, R_size, min_size_to_thread);
    }

    int S_iterator = 0;
    int L_iterator = 0;
    int R_iterator = 0;

    while ((L_iterator < L_size) && (R_iterator < R_size))
    {
        if (L[L_iterator] < R[R_iterator])
        {
            S[S_iterator] = L[L_iterator];
            ++L_iterator;
        }
        else
        {
            S[S_iterator] = R[R_iterator];
            ++R_iterator;
        }
        ++S_iterator;
    }

    while (L_iterator < L_size)
    {
        S[S_iterator] = L[L_iterator];
        ++L_iterator;
        ++S_iterator;
    }

    while (R_iterator < R_size)
    {
        S[S_iterator] = R[R_iterator];
        ++R_iterator;
        ++S_iterator;
    }

    delete[] L;
    delete[] R;
}

int main()
{
    const int S_size = 500000;
    unsigned char S[S_size];
    for (int i = 0; i < S_size; ++i)
    {
        S[i] = i % 255;
    }

    int min_size_to_thread;

    min_size_to_thread = 250;
    auto t1 = std::chrono::high_resolution_clock::now();
    merge_sort(S, S_size, min_size_to_thread);
    auto t2 = std::chrono::high_resolution_clock::now();
    std::cout << "size > " << min_size_to_thread << ": " << (t2 - t1) / std::chrono::milliseconds(1) << std::endl;

    for (int i = 0; i < S_size; ++i)
    {
        S[i] = i % 255;
    }

    min_size_to_thread = 500;
    t1 = std::chrono::high_resolution_clock::now();
    merge_sort(S, S_size, min_size_to_thread);
    t2 = std::chrono::high_resolution_clock::now();
    std::cout << "size > " << min_size_to_thread << ": " << (t2 - t1) / std::chrono::milliseconds(1) << std::endl;

    for (int i = 0; i < S_size; ++i)
    {
        S[i] = i % 255;
    }

    min_size_to_thread = 1000;
    t1 = std::chrono::high_resolution_clock::now();
    merge_sort(S, S_size, min_size_to_thread);
    t2 = std::chrono::high_resolution_clock::now();
    std::cout << "size > " << min_size_to_thread << ": " << (t2 - t1) / std::chrono::milliseconds(1) << std::endl;

    for (int i = 0; i < S_size; ++i)
    {
        S[i] = i % 255;
    }

    min_size_to_thread = 10000;
    t1 = std::chrono::high_resolution_clock::now();
    merge_sort(S, S_size, min_size_to_thread);
    t2 = std::chrono::high_resolution_clock::now();
    std::cout << "size > " << min_size_to_thread << ": " << (t2 - t1) / std::chrono::milliseconds(1) << std::endl;

    for (int i = 0; i < S_size; ++i)
    {
        S[i] = i % 255;
    }

    min_size_to_thread = 250000;
    t1 = std::chrono::high_resolution_clock::now();
    merge_sort(S, S_size, min_size_to_thread);
    t2 = std::chrono::high_resolution_clock::now();
    std::cout << "size > " << min_size_to_thread << ": " << (t2 - t1) / std::chrono::milliseconds(1) << std::endl;

    for (int i = 0; i < S_size; ++i)
    {
        S[i] = i % 255;
    }

    min_size_to_thread = 500000;
    t1 = std::chrono::high_resolution_clock::now();
    merge_sort(S, S_size, min_size_to_thread);
    t2 = std::chrono::high_resolution_clock::now();
    std::cout << "size > " << min_size_to_thread << ": " << (t2 - t1) / std::chrono::milliseconds(1) << std::endl;

    return 0;
}
模板
无效合并\排序(TS[],整数S\大小,整数最小\大小\到\线程)
{
如果(S_大小<2)返回;
//左序列
int L_size=S_size/2;
T*L=新的T[L_尺寸];
对于(int i=0;i最小尺寸到螺纹)
{
std::线程左(合并排序、L、L大小、最小大小到线程);
std::thread thread\u right(合并\u排序、R、R\u大小、最小\u大小\u到\u thread);
右螺纹。连接();
螺纹_左。连接();
}
其他的
{
合并排序(L、L大小、最小大小到线程);
合并排序(R、R大小、最小大小到线程);
}
int S_迭代器=0;
int L_迭代器=0;
int R_迭代器=0;
while((L_迭代器std::cout我认为这是缓存的问题。具体地说,错误共享会减慢算法的速度,因为数据会写入多个线程之间共享的页面。(不同的处理器内核试图跟上共享内存页面)如果
min\u size\u to\u thread
是处理器页面大小的倍数,并且数组在页面边界上对齐,则性能会提高。在这种情况下,线程之间不会共享页面

我总是限制线程的创建数量不变,在四核机器上运行100个线程只是为了对阵列进行排序是没有意义的。在单核上运行多个线程会由于频繁的上下文切换而产生成本。根据我的经验,最大线程数始终是核心数乘以2。单核can处理大约2个线程而不降低性能成本。对于四核CPU,程序一次最多应运行8个线程。 这意味着一个算法可以创建8个子线程,父线程只需
join
s线程,创建7个子线程,在父线程中运行部分算法,最后
join
其他7个线程


总是概要文件,它可能有完全不同的原因。

我编译并运行了您的精确程序,除了添加include之外,没有任何修改,结果或多或少与您预期的一样:

size > 250: 169
size > 500: 85
size > 1000: 50
size > 10000: 29
size > 250000: 42
size > 500000: 89

根据您的屏幕截图,我推测您正在Visual Studio中运行代码。默认的运行按钮会将调试器附加到可执行文件并降低运行时性能。相反,请按Ctrl+F5在不使用调试器的情况下运行,或从菜单“调试”->“开始而不使用调试”。

不应复制子数组。也就是说这会降低您的性能。只需保留一个数组,然后传递您正在使用的索引,尽管这会导致错误的共享问题。@NathanOliver这就是线程速度较慢的原因吗?很可能是。使用
thread\u right
根本没有意义,因为您创建了它并立即等待它(在左侧等待后),因此原始线程在完成之前不会执行任何操作。创建左线程后,在当前线程中执行右线程操作,然后等待左线程完成。您的实现创建并连接两个线程,而您已经有一个热线程和已计划的线程自动驻车。您至少应该在在对当前线程中的右侧进行排序时创建新线程(然后加入)反之亦然。我是新来的,所以我必须阅读有关错误共享和分析的内容。但是对于你答案的第二部分:只看最后两个测试,其中
min\u size\u to\u thread=250000
500000
:第一个在两个线程上运行,第二个在单个线程上运行。单个线程仍然更快。这是一个发布版本吗ld或调试版本?我使用cl.exe所有默认设置生成。奇怪。我尝试在不调试的情况下启动,但仍然得到相同的结果(只是速度要快得多)。获得了
875、810、761、768、536、449
。更改为发行版x64并在不使用调试的情况下启动。现在我得到了与您相同的结果。谢谢