drR*_*lol 5 java concurrency performance file-io multithreading
我正在开发一个(database-ish)项目,其中数据存储在一个平面文件中.对于读/写,我正在使用该RandomAccessFile课程.我是否可以从多线程获得任何东西,并为每个线程提供一个实例RandomAccessFile,或者一个线程/实例是否同样快?读/写是否有任何区别,因为你可以创建只进行读取的实例,而不能写?
RED*_*AIR 13
我现在用下面的代码做了一个基准测试(对不起,它在cpp中).该代码读取一个5 MB的文本文件,其中包含许多作为命令行参数传递的线程.
结果清楚地表明多线程总是加速程序:
更新:我想到,文件缓存将在这里发挥重要作用.所以我制作了testdata文件的副本,重新启动并为每次运行使用了不同的文件.下面更新了结果(括号中的旧结果).结论保持不变.
运行时间为秒
机器A(运行XP x64的双四核XEON,在RAID 5中具有4个10k SAS驱动器)
机器B(运行XP的双核笔记本电脑,带有一个支离破碎的2.5英寸驱动器)
源代码(Windows):
// FileReadThreads.cpp : Defines the entry point for the console application.
//
#include "Windows.h"
#include "stdio.h"
#include "conio.h"
#include <sys\timeb.h>
#include <io.h>
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
int threadCount = 1;
char *fileName = 0;
int fileSize = 0;
double GetSecs(void);
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
DWORD WINAPI FileReadThreadEntry(LPVOID lpThreadParameter)
{ char tx[255];
int index = (int)lpThreadParameter;
FILE *file = fopen(fileName, "rt");
int start = (fileSize / threadCount) * index;
int end = (fileSize / threadCount) * (index + 1);
fseek(file, start, SEEK_SET);
printf("THREAD %4d started: Bytes %d-%d\n", GetCurrentThreadId(), start, end);
for(int i = 0;; i++)
{
if(! fgets(tx, sizeof(tx), file))
break;
if(ftell(file) >= end)
break;
}
fclose(file);
printf("THREAD %4d done\n", GetCurrentThreadId());
return 0;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[])
{
if(argc <= 1)
{
printf("Usage: <InputFile> <threadCount>\n");
exit(-1);
}
if(argc > 2)
threadCount = atoi(argv[2]);
fileName = argv[1];
FILE *file = fopen(fileName, "rt");
if(! file)
{
printf("Unable to open %s\n", argv[1]);
exit(-1);
}
fseek(file, 0, SEEK_END);
fileSize = ftell(file);
fclose(file);
printf("Starting to read file %s with %d threads\n", fileName, threadCount);
///////////////////////////////////////////////////////////////////////////
// Start threads
///////////////////////////////////////////////////////////////////////////
double start = GetSecs();
HANDLE mWorkThread[255];
for(int i = 0; i < threadCount; i++)
{
mWorkThread[i] = CreateThread(
NULL,
0,
FileReadThreadEntry,
(LPVOID) i,
0,
NULL);
}
WaitForMultipleObjects(threadCount, mWorkThread, TRUE, INFINITE);
printf("Runtime %.2f Secs\nDone\n", (GetSecs() - start) / 1000.);
return 0;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
double GetSecs(void)
{
struct timeb timebuffer;
ftime(&timebuffer);
return (double)timebuffer.millitm +
((double)timebuffer.time * 1000.) - // Timezone needed for DbfGetToday
((double)timebuffer.timezone * 60. * 1000.);
}
Run Code Online (Sandbox Code Playgroud)
根据我从C++开发的经验,答案是:是的,使用多个线程可以在读取文件时提高性能.这适用于顺序和串行访问.我不止一次地证明了这一点,尽管我总是发现真正的瓶颈在其他地方.
原因是,对于磁盘访问,线程将被挂起,直到磁盘操作完成.但是现在大多数磁盘都支持本机命令队列(SAS)或Segate(SATA)(以及大多数RAID系统),因此不必按照您的顺序处理请求.
因此,如果您按顺序读取4个文件块,则您的程序必须等待第一个块,然后您请求第二个块,然后请求第一个块.如果您请求包含4个线程的4个块,则可以一次性返回它们.这种优化有限,但它有效(虽然我在这里只有C++的经验).我测量了多个线程可以将顺序读取性能提高100%以上.
一个相当常见的问题。基本上使用多线程不会让你的硬盘运行得更快。相反,执行并发请求可能会使速度变慢。
磁盘子系统(尤其是 IDE、EIDE、SATA)设计为按顺序读取/写入速度最快。