Как я неправильно использую MappedByteBuffer Java - PullRequest
1 голос
/ 26 апреля 2020

Я пытаюсь написать виртуальный класс файлов в Java. Я думаю, что фрагменты моего кода ниже достаточно просты, чтобы увидеть проблему. По сути, я должен заставить свой буфер перематывать () в странных местах, чтобы мой пример работал (помечен как вызовы принуждения к огромному (ниже)). Я неправильно понимаю использование запросов на базовый канал и позиционирование в буфере. Может кто-нибудь сказать мне, что я делаю не так. Я уверен, что это простое недоразумение. Я надеюсь, что мне придется избежать вызова empty_Hack (), потому что мой класс представляет собой структуру данных с произвольным доступом, и вы не можете сказать, в каком порядке вы будете читать и писать, а мой тест очень предсказуем и нереалистичен c в том, как он будет использоваться. .

Вот выдержки из моего класса:

public class FileStorageFloat64<U extends DoubleCoder & Allocatable<U>>
    implements IndexedDataSource<U>, Allocatable<FileStorageFloat64<U>>
{
    private final long numElements;
    private final U type;
    private final double[] tmpArray;
    private final long byteCount;
    private final File file;
    private final RandomAccessFile raf;
    private final FileChannel channel;
    private final MappedByteBuffer buffer;

    public FileStorageFloat64(long numElements, U type) {
        this.numElements = numElements;
        this.type = type.allocate();
        this.tmpArray = new double[type.doubleCount()];
        this.byteCount = numElements * type.doubleCount() * 8;
        try {
            this.file = File.createTempFile("Storage", ".storage");
            this.file.deleteOnExit();
            this.raf = new RandomAccessFile(file, "rw");
            this.channel = raf.getChannel();
            this.buffer = this.channel.map(MapMode.READ_WRITE, 0, this.byteCount);
            for (long i = 0; i < this.byteCount; i++) {
                this.buffer.put((byte)0);
            }
            this.buffer.rewind();
        } catch (IOException e) {
            throw new IllegalArgumentException(e.getMessage());
        }
    }

    @Override
    public void set(long index, U value) {
        if (index < 0 || index >= numElements)
            throw new IllegalArgumentException("storage index out of bounds");
        synchronized(this) {
            try {
                value.toDoubleArray(this.tmpArray, 0);
                long pos = index * this.type.doubleCount() * 8;
                this.channel.position(pos);
                for (int i = 0; i < this.tmpArray.length; i++) {
                    this.buffer.putDouble(this.tmpArray[i]);
                }
            } catch (IOException e) {
                throw new IllegalArgumentException(e.getMessage());
            }
        }
    }

    @Override
    public void get(long index, U value) {
        if (index < 0 || index >= this.numElements)
            throw new IllegalArgumentException("storage index out of bounds");
        synchronized(this) {
            try {
                long pos = index * this.type.doubleCount() * 8;
                this.channel.position(pos);
                for (int i = 0; i < this.tmpArray.length; i++) {
                    this.tmpArray[i] = this.buffer.getDouble();
                }
                value.fromDoubleArray(this.tmpArray, 0);
            } catch (IOException e) {
                throw new IllegalArgumentException(e.getMessage());
            }
        }
    }

    public void hugeHack() {
        this.buffer.rewind();
    }

А вот метод испытания:

    public void run() {

        final int SIZE = 40;

        ComplexFloat64Member v = new ComplexFloat64Member();

        FileStorageFloat64<ComplexFloat64Member> store = new FileStorageFloat64<ComplexFloat64Member>(SIZE, new ComplexFloat64Member());

        assertEquals(SIZE, store.size());

        for (long i = 0; i < store.size(); i++) {
            v.setR(i);
            v.setI(i+1);
            store.set(i, v);
        }

        store.hugeHack();

        for (long i = 0; i < store.size(); i++) {
            store.get(i, v);
            assertEquals(i, v.r(), 0);
            assertEquals(i+1, v.i(), 0);
        }

        store.hugeHack();

        FileStorageFloat64<ComplexFloat64Member> dup = store.duplicate(); // not shown above

        dup.hugeHack();

        assertEquals(store.size(), dup.size());

        for (long i = 0; i < dup.size(); i++) {
            dup.get(i, v);
            assertEquals(i, v.r(), 0);
            assertEquals(i+1, v.i(), 0);
        }

    }

1 Ответ

0 голосов
/ 27 апреля 2020

Ладно, вчера я все облажался и нашел решение, которое использует ByteBuffer; только не MappedByteBuffer. Это в 10-100 раз быстрее, чем мой старый подход RandomAccessFile. Я могу назвать это достаточно хорошим на данный момент.

public class FileStorageFloat64<U extends DoubleCoder & Allocatable<U>>
    implements IndexedDataSource<U>, Allocatable<FileStorageFloat64<U>>
{
    private final long numElements;
    private final U type;
    private final double[] tmpArray;
    private final int bufSize;
    private final File file;
    private final RandomAccessFile raf;
    private final FileChannel channel;
    private final ByteBuffer buffer;

    /**
     * 
     * @param numElements
     * @param type
     */
    public FileStorageFloat64(long numElements, U type) {
        this.numElements = numElements;
        this.type = type.allocate();
        this.tmpArray = new double[type.doubleCount()];
        this.bufSize = type.doubleCount() * 8;
        try {
            this.file = File.createTempFile("Storage", ".storage");
            this.file.deleteOnExit();
            this.raf = new RandomAccessFile(file, "rw");
            this.channel = raf.getChannel();
            // make a one element buffer
            this.buffer = ByteBuffer.allocate(bufSize);
            // fill the buffer with zeroes
            for (int i = 0; i < type.doubleCount(); i++) {
                buffer.putDouble(0);
            }
            // write zeroes to the file over and over
            channel.position(0);
            for (long i = 0; i < numElements; i++) {
                channel.write(buffer);
            }
        } catch (IOException e) {
            throw new IllegalArgumentException(e.getMessage());
        }
    }

    @Override
    public void set(long index, U value) {
        if (index < 0 || index >= this.numElements)
            throw new IllegalArgumentException("storage index out of bounds");
        synchronized(this) {
            try {
                value.toDoubleArray(this.tmpArray, 0);
                for (int i = 0; i < this.tmpArray.length; i++) {
                    this.buffer.putDouble(i*8, this.tmpArray[i]);
                }
                this.buffer.rewind();
                long pos = index * this.bufSize;
                this.channel.position(pos);
                this.channel.write(this.buffer);
            } catch (IOException e) {
                throw new IllegalArgumentException(e.getMessage());
            }
        }
    }

    @Override
    public void get(long index, U value) {
        if (index < 0 || index >= this.numElements)
            throw new IllegalArgumentException("storage index out of bounds");
        synchronized(this) {
            try {
                buffer.rewind();
                long pos = index * bufSize;
                this.channel.position(pos);
                this.channel.read(this.buffer);
                for (int i = 0; i < this.tmpArray.length; i++) {
                    this.tmpArray[i] = this.buffer.getDouble(i*8);
                }
                value.fromDoubleArray(this.tmpArray, 0);
            } catch (IOException e) {
                throw new IllegalArgumentException(e.getMessage());
            }
        }
    }

...