代码之家  ›  专栏  ›  技术社区  ›  fabmilo

在异构多核架构上处理并发读取的有效策略是什么?

  •  3
  • fabmilo  · 技术社区  · 16 年前

    我正在应对使用 8核 机器和高端 GPU(特斯拉10) .

    我有一个大的输入文件,每个核心有一个线程,GPU处理有一个线程。 为了提高效率,GPU线程需要输入大量的行,而 CPU线程只需要一行就可以继续(在临时缓冲区中存储多行的速度较慢)。不需要按顺序读取文件。我正在使用 促进 .

    我的策略是 互斥 在输入流和每个线程上 - 解锁 它。 这不是最佳的,因为在锁定互斥体时,GPU线程应该具有更高的优先级,这是最快和要求最高的线程。

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

    你使用/推荐什么方法?

    3 回复  |  直到 16 年前
        1
  •  2
  •   Sedat Kapanoglu johnnywhoop    16 年前

    如果“每个线程一行”不是一个严格的要求,您可能根本不需要锁定,并且有时最多可以执行两行或三行。然后,您可以根据公式平均拆分文件。假设您希望读取总共1024 kbyte块中的文件(也可以是千兆字节):您将其拆分为具有优先级的核心。所以:

    • #define BLOCK_SIZE (1024 * 1024)
    • #define REGULAR_THREAD_BLOCK_SIZE (BLOCK_SIZE/(2 * NUM_CORES)) // 64kb
    • #define GPU_THREAD_BLOCK_SIZE (BLOCK_SIZE/2)
    • 每个核心获得64kb块
      • 芯1:偏移0,尺寸=普通螺纹块尺寸
      • 芯2:偏移量65536,尺寸=普通螺纹块尺寸
      • 芯3:偏移量131072,尺寸=普通螺纹块尺寸
      • 核心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性能,因为它是最低的级别。

        2
  •  1
  •   user283145    16 年前

    一些想法:

    1)由于瓶颈不在IO中,因此文件应几乎完全保存在RAM中,以便于访问。

    2)实现不应允许线程阻塞。如果能减少阻塞,最好有稍微非最优的解决方案。

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

    结果:

    1)螺纹几乎不可能堵塞。锁定时间非常短(在几个指令的范围内+刷新缓存的时间)

    2)灵活性。线程可以接受任意多的数据。

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

        3
  •  0
  •   Menox    16 年前

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