我正在处理我的Web服务器访问日志并将处理过的信息存储到我的数据库中。以前,我做过单线程进程。完成这个过程需要很长时间。我决定使用并发文件读取来节省执行时间。我使用Executors
线程池实现了这一点。这是我的java代码。
日志文件处理程序
class FileHandler implements Runnable {
private File file;
public FileHandler(File file) {
this.file = file;
}
@Override
public void run() {
try {
byte[] readInputStream = readInputStream(new FileInputStream(file));
} catch (IOException e) {
e.printStackTrace();
}
}
public static byte[] readInputStream(InputStream in) throws IOException {
//closing the bytearrayoutput stream has no effect. @see java doc.
ByteArrayOutputStream bos = null;
byte[] buffer = new byte[1024];
int bytesRead = -1;
bytesRead = in.read(buffer);
//no input to read.
if(bytesRead == -1) {
return null;
}
bos = new ByteArrayOutputStream(in.available()); //creating output stream with approximate capacity.
bos.write(buffer , 0 , bytesRead);
try {
while((bytesRead = in.read(buffer)) != -1) {
bos.write(buffer , 0 , bytesRead);
}
}finally {
if(in != null) {
in.close();
}
}
return bos.toByteArray();
}
}
并发文件阅读
public class AccessLogProcessor {
public static void main(String[] args) {
String[] files = {
"/home/local/ZOHOCORP/bharathi-1397/Downloads/unique-invoice-zuid1.txt" ,
"/home/local/ZOHOCORP/bharathi-1397/Downloads/unique-invoice-zuid.txt"
};
long start = System.currentTimeMillis();
ExecutorService executors = Executors.newFixedThreadPool(files.length);
for(String file : files) {
executors.execute(new FileHandler(new File(file)));
}
executors.shutdown();
while(!executors.isTerminated());
System.out.println("Time Taken by concurrent reading :: "+(System.currentTimeMillis()-start) + " ms ");
}
}
单线程文件阅读
public class Test {
public static void main(String[] args) throws FileNotFoundException, IOException {
String[] files = {
"/home/local/ZOHOCORP/bharathi-1397/Downloads/unique-invoice-zuid1.txt" ,
"/home/local/ZOHOCORP/bharathi-1397/Downloads/unique-invoice-zuid.txt"
};
long start = System.currentTimeMillis();
for(String file : files) {
FileHandler.readInputStream(new FileInputStream(file));
}
System.out.println("Time Taken by concurrent reading :: "+(System.currentTimeMillis()-start) + " ms ");
}
}
10轮执行的测试结果
单线程执行:9ms。
并发执行:14毫秒。
我正在同时读取文件,但为什么timetaken比单线程执行更大?请纠正我如果我做错了什么?
答案 0 :(得分:1)
我可以看到几个问题:
您的测试文件似乎非常小,并且将完全缓存在RAM中,因此您的基准测试不会模拟真正的问题,因为它不会考虑I / O时间。另外,因为它太小了,你所获得的任何改进都可能会被管理线程的开销所吸收。
除非你做的数据处理工作非常复杂,否则它主要是一个I / O绑定任务,而不是一个CPU绑定任务,除非你的文件都在不同的磁盘上,否则同时抢占一个磁盘对不同数据的请求不会让它更快地运行。实际上,对于传统的硬盘而不是固态存储,像这样的“并发”I / O可能要慢得多,因为驱动器头(the pointy arm thing)会疯狂地来回移动到达磁盘的不同部分。
在大多数系统上,System.currentTimeMillis()
的准确度为+/- 10毫秒。因此,您的9 ms和14 ms 的基准测试结果实际上并没有告诉您任何内容。使用System.nanoTime()
获得更准确的计时器。
您运行了多少次代码?显然每次运行程序只有一次。如果它少于几千次,那么你根本没有测量任何有用的东西,因为代码仍在编译中。不要心疼:每个人都犯了这个错误!动态编译语言的基准测试 该死的 。要了解如何编写有用的基准测试,请阅读:
至少,您应该多次循环运行完整代码,以便秒完成,而不是毫秒。
在频率切换CPU(即现代CPU)上,CPU在空闲时正在低速运行。只有当它开始工作时才会逐渐加快速度,除非你将它设置为固定速度(最大电池或最大性能)模式,否则这可能会混淆基准测试。或者,修复问题4将解决问题5,因为使用长循环或添加预热代码将吸收CPU频率切换的影响。
简短的回答是,您目前的基准太小,无法提供有意义的结果。一旦你运行了更长的基准测试,更准确地模拟了真正的问题,你将能够知道多线程是否使它更快。
答案 1 :(得分:-1)
首先,您应该了解并行性适用于处理器执行的一系列指令。处理器执行一系列指令。虚拟处理器称为threads
。您可以分配多个线程以并行执行多个指令。但是,这并不意味着多处理允许您通过网络连接更快地下载电影,从而使带宽倍增。这意味着您的并行线程将暂停,等待网络数据到达。好?我们应该对不理解这件简单事情的人做点什么。
此外,我必须补充一点,10毫秒是java中的时间测量错误。所以,你的读数是时间测量噪音。基准测试需要一段时间才能使系统升温并需要更长的时间间隔,以便定时器分辨率不会引起重大错误。