Skip to content

Commit

Permalink
Use String.indexOf to filter children during
Browse files Browse the repository at this point in the history
* ImfsDirectoryStream is cleaner
* changes are nominal for performance

examples of improvement:
* total=7, kids=1, elapsed=2 -> total=7, kids=1, elapsed=0
* total=33, kids=4, elapsed=32 -> total=33, kids=4, elapsed=25
  • Loading branch information
ZekeAranyLucas committed Nov 26, 2023
1 parent 6694451 commit 9a6c034
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 54 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ Copied from the PDF.
decisions would depend on prioritizing use case scenarios.
- [ ] **Root enumerations are expensive** because they scan the full TreeMap and do a filter.
Basic instrumentation of ImfsDirectoryStreeam shows that something will need to be done.
A tale of two scans: `total=15, kids=1, elapsed=39` vs `total=3, kids=1, elapsed=2`.
A tale of two scans: `total=33, kids=4, elapsed=25ms` vs `total=7, kids=1, elapsed=0ms`.
Either a cache or a new data structure to support enumerations.
- [ ] **Files are stored in contiguous byte arrays.** Humongous files (>16MB) could cause weird fragmentation issues,
especially since these allocations are likely to be long-lived. Could use a fancier allocation scheme like a chain of blocks.
Expand Down
15 changes: 0 additions & 15 deletions src/main/java/com/imfs/ImfsChildren.java

This file was deleted.

37 changes: 25 additions & 12 deletions src/main/java/com/imfs/ImfsDirectoryStream.java
Original file line number Diff line number Diff line change
@@ -1,36 +1,49 @@
package com.imfs;

import java.io.IOException;
import java.net.URI;
import java.nio.file.DirectoryStream;
import java.nio.file.Path;
import java.util.Iterator;
import java.util.stream.Stream;

