当前位置: 首页 > 面试题库 >

交错并行文件读取比顺序读取慢?

公良琛
2023-03-14
问题内容

我实现了一个小的IO类,它可以从不同磁盘(例如,两个包含相同文件的硬盘)上的多个相同文件中读取数据。在顺序情况下,两个磁盘在文件上的平均读取速度均为60MB
/ s,但是当我进行交错操作(例如4k磁盘1、4k磁盘2然后合并)时,有效读取速度会降低到40MB / s而不是增加吗?

上下文:Win 7 + JDK 7b70、2GB RAM,2.2GB测试文件。基本上,我尝试以穷人的方式模仿Win7的ReadyBoost和RAID x。

从本质上讲,当向类发出read()时,它将创建两个可运行对象,并带有从特定位置和长度读取预打开的RandomAccessFile的指令。使用执行程序服务和Future.get()调用,当两者都完成时,读取的数据将被复制到公共缓冲区中并返回给调用者。

我的方法是否存在概念错误?(例如,操作系统缓存机制将始终抵消吗?)

protected <T> List<T> waitForAll(List<Future<T>> futures) 
throws MultiIOException {
    MultiIOException mex = null;
    int i = 0;
    List<T> result = new ArrayList<T>(futures.size());
    for (Future<T> f : futures) {
        try {
            result.add(f.get());
        } catch (InterruptedException ex) {
            if (mex == null) {
                mex = new MultiIOException();
            }
            mex.exceptions.add(new ExceptionPair(metrics[i].file, ex));
        } catch (ExecutionException ex) {
            if (mex == null) {
                mex = new MultiIOException();
            }
            mex.exceptions.add(new ExceptionPair(metrics[i].file, ex));
        }
        i++;
    }
    if (mex != null) {
        throw mex;
    }
    return result;
}

public int read(long position, byte[] output, int start, int length) 
throws IOException {
    if (start < 0 || start + length > output.length) {
        throw new IndexOutOfBoundsException(
        String.format("start=%d, length=%d, output=%d", 
        start, length, output.length));
    }
    // compute the fragment sizes and positions
    int result = 0;
    final long[] positions = new long[metrics.length];
    final int[] lengths = new int[metrics.length];
    double speedSum = 0.0;
    double maxValue = 0.0;
    int maxIndex = 0;
    for (int i = 0; i < metrics.length; i++) {
        speedSum += metrics[i].readSpeed;
        if (metrics[i].readSpeed > maxValue) {
            maxValue = metrics[i].readSpeed;
            maxIndex = i;
        }
    }
    // adjust read lengths
    int lengthSum = length;
    for (int i = 0; i < metrics.length; i++) {
        int len = (int)Math.ceil(length * metrics[i].readSpeed / speedSum);
        lengths[i] = (len > lengthSum) ? lengthSum : len;
        lengthSum -= lengths[i];
    }
    if (lengthSum > 0) {
        lengths[maxIndex] += lengthSum;
    }
    // adjust read positions
    long positionDelta = position;
    for (int i = 0; i < metrics.length; i++) {
        positions[i] = positionDelta;
        positionDelta += (long)lengths[i]; 
    }        
    List<Future<byte[]>> futures = new LinkedList<Future<byte[]>>();
    // read in parallel
    for (int i = 0; i < metrics.length; i++) {
        final int j = i;
        futures.add(exec.submit(new Callable<byte[]>() {
            @Override
            public byte[] call() throws Exception {
                byte[] buffer = new byte[lengths[j]];
                long t = System.nanoTime();
                long t0 = t;

                long currPos = metrics[j].handle.getFilePointer();
                metrics[j].handle.seek(positions[j]);
                t = System.nanoTime() - t;
                metrics[j].seekTime = t * 1024.0 * 1024.0 / 
                    Math.abs(currPos - positions[j]) / 1E9 ;

                int c = metrics[j].handle.read(buffer);
                t0 = System.nanoTime() - t0;
                // adjust the read speed if we read something
                if (c > 0) {
                    metrics[j].readSpeed = (alpha * c * 1E9 / t0 / 1024 / 1024
                    + (1 - alpha) * metrics[j].readSpeed) ;
                }
                if (c < 0) {
                    return null;
                } else
                if (c == 0) {
                    return EMPTY_BYTE_ARRAY;
                } else
                if (c < buffer.length) {
                    return Arrays.copyOf(buffer, c);
                }
                return buffer;
            }
        }));
    }
    List<byte[]> data = waitForAll(futures);
    boolean eof = true;
    for (byte[] b : data) {
        if (b != null && b.length > 0) {
            System.arraycopy(b, 0, output, start + result, b.length);
            result += b.length;
            eof = false;
        } else {
            break; // the rest probably reached EOF
        }
    }
    // if there was no data at all, we reached the end of file
    if (eof) {
        return -1;
    }
    sequentialPosition = position + (long)result;

    // evaluate the fastest file to read
    double maxSpeed = 0;
    maxIndex = 0;
    for (int i = 0; i < metrics.length; i++) {
        if (metrics[i].readSpeed > maxSpeed) {
            maxSpeed = metrics[i].readSpeed;
            maxIndex = i;
        }
    }
    fastest = metrics[maxIndex];
    return result;
}

