为什么ObjectInputStream.readUnshared()比ObjectOutputStream.writeUnshared()慢1000倍?

时间:2020-05-11 01:12:06

标签: java deserialization

我正在研究将大量对象排队的一对类,然后使用Java Serialization / ObjectOutputStream和ObjectInputStream允许对该队列进行迭代。队列中需要的数据量超过50GB,因此需要文件IO。问题在于,写入需要2毫秒的文件需要大约2秒钟才能读取。我已经尝试过BufferedInputStream,并且每个阅读器都有一个专用线程来保持输入缓冲区已满。

private File file;
private FileInputStream fileStream;
private BufferedInputStream buffer;
private ObjectInputStream stream;

......

this.file = file;
this.fileStream = new FileInputStream(this.file);
this.buffer = new BufferedInputStream(this.fileStream, 2^20); //Tried multiple depths, does not change behavior
this.stream = new ObjectInputStream(this.buffer);

.....

long startTime = System.currentTimeMillis();
LinkedList<T> list = (LinkedList<T>) stream.readUnshared();
long endTime = System.currentTimeMillis();
totalReadTime += endTime - startTime;

填充序列化文件的类正在插入具有10000个元素的LinkedLists。使用Integers进行测试时,stream.readUnshared()调用的平均时间为214.7ms。为了进行比较,任何给定测试的写入时间最多为3毫秒,其中测试通常向文件中写入10倍的10,000个元素的LinkedList。

我已经包括了所有源代码和性能测试程序。这是我的示例输出:

Hello
This
Is
A
            Total time spent reading: 0
Final
Test
            Average time spent reading: 0.0
Starting test: 0
    Threads Created.
    Total write time: 3
            Total time spent reading: 2256
            Average time spent reading: 225.6
    Total read time: 2030
    Threads Joined. Total time: 2265
Starting test: 1
    Threads Created.
    Total write time: 2
            Total time spent reading: 2147
            Average time spent reading: 214.7
    Total read time: 1961
    Threads Joined. Total time: 2153
Starting test: 2
    Threads Created.
    Total write time: 1
            Total time spent reading: 2143
            Average time spent reading: 214.3
    Total read time: 1957
    Threads Joined. Total time: 2149

