有哪些策略可以有效地处理异构多核架构上的并发读取?

时间:2010-04-15 08:46:46

标签: c++ multithreading boost concurrency

我正在应对使用 8核机器和高端 GPU(Tesla 10)的功能的挑战。

我有一个大的输入文件,每个核心一个线程,一个用于GPU处理。 为了高效,Gpu线程需要来自输入的大量行 Cpu线程只需要一行(在临时缓冲区中存储多行的速度较慢)。该文件不需要按顺序读取。我正在使用提升

我的策略是在输入流上设置互斥,每个线程锁定 - 解锁。 这不是最优的,因为在锁定互斥锁时,gpu线程应该具有更高的优先级,是最快且最苛刻的互斥锁。

我可以提出不同的解决方案,但在急于实施之前,我想有一些指导方针。

您使用/推荐什么方法?

3 个答案:

答案 0 :(得分:2)

如果“每个线程1行”不是严格要求,您可能根本不需要锁定,有时可以最多2行或3行。然后,您可以根据公式平等地拆分文件。假设您要以1024 KB的块总数读取文件(也可能是千兆字节):您可以通过优先级将其拆分为核心。所以:

  • #define BLOCK_SIZE (1024 * 1024)
  • #define REGULAR_THREAD_BLOCK_SIZE (BLOCK_SIZE/(2 * NUM_CORES)) // 64kb
  • #define GPU_THREAD_BLOCK_SIZE (BLOCK_SIZE/2)
  • 每个核心获得64 KB块
    • 核心1:偏移0,大小= REGULAR_THREAD_BLOCK_SIZE
    • 核心2:偏移65536,大小= REGULAR_THREAD_BLOCK_SIZE
    • Core 3:offset 131072,size = REGULAR_THREAD_BLOCK_SIZE
    • 核心n:偏移(n * REGULAR_THREAD_BLOCK_SIZE),尺寸= REGULAR_THREAD_BLOCK_SIZE
  • GPU获得512 KB,偏移量= (NUM_CORES * REGULAR_THREAD_BLOCK_SIZE),尺寸= GPU_THREAD_BLOCK_SIZE

理想情况下,它们不会重叠。有些情况下它们可以重叠。由于您正在读取文本文件,因此一行可能会落入下一个核心块中。为了避免重叠,你总是跳过其他核心的第一行,并且总是完成最后一行,假设下一个线程无论如何都会跳过它,这里是伪代码:

void threadProcess(buf, startOFfset, blockSize)
{
    int offset = startOffset;
    int endOffset = startOffset + blockSize;
    if(coreNum > 0) {
        // skip to the next line
        while(buf[offset] != '\n' && offset < endOffset) offset++;
    }
    if(offset >= endOffset) return; // nothing left to process
    // read number of lines provided in buffer
    char *currentLine = allocLineBuffer(); // opening door to security exploits :)
    int strPos = 0;
    while(offset < endOffset) {
        if(buf[offset] == '\n') {
            currentLine[strPos] = 0;
            processLine(currentLine); // do line processing here
            strPos = 0; // fresh start
            offset++;
            continue;
        }
        currentLine[strPos] = buf[offset];
        offset++;
        strPos++;
    }
    // read the remaineder past the buf
    strPos = 0;
    while(buf[offset] != '\n') {
        currentLine[strPos++] = buf[offset++];
    }
    currentLine[strPos] = 0;
    processLine(currentLine); // process the carryover line
}

正如您所看到的,这会并行化读取块的处理而不是读取本身。你如何并行读取?最棒的方法是将整个块映射到内存中。这将获得最佳的I / O性能,因为它是最低级别。

答案 1 :(得分:1)

一些想法:

1)因为瓶子不在IO中,所以文件应该几乎完全保存在RAM中以便于访问。

2)实现不应该允许线程阻塞。如果减少阻塞,最好采用略微非最佳的解决方案。

假设我们有大数据文件线程可以在黑暗战术中使用镜头。这意味着一旦线程获得锁定,它只会增加fpos并解锁内存。然后它授予自己处理它刚刚获得的内存部分的权限。例如,线程可以处理片段中开头的所有行。

成果:

1)线程几乎不可能阻塞。锁定时间非常短(在几条指令的范围内+刷新缓存的时间)

2)灵活性。线程可以根据需要获取尽可能多的数据。

当然,应该有一些机制来适应数据文件中的行长度,以避免最坏的情况。

答案 2 :(得分:0)

我会使用缓冲区。让一个线程从磁盘填充该缓冲区。每个线程将锁定缓冲区,将数据读入线程的缓冲区,然后在处理数据之前释放互斥锁上的锁。