0

我已经将一个巨大的文件映射到 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 < 0)线程 ID = 0;

4

4 回答 4

1

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

不要,std::string必须复制内存,所以你失去了性能改进,否则 mmap 会得到你。只需将原始内存作为 char 数组处理

我可以从一个线程做到这一点,但我需要优化它

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


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

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

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

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

于 2013-06-25T13:46:57.163 回答
0

std::string::getline不是线程安全的,你不能getline()从不同的线程中使用。

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

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

或使用子字符串函数(C++ 风格)

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

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

于 2013-06-25T13:13:16.723 回答
0

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

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

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

于 2013-06-25T13:17:05.970 回答
0

在奇怪的 0 ID 部分:我认为发布的代码只是演示,但你可能会像字面那样理解它。

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

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

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

并将指针传递给循环中的正确单元格。

于 2013-06-25T14:08:02.303 回答