(指标数组中的FileMetrics包含读取速度的测量值,以自适应地确定各种输入通道的缓冲区大小-在我的测试中,alpha = 0和readSpeed =
1结果均等分布)

编辑 我进行了一个无纠缠的测试(例如,在单独的线程中分别读取两个文件。),我的综合有效速度为110MB / s。

Edit2 我想我知道为什么会这样。

当我按顺序并行读取时,它不是对磁盘的顺序读取,而是由于交错(并且可能与分配表查找有关)而引起的“读取-跳过-读取-
跳过”模式。基本上,这会将每个磁盘的有效读取速度降低到一半或更低。


问题答案:

如您所说,在磁盘上进行顺序读取要比读取-跳过-读取-跳过模式快得多。硬盘在顺序读取时具有较高的带宽,但是查找时间(延迟)很昂贵。

与其将文件的副本存储在每个磁盘中,不如将文件的块i存储在磁盘i(mod 2)上。这样,您可以依次从两个磁盘读取数据并将结果重新组合到内存中。



 类似资料:
  • 问题内容: 我有一个大文件,需要阅读并制作字典。我希望尽快。但是我在python中的代码太慢了。这是显示问题的最小示例。 首先制作一些假数据 现在,这里是一个最小的python代码片段,可以读入它并制作一个字典。 时间: 但是,可以更快地读取整个文件,如下所示: 我的CPU有8个核心,是否可以在python中并行化此程序以加快速度? 一种可能是读取大块输入,然后在不同的非重叠子块上并行运行8个进程

  • 问题内容: 我有一个顺序文件,它是hadoop map- reduce作业的输出。在此文件中,数据以键值对的形式写入,而值本身是一个映射。我想将值读取为MAP对象,以便我可以进一步处理它。 程序输出:关键是:[this is key]值是:{abc = 839177,xyz = 548498,lmn = 2,pqr = 1} 在这里,我以字符串的形式获取值,但我希望将其作为map的对象。 问题答案

  • 我在标准Spring Boot应用程序的resources文件夹下有以下文件。Spring忙碌的profile设置为dev,属性文件的读取顺序是什么。?

  • 问题内容: 我有一个Java ee应用程序,在该应用程序中,我使用servlet来打印使用log4j创建的日志文件。在读取日志文件时,通常会寻找最后一个日志行,因此,如果servlet以相反的顺序打印日志文件,则它会更加有用。我的实际代码是: 我在互联网上找到的实现涉及使用StringBuffer并在打印之前加载所有文件,难道没有找到找到文件结尾并读取内容直到文件开头的代码轻巧的方式吗? 问题答案

  • 在COBOL中,我从顺序行文件中读取。一行行,到EOF,诸如此类的 行的长度从40到80个字符不等。我需要知道,每行有多少个字符。但是行可以以一些空格结束,我也需要计算。所以我不能在程序中从变量取字符串的长度。READ语句是否有返回值,它返回已读行的字符数(直到达到CRLF)?

  • 第一次使用python。我正在尝试浏览包含段落和表格的word文档。我已经弄清楚了如何使用以下代码浏览文档中的所有段落和文档中的所有表格: 但我正试图找到一种方法,像任何阅读它的人一样,有序地浏览这份文件。所以如果我们有一份文件包含: 它会按照这个顺序读。我想这样做的原因是,根据表格后面的段落,我想对它执行不同的操作。