未缓冲的输出非常慢
未缓冲的输出非常慢
我按照https://stackoverflow.com/a/13456219/141172中的方法从数据库生成了一个非常大的.csv文件。在某个点之前,它运行得很好。当导出的文件太大时,我会遇到一个OutOfMemoryException
的错误。
如果我通过修改代码关闭输出缓冲,如下所示:
protected override void WriteFile(System.Web.HttpResponseBase response) { response.BufferOutput = false; // <--- 添加这一行 this.Content(response.OutputStream); }
文件下载会完成。然而,与启用输出缓冲时相比,它慢了几个数量级(在本地主机上对相同的文件进行了缓冲设置为true/false的测量)。
我知道这样会更慢,但为什么会变得如此之慢?有什么方法可以提高处理速度吗?
更新
根据评论中的建议,使用File(Stream stream, String contentType)也是一种选择。然而,我不知道如何创建stream
。数据是根据数据库查询动态组装的,而内存流(MemoryStream)会耗尽连续的物理内存。欢迎提供建议。
更新2
评论中提到,交替从数据库中读取数据并写入流会导致性能下降。我修改了代码,将流写入操作放在一个单独的线程中(使用生产者/消费者模式)。但是,性能上没有明显的差异。
在Eric的最新更新中,他提到了使用另一个线程。我在实现数据库导出时也遇到了这个问题。以下是我使用的解决方案的一些示例代码:处理临时文件流。
在处理大量数据时,输出缓冲区可能会导致输出变得非常缓慢。这可能是由于输出被暂时存储在缓冲区中,而不是立即发送到输出设备。
为了解决这个问题,可以使用Unbuffered Output方法。这种方法可以立即将输出发送到输出设备,而不需要先将其存储在缓冲区中。
以下是一个示例代码,展示了如何使用Unbuffered Output方法来解决输出缓慢的问题:
// 开启Unbuffered Output ob_implicit_flush(true); // 输出内容 echo "Output content\n"; // 立即将输出发送到输出设备 flush();
通过将
ob_implicit_flush(true);
设置为true,可以开启Unbuffered Output。然后,使用
echo
来输出内容,并使用
flush()
立即将输出发送到输出设备。
使用Unbuffered Output方法可以解决输出缓慢的问题,确保输出立即发送到输出设备,从而提高输出速度。
问题原因:ASP.NET和IIS在输出流处理方面可能使用的是过小的块。可以通过添加一个具有非常大缓冲区(如4MB)的BufferedStream进行优化。
解决方法:根据评论,这种方法有效。现在,可以调整缓冲区大小以节省内存和减少工作集大小,这对缓存非常有利。以下是从回答中提取的代码:
public ActionResult Export() { // Domain specific stuff here return new FileGeneratingResult("MyFile.txt", "text/text", stream => this.StreamExport(stream), false); } private void StreamExport(Stream stream) { using (BufferedStream bs = new BufferedStream(stream, 256*1024)) using (StreamWriter sw = new StreamWriter(bs)) { foreach (var stuff in MyData()) { sw.Write(stuff); } } }
根据测试,性能提升约为100倍。我也对需要使用外部BufferedStream感到惊讶。256KB的缓冲区速度主观上与4MB的缓冲区相当。由于这是一个很少使用的功能,我不会进一步调整。如果处理大量此类请求的人,建议测试较小的缓冲区以找到良好性能和较小内存占用的最佳缓冲区大小。
补充信息:有人询问如何使用BufferedStream,以下是简化版的代码示例。我还通过在写入器上添加缓冲区进一步提高了性能。生成了一个非常大的csv文件(约11GB)。我假设在读取器上也添加缓冲区后,性能将进一步提高。