0

我正在处理我的网络服务器访问日志并将处理后的信息存储到我的数据库中。以前,我是单线程进程。完成这个过程花了很长时间。我决定使用并发文件读取来节省执行时间。我使用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。

并发执行:14ms。

我正在同时读取文件,但为什么耗时大于单线程执行?请纠正我如果我做错了什么?

4

2 回答 2

1

我可以看到几个问题:

  1. 您的测试文件似乎非常小,并且将完全缓存在 RAM 中,因此您的基准测试没有模拟真正的问题,因为它没有考虑 I/O 时间。此外,因为它是如此之小,所以您所做的任何改进都可能会被管理线程的开销所吸收。

  2. 除非您所做的数据处理工作非常复杂,否则它主要是 I/O 密集型任务,而不是 CPU 密集型任务,除非您的文件都在不同的磁盘上,否则不会在一个磁盘上同时请求不同的数据让它更快地工作。事实上,对于传统的硬盘而不是固态存储,像这样的“并发”I/O 可能会慢得多,因为驱动器头(尖头的东西)会疯狂地来回摆动以到达不同的磁盘的一部分。

  3. 在大多数系统上,精度System.currentTimeMillis()为 +/- 10 毫秒。因此,您 9 毫秒和 14 毫秒的基准测试结果 实际上并没有告诉您任何信息。用于System.nanoTime()更准确的计时器。

  4. 你运行了多少次代码?显然每次运行程序时只有一次。如果它少于数千次,那么您根本没有测量任何有用的东西,因为代码仍在编译。不要难过:每个人都会犯这个错误!在动态编译语言中进行基准测试非常困难。要了解如何编写有用的基准,请阅读:

    至少,您应该在循环中多次运行完整的代码,以便在几秒钟内完成,而不是几毫秒。

  5. 在频率切换 CPU(即现代 CPU)上,CPU 在空闲时以低频运行。只有当它开始工作时,它才会逐渐提高速度,这可能会混淆基准测试,除非您将其设置为固定速度(最大电池或最大性能)模式。或者,修复问题 4 将修复问题 5,因为使用长循环或添加预热代码会吸收 CPU 频率切换的影响。

简短的回答是您当前的基准测试太小而无法给出有意义的结果。一旦您运行了一个更准确地模拟实际问题的更长的基准测试,您将能够知道多线程是否使其更快。

于 2013-08-26T00:37:57.147 回答
-1

首先,您应该了解并行性适用于处理器执行的一系列指令。处理器执行一系列指令。虚拟处理器被称为threads。您可以分配多个线程来并行执行多条指令。但是,这并不意味着多处理允许您通过网络连接更快地下载电影,从而增加带宽。这意味着您的并行线程将暂停等待网络数据到达。好的?我们应该对那些不了解这个简单事情的人做点什么。

另外,我必须补充一点,10 毫秒是 java 中的时间测量误差。因此,您的读数是时间测量噪声。基准测试需要一些时间让系统升温并需要更长的时间间隔,以便计时器分辨率不会引入重大错误。

于 2013-08-25T18:52:39.620 回答