public class ImfsDirectoryStream implements DirectoryStream<Path> {
private final ImfsPath parent;
private final ImfsChildren children;
private final Stream<Path> stream;
private ImfsFileSystem fileSystem;
private String parent;
private int version = 0;
private int inputSize;
private Stream<Path> stream;
private int total = 0;
private int kids = 0;
long startTime = System.nanoTime();
private final int offset;

public ImfsDirectoryStream(ImfsPath parent, ImfsChildren children, Filter<? super Path> filter) {
this.parent = parent;
this.children = children;
this.stream = children.getStream().filter(this::isChild)
public ImfsDirectoryStream(ImfsFileSystem fileSystem, String materializedPath,
Stream<String> input, int inputSize, Filter<? super Path> filter) {
this.fileSystem = fileSystem;
this.parent = materializedPath;
this.inputSize = inputSize;
this.offset = parent.length() + 1;

this.stream = input.filter(this::isChild)
.map(this::toPath)
.filter(each -> {
try {
return filter == null || filter.accept(each);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
});
}

private Path toPath(String entry) {
return new ImfsPath(fileSystem, URI.create("imfs://" + fileSystem.getKey() + "/" + entry));
}

private boolean isChild(Path each) {
private boolean isChild(String each) {
total++;
var result = each.getParent().equals(parent);
// paths with no extra slashes are children
var result = each.indexOf("/", offset) == -1;
kids += result ? 1 : 0;
return result;
}
Expand All @@ -49,9 +62,9 @@ public void close() throws IOException {
long elapsed = (endTime - startTime) / 1_000_000;

System.out.println("ImfsDirectoryStream"
+ ": path=" + parent.toUri().toString()
+ ", version=" + children.getVersion()
+ ", size=" + children.getSize()
+ ": path=" + parent
+ ", version=" + version
+ ", size=" + inputSize
+ ", total=" + total
+ ", kids=" + kids
+ ", elapsed=" + elapsed);
Expand Down
27 changes: 6 additions & 21 deletions src/main/java/com/imfs/ImfsFileSystem.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package com.imfs;

import java.io.IOException;
import java.net.URI;
import java.nio.file.FileStore;
import java.nio.file.FileSystem;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.WatchService;
import java.nio.file.DirectoryStream.Filter;
import java.nio.file.attribute.UserPrincipalLookupService;
import java.nio.file.spi.FileSystemProvider;
import java.util.Set;
Expand Down Expand Up @@ -116,37 +116,22 @@ public String getKey() {
return key;
}

public ImfsChildren streamAllPaths() {
public ImfsDirectoryStream streamAllPaths(Filter<? super Path> filter) {
System.out.println("--- streamAllPaths: records.size() = " + records.size());
return ImfsChildren.builder()
.size(records.size())
.version(0)
.stream(records.keySet().stream()
.map(entry -> {
return new ImfsPath(this, URI.create("imfs://" + key + "/" + entry));
}))
.build();

return new ImfsDirectoryStream(this, "", records.keySet().stream(), records.size(), filter);
}

public ImfsChildren streamChildren(String materializedPath) {
public ImfsDirectoryStream streamChildren(String materializedPath, Filter<? super Path> filter) {
if (materializedPath.length() == 0) {
return streamAllPaths();
return streamAllPaths(filter);
}

var from = materializedPath + "/";
var to = materializedPath + "0";
var sub = records.subMap(from, to);
System.out.println("--- streamChildren: sub.size() = " + sub.size());
return new ImfsDirectoryStream(this, materializedPath, sub.keySet().stream(), sub.size(), filter);

return ImfsChildren.builder()
.size(sub.size())
.version(0)
.stream(sub.keySet().stream()
.map(entry -> {
return new ImfsPath(this, URI.create("imfs://" + key + "/" + entry));
}))
.build();
}

public boolean contains(String materializedPath) {
Expand Down
6 changes: 1 addition & 5 deletions src/main/java/com/imfs/ImfsProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -170,18 +170,14 @@ var record = fileSystem.getRecord(imfsPath.getMaterializedPath());

return new ByteArraySeekableByteChannel(record.getBytes());
}
// TODO Auto-generated method stub
throw new UnsupportedOperationException("only READ and WRITE are implemented in 'newByteChannel'");
}

@Override
public DirectoryStream<Path> newDirectoryStream(Path path, Filter<? super Path> filter) throws IOException {
var imfsPath = checkPath(path);
var fileSystem = (ImfsFileSystem) imfsPath.getFileSystem();
var children = fileSystem.streamChildren(imfsPath.getMaterializedPath());

// instrument the stream to close the underlying directory stream
return new ImfsDirectoryStream(imfsPath, children, filter);
return fileSystem.streamChildren(imfsPath.getMaterializedPath(), filter);
}

@Override
Expand Down
64 changes: 64 additions & 0 deletions src/test/java/com/imfs/ImfsContextTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -378,4 +378,68 @@ public void testGrepTree() throws IOException {
assertEquals(" public void testGrepTree() throws IOException {", results.get(0));
assertEquals(3, results.size());
}

@Test
public void testWalkSpeed() throws IOException {
var context = new ImfsContext("imfs://ImfsContextTest/");

context.importFiles("src", "src");

var results = Files.walk(context.getPath())
.filter(Files::isRegularFile)
.collect(Collectors.toList());

assertEquals(15, results.size());
}

// Debug console from testWalkSpeed() while using Paths to do parenting
//
// ImfsDirectoryStream:
// path=imfs://ImfsContextTest/src/main/resources/META-INF/services, version=0,
// size=1, total=1, kids=1, elapsed=0
// ImfsDirectoryStream: path=imfs://ImfsContextTest/src/main/resources/META-INF,
// version=0, size=2, total=2, kids=1, elapsed=0
// ImfsDirectoryStream: path=imfs://ImfsContextTest/src/main/resources,
// version=0, size=3, total=3, kids=1, elapsed=0
// ImfsDirectoryStream: path=imfs://ImfsContextTest/src/main, version=0,
// size=20, total=20, kids=2, elapsed=5
// ...
// ImfsDirectoryStream: path=imfs://ImfsContextTest/src/test/java/com/imfs/app,
// version=0, size=1, total=1, kids=1, elapsed=0
// ImfsDirectoryStream: path=imfs://ImfsContextTest/src/test/java/com/imfs,
// version=0, size=4, total=4, kids=3, elapsed=0
// ImfsDirectoryStream: path=imfs://ImfsContextTest/src/test/java/com,
// version=0, size=5, total=5, kids=1, elapsed=1
// ImfsDirectoryStream: path=imfs://ImfsContextTest/src/test/java, version=0,
// size=6, total=6, kids=1, elapsed=1
// ImfsDirectoryStream: path=imfs://ImfsContextTest/src/test, version=0, size=7,
// total=7, kids=1, elapsed=2
// ImfsDirectoryStream: path=imfs://ImfsContextTest/src, version=0, size=29,
// total=29, kids=2, elapsed=9
// ImfsDirectoryStream: path=imfs://ImfsContextTest/, version=0, size=33,
// total=33, kids=4, elapsed=32
//
// Debug console from testWalkSpeed() while using indexOf to do parenting
// ImfsDirectoryStream: path=src/main/resources/META-INF/services, version=0,
// size=1, total=1, kids=1, elapsed=0
// ImfsDirectoryStream: path=src/main/resources/META-INF, version=0, size=2,
// total=2, kids=1, elapsed=0
// ImfsDirectoryStream: path=src/main/resources, version=0, size=3, total=3,
// kids=1, elapsed=0
// ImfsDirectoryStream: path=src/main, version=0, size=19, total=19, kids=2,
// elapsed=3
// ...
// ImfsDirectoryStream: path=src/test/java/com/imfs/app, version=0, size=1,
// total=1, kids=1, elapsed=0
// ImfsDirectoryStream: path=src/test/java/com/imfs, version=0, size=4, total=4,
// kids=3, elapsed=0
// ImfsDirectoryStream: path=src/test/java/com, version=0, size=5, total=5,
// kids=1, elapsed=0
// ImfsDirectoryStream: path=src/test/java, version=0, size=6, total=6, kids=1,
// elapsed=0
// ImfsDirectoryStream: path=src/test, version=0, size=7, total=7, kids=1,
// elapsed=0
// ImfsDirectoryStream: path=src, version=0, size=28, total=28, kids=2,
// elapsed=4
// ImfsDirectoryStream: path=, version=0, size=32, total=32, kids=4, elapsed=25
}

0 comments on commit 9a6c034

Please sign in to comment.