Skip to content

Commit

Permalink
experimental use of memory mapping for small files
Browse files Browse the repository at this point in the history
  • Loading branch information
overheadhunter committed Feb 11, 2024
1 parent e1c138a commit 03cf1e4
Showing 1 changed file with 39 additions and 10 deletions.
49 changes: 39 additions & 10 deletions src/main/java/org/cryptomator/frontend/fuse/OpenFile.java
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
package org.cryptomator.frontend.fuse;

import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Closeable;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.FileAttribute;
import java.util.Set;

public class OpenFile implements Closeable {

private static final Logger LOG = LoggerFactory.getLogger(OpenFile.class);
private static final long MAX_MAPPED_MEMORY_SIZE = 50_000_000L; // 50 MB

private final Path path;
private final FileChannel channel;
Expand All @@ -25,16 +29,24 @@ public class OpenFile implements Closeable {
* This value only changes while holding a write lock, see {@link org.cryptomator.frontend.fuse.locks.LockManager}.
*/
private volatile boolean dirty;
private @Nullable MappedByteBuffer mappedContents;

private OpenFile(Path path, FileChannel channel) {
private OpenFile(Path path, FileChannel channel, @Nullable MappedByteBuffer mappedContents) {
this.path = path;
this.channel = channel;
this.dirty = false;
this.mappedContents = mappedContents;
}

static OpenFile create(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException {
FileChannel ch = FileChannel.open(path, options, attrs);
return new OpenFile(path, ch);
if (ch.size() < MAX_MAPPED_MEMORY_SIZE) {
var write = options.contains(StandardOpenOption.WRITE) || options.contains(StandardOpenOption.CREATE) || options.contains(StandardOpenOption.CREATE_NEW) || options.contains(StandardOpenOption.APPEND);
var buf = ch.map(write ? FileChannel.MapMode.READ_WRITE : FileChannel.MapMode.READ_ONLY, 0L, ch.size());
return new OpenFile(path, ch, buf);
} else {
return new OpenFile(path, ch, null);
}
}

/**
Expand All @@ -43,7 +55,7 @@ static OpenFile create(Path path, Set<? extends OpenOption> options, FileAttribu
* @param buf Buffer
* @param num Number of bytes to read
* @param offset Position of first byte to read
* @return Actual number of bytes read (can be less than {@code size} if reached EOF).
* @return Actual number of bytes read (can be less than {@code num} if reached EOF).
* @throws IOException If an exception occurs during read.
*/
public int read(ByteBuffer buf, long num, long offset) throws IOException {
Expand All @@ -52,6 +64,10 @@ public int read(ByteBuffer buf, long num, long offset) throws IOException {
return 0;
} else if (num > Integer.MAX_VALUE) {
throw new IOException("Requested too many bytes");
} else if (mappedContents != null && offset + num < mappedContents.capacity()) {
int toRead = (int) Math.min(num, buf.limit());
buf.put(0, mappedContents, (int) offset, toRead);
return toRead;
} else {
int read = 0;
int toRead = (int) Math.min(num, buf.limit());
Expand Down Expand Up @@ -80,13 +96,18 @@ public int write(ByteBuffer buf, long num, long offset) throws IOException {
dirty = true;
if (num > Integer.MAX_VALUE) {
throw new IOException("Requested too many bytes");
} else if (mappedContents != null && offset + num < mappedContents.capacity()) {
int toWrite = (int) Math.min(num, buf.limit());
mappedContents.put((int) offset, buf, 0, toWrite);
return toWrite;
} else {
int written = 0;
int toWrite = (int) Math.min(num, buf.limit());
while (written < toWrite) {
written += channel.write(buf, offset + written);
}
return written;
}
int written = 0;
int toWrite = (int) Math.min(num, buf.limit());
while (written < toWrite) {
written += channel.write(buf, offset + written);
}
return written;
}

/**
Expand All @@ -105,19 +126,27 @@ public void close() throws IOException {
}

public void fsync(boolean metaData) throws IOException {
if (mappedContents != null) {
mappedContents.force();
}
channel.force(metaData);
dirty = false;
}

public void truncate(long size) throws IOException {
if (mappedContents != null && size > mappedContents.capacity()) {
mappedContents.force();
mappedContents = channel.map(FileChannel.MapMode.READ_WRITE, 0L, size);
}
FileChannelUtil.truncateOrExpand(channel, size);
}

@Override
public String toString() {
return "OpenFile{"
+ "path=" + path + ", "
+ "channel=" + channel
+ "channel=" + channel + ", "
+ "mappedContents=" + mappedContents
+ "}";
}

Expand Down

0 comments on commit 03cf1e4

Please sign in to comment.