我不是专业人士,可以接受建设性的反馈。谢谢您的关注。

    //Serial Object Writer Class
    package utils;

    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.ObjectOutputStream;
    import java.io.Serializable;
    import java.util.LinkedList;
    import java.util.concurrent.ConcurrentLinkedQueue;
    import java.util.concurrent.atomic.AtomicBoolean;
    import java.util.concurrent.atomic.AtomicInteger;
    import java.util.concurrent.atomic.AtomicLong;


    public class SerialObjFileCollector<T extends Serializable> extends Thread {

        private ObjectOutputStream stream;
        private FileOutputStream fileStream;
        private File tempFile;
        private AtomicBoolean isOpen;
        private AtomicInteger flushCount;
        private AtomicLong count;
        private LinkedList<T> objects;
        private int sendThreshold = 10000;
        private ConcurrentLinkedQueue<LinkedList<T>> sendQueue;

        public SerialObjFileCollector() {
            this.initialize();
        }

        private void initialize() {
            try {
                tempFile = File.createTempFile("binary_data", ".SerialObjFileCollector");
                tempFile.deleteOnExit();
                fileStream = new FileOutputStream(tempFile);
                stream = new ObjectOutputStream(fileStream);
                isOpen = new AtomicBoolean(true);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                isOpen = new AtomicBoolean(false);
            }
            objects = new LinkedList<T>();
            sendQueue = new ConcurrentLinkedQueue<LinkedList<T>>();
            flushCount = new AtomicInteger(0);
            count = new AtomicLong(0);
            this.start();
        }

        public void writeObject(T object) {
            if(!isOpen.compareAndExchange(false, false)) throw new UnsupportedOperationException("Cannot write objects to a closed SerialObjFileCollector!");
            objects.add(object);
            if(objects.size() > sendThreshold) {
                this.sendQueue.add(objects);
                synchronized (sendQueue) {sendQueue.notifyAll();}
                objects = new LinkedList<T>();
            }
        }

        public void writeObjects(Iterable<T> objects) {
            for(T object : objects) {
                this.writeObject(object);
            }
        }

        public long size() {
            return count.get();
        }

        public SerialObjFileReader<T> getReader() {
            if(isOpen.compareAndExchange(true, true)) {
                flush(true);
                try {
                    fileStream.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }

            return new SerialObjFileReader<T>(tempFile,count.get(),sendThreshold);
        }

        public void flush() {
            this.flush(false);
        }

        private void flush(boolean close) {
            this.sendQueue.add(objects);
            flushCount.incrementAndGet();
            isOpen.set(!close);
            objects = new LinkedList<T>();
            try {
                synchronized (sendQueue) {sendQueue.notifyAll();}
                synchronized (flushCount) {flushCount.wait();}
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        public void run(){

            //Keep the thread open even when closing and flushing
            while(isOpen.compareAndExchange(true, true) || flushCount.get() > 0) {

                if(sendQueue.isEmpty() && flushCount.get() == 0)    try {
                    synchronized (sendQueue) {sendQueue.wait();}
                    } catch (InterruptedException e1) {
                        // TODO Auto-generated catch block
                        e1.printStackTrace();
                    }

                //Write out the Queue
                while(!this.sendQueue.isEmpty()) {
                    LinkedList<T> toSend = sendQueue.poll();
                    try {
                        stream.writeUnshared(toSend);
                        stream.reset();
                        count.addAndGet(toSend.size());
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    };
                }

                //Flush the streams
                if(flushCount.get() > 0) {
                    try {
                        stream.flush();
                        fileStream.flush();
                        flushCount.decrementAndGet();
                        synchronized (flushCount) {flushCount.notifyAll();}
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }   
                }
            }

            synchronized (flushCount) {flushCount.notifyAll();}
        }
    }

//Serial Object Reader Class
package utils;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicLong;

public class SerialObjFileReader<T extends Serializable> implements Iterable<T> {

    private File file;
    private long count;
    private int maxSize;


    public SerialObjFileReader(File file, long count, int maxSize) {
        this.initialize(file,count,maxSize);
    }

    private void initialize(File file, long count, int maxSize) {
        this.file = file;
        this.count = count;
        this.maxSize = maxSize;
    }

    @Override
    public Iterator<T> iterator() {
        return new SerialObjFileIterator<T>(file,count,maxSize);
    }

    public long size() {
        return count;
    }

    public static class SerialObjFileIterator<T> extends Thread implements Iterator<T> {

        private File file;
        private FileInputStream fileStream;
        private BufferedInputStream buffer;
        private ObjectInputStream stream;
        private long count;
        private int maxSize;
        private AtomicLong dequeCount; 
        private Iterator<T> localIterator;
        private ConcurrentLinkedQueue<Iterator<T>> rcvQueue;
        private long totalReadTime = 0;
        private int readCount = 0;


        public SerialObjFileIterator(File file, long count, int maxSize) {
            this.initialize(file,count,maxSize);
        }

        private void initialize(File file, long count, int maxSize) {
            this.count = 0;
            try {
                this.file = file;
                this.fileStream = new FileInputStream(this.file);
                this.buffer = new BufferedInputStream(this.fileStream, 2^20);
                this.stream = new ObjectInputStream(this.buffer);
                this.count = count;
                this.maxSize = maxSize;
                this.dequeCount = new AtomicLong(count);
                this.rcvQueue = new ConcurrentLinkedQueue<Iterator<T>>();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }


            if(this.count > 0) localIterator = readNewIterator();
            this.start();
        }

        @Override
        public boolean hasNext() {
            return remaining() > 0 || (localIterator == null ? false : localIterator.hasNext());        //Probably some redundancy here
        }

        @Override
        public T next() {
            T result = localIterator.next();
            count--;
            if(!localIterator.hasNext() && count > 0) localIterator = pullNewIterator();
        return result;
    }

    private Iterator<T> pullNewIterator() {
        synchronized (rcvQueue) {rcvQueue.notifyAll();} //Wake the thread regardless
        if(!rcvQueue.isEmpty()) {
            return rcvQueue.poll();
        } else if (count > 0){
            try {
                synchronized (dequeCount) {dequeCount.wait();}
                return rcvQueue.poll();
            } catch (InterruptedException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
        }
        return null;
    }

    private Iterator<T> readNewIterator() {
        if(dequeCount.get() > 0) try {
            long startTime = System.currentTimeMillis();
            LinkedList<T> list = (LinkedList<T>) stream.readUnshared();
            long endTime = System.currentTimeMillis();
            totalReadTime += endTime - startTime;
            readCount++;
            dequeCount.set( Math.max(dequeCount.get() - maxSize, 0));
            return list.iterator();
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            System.exit(1);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            System.exit(1);
        }
        return null;
    }

    public long remaining() {
        return count;
    }

    public void run() {
        while(dequeCount.get() > 0) {
            while(rcvQueue.size() < 3 && dequeCount.get() > 0) {
                rcvQueue.add(readNewIterator());
            }
            synchronized (dequeCount) {dequeCount.notifyAll();} //Wake the thread regardless
            if(dequeCount.get() > 0) try {
                synchronized (rcvQueue) {rcvQueue.wait();}
            } catch (InterruptedException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
        }
        System.out.println("\t\t\tTotal time spent reading: " + totalReadTime);
        System.out.println("\t\t\tAverage time spent reading: " + ((double) totalReadTime / ((double) readCount)));
    }
    }

}
    //Serial Object Performance Test Class
    package utils;

    import java.util.Iterator;
    import java.util.LinkedList;
    import java.util.concurrent.atomic.AtomicReference;

    public class SerialObjTester {

        public static void main(String[] args) {
            SerialObjFileCollector<String> collector = new SerialObjFileCollector<String>();
            collector.writeObject("Hello");
            collector.writeObject("This");
            collector.writeObject("Is");
            collector.writeObject("A");
            collector.writeObject("Final");
            collector.writeObject("Test");

        for(String str : collector.getReader()) {
            System.out.println(str);
        }


        //Throughput Tests

        final int LIST_SIZE = 100000;
        final int WRITE_COUNT = 100000;

        SerialObjFileCollector<LinkedList<Integer>> intCollector = new SerialObjFileCollector<LinkedList<Integer>>();
        LinkedList<Integer> intList = new LinkedList<Integer>();
        for(int count = 0; count < LIST_SIZE; count++) intList.add(count);

        //Populate initial collector
        for(int count = 0; count < WRITE_COUNT; count++) intCollector.writeObject(intList);
        AtomicReference<SerialObjFileCollector<LinkedList<Integer>>> intCollectorRef = new AtomicReference<SerialObjFileCollector<LinkedList<Integer>>>(intCollector);


        for(int testCount = 0; testCount < 100; testCount++) {
            System.out.println("Starting test: " +  testCount);
            SerialObjFileReader<LinkedList<Integer>> intReader = intCollectorRef.get().getReader();

            Thread readerThread = new Thread() {
                public void run() {
                    Iterator<LinkedList<Integer>> intListIter = intReader.iterator();
                    LinkedList<Integer> list = null;
                    final long startTime = System.currentTimeMillis();
                    while(intListIter.hasNext()) {
                        list = intListIter.next();
                    }
                    final long endTime = System.currentTimeMillis();
                    int testInt = 0;
                    for(int compareInt : list) if(compareInt != testInt++) System.err.println("\t\tInteger List Inconsistency found!");
                    System.out.println("\tTotal read time: " + (endTime - startTime) );
                }
            };

            Thread writerThread = new Thread() {
                public SerialObjFileCollector<LinkedList<Integer>> localIntCollector;
                public void run() {
                    localIntCollector = new SerialObjFileCollector<LinkedList<Integer>>();
                    final long startTime = System.currentTimeMillis();
                    for(int count = 0; count < WRITE_COUNT; count++) localIntCollector.writeObject(intList);
                    final long endTime = System.currentTimeMillis();
                    intCollectorRef.set(localIntCollector);
                    System.out.println("\tTotal write time: " + (endTime - startTime) );
                }
            };

            System.out.println("\tThreads Created.");
            final long startTime = System.currentTimeMillis();
            readerThread.start();
            writerThread.start();

            try {
                readerThread.join();
                writerThread.join();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            final long endTime = System.currentTimeMillis();
            System.out.println("\tThreads Joined. Total time: " + (endTime - startTime) );
        }
    }

    }




0 个答案:

没有答案