我正在应对使用 8核机器和高端 GPU(Tesla 10)的功能的挑战。
我有一个大的输入文件,每个核心一个线程,一个用于GPU处理。 为了高效,Gpu线程需要来自输入的大量行 Cpu线程只需要一行(在临时缓冲区中存储多行的速度较慢)。该文件不需要按顺序读取。我正在使用提升。
我的策略是在输入流上设置互斥,每个线程锁定 - 解锁。 这不是最优的,因为在锁定互斥锁时,gpu线程应该具有更高的优先级,是最快且最苛刻的互斥锁。
我可以提出不同的解决方案,但在急于实施之前,我想有一些指导方针。
您使用/推荐什么方法?
答案 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)
(n * REGULAR_THREAD_BLOCK_SIZE)
,尺寸= REGULAR_THREAD_BLOCK_SIZE
(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)
我会使用缓冲区。让一个线程从磁盘填充该缓冲区。每个线程将锁定缓冲区,将数据读入线程的缓冲区,然后在处理数据之前释放互斥锁上的锁。