并行插入排序,幼稚尝试,使用pthread尝试失败,未并行排序但可与串行正常工作的线程

如何解决并行插入排序,幼稚尝试,使用pthread尝试失败,未并行排序但可与串行正常工作的线程

上下文

你好!

我尝试使用pthreads,并决定实施插入排序以查看性能差异,但是我遇到了一个奇怪的错误,我不确定要去哪里或我真正在运行什么问题进入。

步骤

对于上下文,我对并行插入排序所做的工作如下,

  1. 使功能完全包含在其中
  2. 向该函数发送一个结构容器,i)指向数组本身的指针,ii)数组 大小,
  3. 获得了内核数量(通过我的计算机上的func将此称为numberOfCores(是的,输出是正确的,我有4个内核)
  4. 创建了numberOfCores个pthread_t对象
  5. 请记住,最后一个线程不必处理包含相等数量元素的数组(尝试将余额相应地分配给线程)
  6. 因此,我创建了一个二维矩阵,其中行== {numberOfCores - 1,行== {floor(sizeOfArray / numberOfCores),我用不同的测试输入反复检查,这些输入已正确分配,
  7. 制作了一个lastArray数组,大小为lastArraySize = (sizeOfCompleteArray / numberOfCores) + (sizeOfCompleteArray % numberOfCores)
  8. 然后我将原始数组拆分为subArrays(二维矩阵)
  9. 然后我将数组的最后一部分分割为lastArray
  10. 然后,我打包各个数组及其大小,并分配线程以运行简单的insertionSort函数,并发送各自的packed数据。我的计划是在每个单独的线程对不同的数组进行排序之后merge。我知道有可能100%编写更有效的代码库,但这只是很小的一部分,我不想花太多钱。请逐行检查以下代码。
  11. 然后我使用pthread_join来获取numberOfCores数组的排序结果
  12. 然后我检查每一个是否已排序。

问题

问题出在这里

  1. 如果我使用循环从第9步(如上所述)开始按顺序对上述数组进行排序,则所有数组都会按预期进行排序。没问题
  2. 但是,如果尝试使用并行版本,则会遇到意外结果。有时2被排序,有时仅1,有时3,但是从不4。为什么它一直给我带来不可靠的结果,这在我眼前已经无济于事。
  3. 数组[0]始终未排序。我不知道为什么。当我使用上述串行方法时,它确实得到了排序,但从没有使用并行方法。

代码


// def for InsertionSortPacking,mentioned below 
struct InsertionSortPacking
{
    ull *Array;       // Pointer to the array
    size_t ArraySize; // Size of the array
};

static inline void *
pthread_insertionSort(void *arrayMetaDataToUnpack) // arrayMetaDataToUnpack contains the pointer used to pass the data to the func
{
    // UNPACKING START
    ull *_Array = ((InsertionSortPacking *)arrayMetaDataToUnpack)->Array;
    size_t sizeOfArray = ((InsertionSortPacking *)arrayMetaDataToUnpack)->ArraySize;
    // UNPACKING END

    // Compulsory use of reinterpret_cast to keep everything consistent
    ull *__tempArray = reinterpret_cast<ull *>(_Array);

    // Custom func to get the number of cores on machine
    int numberOfCores = getNumCores();
    std::cout << "Number of cores detected: " << numberOfCores << ".\n";

    // Step 1,create vars
    pthread_t threads[numberOfCores];              // all threads must run,step 4
    int rows = numberOfCores - 1;                  // ... but not every thread might be bound to have equal job ( equal sub array sizes ),step 5
    int cols = floor(sizeOfArray / numberOfCores); // the most evenly contained size possible,step 6
    ull subArrays[rows][cols]{0u};                 // the {} initializes everything for me,step 6

    // step 7
    int lastArraySize = (sizeOfArray / numberOfCores) + (sizeOfArray % numberOfCores);
    ull lastArray[lastArraySize];

    // step 8
    for (int i = 0; i < rows; ++i)
        for (int j = 0; j < cols; ++j)
            subArrays[i][j] = __tempArray[i * numberOfCores + j];

    // step 9
    for (int i = 0,j = cols * rows + 1;
         i < lastArraySize and j < sizeOfArray;
         ++i,++j)
        lastArray[i] = __tempArray[j];

    // EXTRA,just for clarification. Individually,all work fine
    // getTimeForTemplate just prints out some logs,takes in the
    // the function pointer to the function I want to use,the array,I want to sort,the size,and a text to print
    // for (int i = 0; i < rows; ++i)
    //     getTimeForTemplate(insertionSort,subArrays[i],cols,"insertion sort - serial,subArray[" + std::to_string(i) + std::string("]"));
    // getTimeForTemplate(insertionSort,lastArray,lastArraySize,lastArray");

    // step 10
    for (int i = 0; i <= rows; ++i)
    {
        InsertionSortPacking __tempPacking{};

        if (i == rows) // Step 3.1,for the lastArray
        {
            __tempPacking.Array = (ull *)lastArray;
            __tempPacking.ArraySize = lastArraySize;
        }
        else // Step 3.2,for the remaining arrays
        {
            __tempPacking.Array = (ull *)subArrays[i];
            __tempPacking.ArraySize = cols;
        }

        int __rc = pthread_create(&threads[i],nullptr,insertionSort,(void *)&__tempPacking);
        if (__rc)
        {
            std::cout << "[ERROR] Unable to create thread,rc " << __rc << " i," << i << std::endl;
            exit(-1);
        }
    }

    // step 11,joining the pthreads,regardless of array size
    for (int i = 0; i <= rows; ++i)
    {
        int __rc = pthread_join(threads[i],nullptr);
        if (__rc)
        {
            std::cout << "[ERROR] Unable to join thread," << i << std::endl;
            exit(-1);
        }
    }

    // Step 12,checking if all the jobs have completed the sorting
    for (int i = 0; i <= rows; ++i)
    {
        InsertionSortPacking __tempPacking{};

        if (i == rows) // Step 3.1,for the lastArray
        {
            __tempPacking.Array = (ull *)lastArray;
            __tempPacking.ArraySize = lastArraySize;

            if (isSorted(&__tempPacking) == -1) // Sorting succeeded if -1 returned
                std::cout << "Sorting succeeded for lastArrray\n";
            else
                std::cout << "Sorting failed for lastArray\n";
        }
        else // Step 3.2,for the remaining arrays
        {
            __tempPacking.Array = (ull *)subArrays[i];
            __tempPacking.ArraySize = cols;

            if (isSorted(&__tempPacking) == -1) // Sorting succeeded if -1 returned
                std::cout << "Sorting succeeded for subArray[" << i << "]\n";
            else
                std::cout << "Sorting failed for subArray[" << i << "]\n";
        }
    }

    ...
    // further code for merging and whatnot

    return sortedArray;

还有我用来编译和运行的命令

g++ -std=gnu++17 -std=c++17 -O2 insertion.cpp -o insertion -lpthread && ./insertion > log.log

以下是完整程序https://gist.github.com/Rubix982/d0bbdabc2d8a1fc531e9104f7172dbfe

中的一些日志

我有什么问题,为什么不能解决?

  1. 这似乎根本不是比赛条件。每个阵列都是不同的,并且在内存中是独立的。没有两个线程可以在序列中的任何地方访问不同的线程
  2. ...可能是线程在部分排序时有时会联接-会发生这种情况吗?
  3. 我运行的线程数没有超出我的计算机处理能力(肯定有4个内核)
  4. 我不明白在哪里以及如何调试为什么有时1个线程失败,或者为什么3个线程又一次失败
  5. 我根本看不到互斥锁的需要。没有比赛条件,但也许我是从错误的角度考虑这个问题
  6. 我尝试使用sleep()来确保线程在将它们连接在一起之前按时完成其工作,但无济于事。

结论

任何帮助将不胜感激!请让我知道我可能在哪里出错,以及调试和修复此错误的可能方法。 我知道我不应该使用选择排序/插入排序进行并行化,而应该使用合并排序和快速排序及其并行版本,但这只是出于娱乐和学习目的。

谢谢!

解决方法

启动线程存在严重问题:

    for (int i = 0; i <= rows; ++i)
    {
        InsertionSortPacking __tempPacking{};

请注意,__tempPacking的生存期是主机循环的一次迭代,但是在这里...

[...]

        int __rc = pthread_create(&threads[i],nullptr,insertionSort,(void *)&__tempPacking);

...您将指向__tempPacking的指针传递到新线程。一旦该对象的生存期结束,新线程就不得尝试访问它,但是即使所有insertionSort()都执行了该操作,也已将成员读回到局部变量中,没有任何东西强制在对象的生存期内完成。这是一个一般的竞争条件,即使不是语言标准定义的“数据竞争”,当排​​序线程丢失时,结果行为也是不确定的。

        if (__rc)
        {
            std::cout << "[ERROR] Unable to create thread,rc " << __rc << " i," << i << std::endl;
            exit(-1);
        }
    }

您继续评论

  1. 这似乎根本不是比赛条件。每个阵列都是不同的,并且在内存中是独立的。没有两个线程可以访问不同的线程 在序列中的任何地方穿线

参见上文。确实存在竞争条件,只是与您正在查看的对象无关。

  1. ...可能是线程在部分排序时有时会联接-会发生这种情况吗?

由于您拥有UB,所以任何事情都会发生。在没有UB的情况下,连接线程会使连接器等待线程功能终止。您还没有展示它,所以我不能说您的insertionSort()函数是否可能在不对数组进行完全排序的情况下容易终止,但这并不是它在多线程程序中使用所特有的特性

  1. 我运行的线程数没有超出我的计算机处理能力(肯定有4个内核)

那没什么大不了的。您可以拥有比核心更多的线程。它们只是不会同时运行。

  1. 我不明白在哪里以及如何调试为什么有时1个线程失败,或者为什么3个线程又一次失败

是的,调试多线程程序可能非常困难。许多调试器可以运行多线程代码并询问不同线程的状态,但是与线程相关的错误特别可能对执行环境敏感,因此在调试器中运行时它们可能不会显现。

  1. 我根本看不到互斥锁的需要。没有比赛条件,但也许我是从错误的角度考虑这个问题

通过确保提供给每个线程的InsertionSortPacking对象在保持每个线程拥有自己独特的属性的同时保留每个线程的寿命,可以避免使用同步对象。

或者,您可以动态分配它们,并赋予排序线程删除它们的责任。

  1. 我尝试使用sleep()来确保线程在将它们连接在一起之前按时完成其工作,但无济于事。

有时sleep()可以解决同步问题,但是它从来都不是真正的解决方案。如果存在比赛状况,那么sleep()可以使每个竞争对手获胜的可能性有所偏差,但这并不能解决首先出现比赛的问题。

在这种情况下,如果将sleep()放在线程启动循环的末尾而不是在线程启动循环的末尾,它实际上可能会阻止问题的出现,但这会破坏您的目的,并且再次,才是真正的解决方案。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-