【问题标题】:What's the fastest way to write a very small string to a file in Java?用Java将一个非常小的字符串写入文件的最快方法是什么?
【发布时间】:2013-09-25 14:14:32
【问题描述】:

我的代码需要取一个 0 到 255 之间的整数值并将其作为字符串写入文件。它需要快速,因为它可能会被非常快速地重复调用,因此任何优化都会在重负载下变得明显。关于将大量数据写入文件的有效方法,这里还有其他问题,但是少量数据呢?

这是我目前的方法:

public static void writeInt(final String filename, final int value)
{
    try
    {
        // Convert the int to a string representation in a byte array
        final String string = Integer.toString(value);
        final byte[] bytes = new byte[string.length()];
        for (int i = 0; i < string.length(); i++)
        {
            bytes[i] = (byte)string.charAt(i);
        }

        // Now write the byte array to file
        final FileOutputStream fileOutputStream = new FileOutputStream(filename);
        fileOutputStream.write(bytes, 0, bytes.length);
        fileOutputStream.close();
    }
    catch (IOException exception)
    {
        // Error handling here
    }
}

我认为BufferedOutputStream 在这里没有帮助:构建刷新缓冲区的开销对于 3 个字符的写入可能适得其反,不是吗?我还能做其他改进吗?

【问题讨论】:

  • 如果您确实需要在每次写入后主动刷新,那么您在代码中所做的任何事情都无济于事。缓冲区的存在是有原因的。
  • 你是否被限制在每次调用该方法时都创建和写入一个新文件?如果没有,您可以保留一个相当大的 byte[] 作为缓冲区,并且仅在程序退出或缓冲区已满时才写入文件。
  • 谢谢大家。该文件将始终存在,但每次写入时我都需要完全替换其内容。
  • 作为一名长期的 Android 开发人员,我很好奇您的用例将什么定义为“重负载”,因为移动设备上的“重负载”通常不是一件好事,因为它会破坏电池寿命并将您的设备变成真正的热砖。
  • 啊,很公平。继续。 :)

标签: java android performance file-io


【解决方案1】:

我认为这与 0-255 范围要求的要求一样有效。使用缓冲写入器的效率会降低,因为它会创建一些您不需要创建的临时结构,而写入的字节数太少。

static byte[][] cache = new byte[256][];
public static void writeInt(final String filename, final int value)
{
    // time will be spent on integer to string conversion, so cache that
    byte[] bytesToWrite = cache[value];
    if (bytesToWrite == null) {
        bytesToWrite = cache[value] = String.valueOf(value).getBytes();
    }

    FileOutputStream fileOutputStream = null;
    try {
        // Now write the byte array to file
        fileOutputStream = new FileOutputStream(filename);
        fileOutputStream.write(bytesToWrite);
        fileOutputStream.close();
    } catch (IOException exception) {
        // Error handling here
    } finally {
        if (fileOutputStream != null) {
            fileOutputStream.close()
        }
    }
}

【讨论】:

  • 这是一个很好的答案,谢谢。我喜欢缓存,String.valueOf(value).getBytes() 至少比我的代码更整洁。
  • 我肯定会在 finally 块中关闭流。在您的情况下可能会导致打开的文件处理程序过多。
  • 这不是最快的方法。您可以将其分解为最多 3 个 writeByte 操作,这应该比在访问缓存的字符串表示时冒缓存未命中风险更快
【解决方案2】:

IMO 不能让它更快。如果不是这样,BufferedOutputStream 在这里将无济于事。如果我们查看 src,我们会看到 FileOutputStream.write(byte b[], int off, int len) 直接将字节数组发送到本机方法,而 BufferedOutputStream.write(byte b[], int off, int len) 是同步并首先将数组复制到其缓冲区,然后在关闭时将字节从缓冲区刷新到实际流。

除了在这种情况下最慢的部分是打开/关闭文件。

【讨论】:

    【解决方案3】:

    我认为,这里的瓶颈是 IO,这两项改进可能会有所帮助:

    • 考虑更新的粒度。 IE。如果您的应用程序每秒需要不超过 20 次更新,那么您可以优化您的应用程序,使其每 1/20 秒写入不超过 1 次更新。根据环境的不同,这可能非常有益。
    • 事实证明,Java NIO 对于大尺寸来说要快得多,因此尝试使用小尺寸也是有意义的,例如写信给Channel 而不是InputStream

    【讨论】:

      【解决方案4】:

      很抱歉来晚了 :)

      我认为尝试优化代码可能不是正确的方法。如果您重复写入同一个小文件,并且每次都必须写入而不是在应用程序中缓冲它,那么到目前为止,最大的考虑因素将是文件系统和存储硬件。

      关键是,如果你每次都真的碰到硬件,那么你会严重破坏它。但是,如果您的系统正在缓存写入,那么您也许可以让它不经常碰到硬件:数据在到达那里之前已经被覆盖,只有新数据会被写入。

      但这取决于两件事。一方面,当您的文件系统在写入旧文件之前获得新写入时会做什么?一些文件系统可能最终仍会在日志中写入额外的条目,或者甚至将旧文件写入一个位置,然后将新文件写入另一个物理位置。那将是一个杀手。

      另一方面,当被要求覆盖某些内容时,您的硬件会做什么?如果是传统硬盘,它可能只会覆盖旧数据。如果它是闪存(如果是 Android,它很可能是),磨损均衡将启动,它会继续写入驱动器的不同位。

      在磁盘缓存和文件系统方面,您确实需要尽您所能,以确保如果您在将缓存推送到磁盘之前发送 1000 次更新,则只会写入最后一次更新。

      由于这是 Android,您可能正在查看 ext2/3/4。仔细查看日志选项,并调查 ext4 中延迟分配的影响。也许最好的选择是使用 ext4,但关闭日志。

      【讨论】:

        【解决方案5】:

        通过 google 快速搜索,找到了针对不同大小文件的不同写入/读取操作的基准:

        http://designingefficientsoftware.wordpress.com/2011/03/03/efficient-file-io-from-csharp/

        作者得出的结论是WinFileIO.WriteBlocks 将数据写入文件的速度最快,尽管 I/O 操作严重依赖于多种因素,例如操作系统文件缓存、文件索引、磁盘碎片、文件系统缓存等。

        【讨论】:

        • 这适用于 Android 上的 Java,而不是 C#。除非我在这里遗漏了什么。
        • 抱歉,这是一个 Java 问题,而不是 C#。我现在已将其添加到标题中,因为很容易在标签中错过它。 :)
        • @MattC 不,我是那个错过了什么的人。好吧,毕竟,Java 和 C# 都是托管的、垃圾收集的高级编程语言,其框架(或 Java 的虚拟机)作为彼此非常相似的后端。尽管方法和程序不同,但许多事情都以相同的方式完成。也许 Java 有类似的方法来解决这种情况?
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-01-19
        • 1970-01-01
        • 1970-01-01
        • 2014-09-03
        • 2011-04-10
        • 1970-01-01
        • 2011-07-01
        相关资源
        最近更新 更多