【问题标题】:Parse c++ string using multiple threads使用多个线程解析 C++ 字符串
【发布时间】:2013-06-25 12:37:57
【问题描述】:

我已经将一个巨大的文件映射到 char 字符串中,并用它制作了一个 c++ 字符串。我需要根据作为空格字符的分隔符来解析这个字符串,并将值存储在矩阵中。我可以从一个线程做到这一点,但我需要优化它。所以我使用多个线程来解析这个 sstream 中的字符串并将其存储在 matrix 中。虽然基于线程 id,我可以将解析的数据同步存储到矩阵中,但是我如何同步解析,因为任何线程都可以随时调度并解析字符串。这是我的代码

void* parseMappedString(void* args)
{
    char temp[BUFFSIZE];
    long int threadID = *((long int*)args);
    if (threadID  < 0)
        threadID = 0;

    for (int i = ((threadID) * 160); i < ((threadID+1) * 160); i++)
    {
        for (int j = 0; j < 4000; j++)
        {
            pthread_mutex_lock(&ParseMatrixMutex);
            if ((matrix_str.getline(temp,BUFFSIZE, ' ')) )
            {
                pthread_mutex_unlock(&ParseMatrixMutex);
                matrix[i][j] = parseFloat((temp));
            }
            else
            {
                pthread_mutex_unlock(&ParseMatrixMutex);
            }
        }
    }
}

void create_threads_for_parsing(void)
{
    long int i;

    for (i = 0; i < 5; i++)
        pthread_create(&Threads[i], NULL, parseMappedString, (void*)&i);
}

如果您在代码中看到总共有五个线程,并且每个线程正在处理 160 * 4000 个元素。他们根据他们的线程 id 存储,因此存储在矩阵中的唯一位置。这样它是同步的。但是 getline 可以随时由任何线程完成,因此线程 5 可以解析属于第一个线程的数据。我该如何避免这种情况?

我不得不关注,因为我在 args 中收到 1-4 个线程 ID,但从来没有 0。它总是以一些垃圾负值的形式出现,因此我不得不像这样对其进行硬编码。

如果(线程 ID

【问题讨论】:

  • 想法是线程 1 不应该解析字符串,例如178 * 278 元素,因为它属于线程 2
  • 您如何期望线程 2 在不读取所有前面的元素或等待线程 1 完成读取“其”部分的情况下定位该元素?为了做你想做的事,你需要每个线程都等待前一个线程完成,所以你不会通过多线程获得任何东西。
  • 是不是说没有办法优化文件读取。我需要 1.5 秒才能读取包含 4k *4k 浮点数的文本文件并将其存储在矩阵中。没办法优化???
  • 您预计它的速度有多快? 1.5 秒内 1600 万次浮点数约为每个浮点数 100 纳秒,这在 3GHz CPU 上约为 300 个时钟。这还不错。如果您希望它们更快,请将它们存储为二进制以避免解析。

标签: c++ parsing pthreads sstream


【解决方案1】:

我已经将一个巨大的文件映射到 char 字符串中并制作了一个 c++ 字符串

不要,std::string 必须复制内存,这样你就失去了 mmap 本来可以得到的性能改进。只需将原始内存作为 char 数组处理

我可以从一个线程完成,但我需要优化它

你确定多个线程优化它吗?您是否分析并确认它确实受 CPU 限制而不是 I/O 限制?


如果您确定要使用多线程,我建议您这样做:

  1. 创建N个线程(这个要根据核数再根据测试结果调整)
  2. 将您的 mmap 区域分割成 N 个大小大致相等的块
    • 您可以来回搜索最接近块边界的换行符
  3. 让每个线程n创建自己的独立输出
  4. 之后合并所有输出

至于代码中的错误,我试图说服您不要使用:您将(void*)&amp;i 作为参数传递给线程函数。这是一个指向自动本地的指针,它在create_threads_for_parsing 的末尾超出范围,因此在任何线程读取它时它很可能是随机垃圾。 即使它不是随机垃圾(即,如果create_threads_for_parsing 在返回之前加入所有线程,以保持i 在范围内),它也将是同一个指针 对于每个线程。

为了安全地将不同的整数 id 传递给每个线程,您应该为每个线程分配一个不同的整数,并传递其地址。要么就是这样,要么与intptr_t搞混。

【讨论】:

  • 如果读取整个文件内容,我希望从完全线性读取开始到结束获得最佳结果。交错阅读四个季度通常会导致颠簸和严重减速。在这种情况下,我根本不会使用 mmap。
  • 你能告诉我为什么我没有在我的 parseMappedString 函数中正确获取线程 ID。我从来没有得到 0 作为我应该的 threadid。有时全为 0:
  • @BalogPal 如果文件足够大,可以跨越 RAID 条带,那么并行读取可能会有用,但我同意你的怀疑
【解决方案2】:

std::string::getline 不是线程安全的,您不能在不同的线程中使用getline()

您要么需要使用strncopy(c 样式)访问内存中原始字符串数据中的已知位置

strncopy(matrix_str.c_str(), temp, 4000);

或使用substring-function(C++ 风格)

std::string piece = matrix_str.substr(i,4000)

编辑:如果您的 matrix_str 不是 std::string 而是 std::sstream 对象,则这将不起作用,因为必须按顺序访问流。你的问题在这方面有点含糊......

【讨论】:

  • 你能告诉我为什么我没有在我的 parseMappedString 函数中正确获取线程 ID。我从来没有得到 0 作为我应该的 threadid。有时全为 0 :(
  • 您正在传递一个指向包含您的 threadID 的新线程的指针。但是这个指针指向的数据在你的 for 循环中发生了变化。因此,如果你很幸运,它可以工作,但如果没有,你会在 for 循环更改它时读取一个值。
【解决方案3】:

代码几乎是完全互斥的——因此根本没有必要使用线程。

颚化的想法是允许同时实际完成工作。为此,您应减少数据共享,理想情况下为零。

就像将大字符串预先分成 4 部分并将其发布到线程,以便他们可以读取和处理它,并将结果也放在他们独有的位置。如果没有共享单元格,则输出可以转到矩阵,但请注意false sharing 仍然可能破坏性能。

【讨论】:

  • 为什么解锁是错位。什么情况下不解锁。
  • 我忽略了——见修改后的帖子
【解决方案4】:

关于奇怪的 0 ID 部分:我认为发布的代码只是演示,但您可能真的是这样。

您必须在离开函数 create_threads_for_parsing 之前加入所有线程。目前,您将指针传递给线程,该指针指向其中的局部变量。

更糟糕的是,变量是共享的,所以你有一个竞争条件。你可以这样做:

static const int ids = {0, 1, 2, 3, 4};

并传递一个指向循环中正确单元格的指针。

【讨论】:

  • 仍然是错误的,因为它们都得到了一个指向 same 局部变量的指针。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-10-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多