Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
4a8f067
WIP: Improve segment index performance
fm3 Mar 20, 2025
45adb6a
implementation with triple cast on every element, not faster than scala
fm3 Mar 20, 2025
398e757
measure what else is slow
fm3 Mar 20, 2025
3b8fd9f
wip cleanup, measure other stuff
fm3 Mar 24, 2025
ecb2c9a
Merge branch 'master' into segment-index-perf
fm3 Mar 24, 2025
be8ba6f
wip: use volume bucket buffer, fossil multi-put
fm3 Mar 24, 2025
0f29acb
logging
fm3 Mar 24, 2025
79312c8
WIP rewrite segmentIndexBuffer to include caching, use Set
fm3 Mar 25, 2025
f515bfc
use new segment index buffer functionality
fm3 Mar 25, 2025
ad7eeff
WIP fossil multi-get for buckets
fm3 Mar 25, 2025
7693b78
fix setting volumeBucketDataHasChanged
fm3 Mar 25, 2025
a3a36ce
do not parallelize prefill
fm3 Mar 25, 2025
0d5c9df
Merge branch 'master' into segment-index-perf
fm3 Mar 26, 2025
8264691
use batched multi-get and multi-put
fm3 Mar 26, 2025
9353c6b
connect to temporaryStore, compress if needed
fm3 Mar 26, 2025
9dc7d31
load buckets from temporarystore, remote fallback layer
fm3 Mar 26, 2025
2cea8b5
load buckets from fallback layer in one request
fm3 Mar 27, 2025
f6290c1
Merge branch 'master' into segment-index-perf
fm3 Mar 27, 2025
b9c75ce
Merge branch 'segment-index-perf' into segment-index-perf-fallback-ba…
fm3 Mar 27, 2025
ab0f2d7
some cleanup
fm3 Mar 27, 2025
9f1872f
readerOnly segmentIndexBuffer
fm3 Mar 27, 2025
10c2c92
cleanup
fm3 Mar 27, 2025
2dd76bc
cleanup
fm3 Mar 27, 2025
9bdb487
format, remove logging
fm3 Mar 27, 2025
d400771
debug wrong volume values for editable mapping (id seems to be mapped…
fm3 Mar 27, 2025
6247a9a
Merge branch 'master' into segment-index-perf
fm3 Mar 31, 2025
27129b9
request correct version of editable mapping data
fm3 Mar 31, 2025
f0ff544
changelog, migration
fm3 Mar 31, 2025
eee5bbc
cleanup
fm3 Mar 31, 2025
bf99be3
Merge branch 'master' into segment-index-perf
fm3 Mar 31, 2025
4cf95d7
make cpp types more explicit
fm3 Mar 31, 2025
5275c20
Merge branch 'master' into segment-index-perf
fm3 Apr 1, 2025
5352edf
clean up cpp code
fm3 Apr 1, 2025
993b9cc
const, try/catch
fm3 Apr 1, 2025
d80da1b
clang-format
fm3 Apr 1, 2025
b6ac259
size_t for index
fm3 Apr 1, 2025
c659595
Use native bucket scanner also in EditableMappingService
fm3 Apr 1, 2025
3b7b4af
Do not cache EditableMappingBucketProvider across versions
fm3 Apr 2, 2025
21bfbec
pr feedback part 1; consistent volumeLayer naming
fm3 Apr 2, 2025
5292a33
use fossil multi-get when requesting multiple segmentIds from segment…
fm3 Apr 2, 2025
2b71386
use map for more efficient lookups during gathering segmentIndex values
fm3 Apr 2, 2025
8bc345e
add one more conversion from set to seq for less map overhead
fm3 Apr 2, 2025
ab98fc4
Update webknossos-tracingstore/app/com/scalableminds/webknossos/traci…
fm3 Apr 2, 2025
a2bd486
Update webknossos-tracingstore/app/com/scalableminds/webknossos/traci…
fm3 Apr 2, 2025
a1ceb36
Merge branch 'master' into segment-index-perf
fm3 Apr 3, 2025
56b8bdd
Merge branch 'segment-index-perf' into bucket-scanner-proofreading
fm3 Apr 3, 2025
2da2dc0
Merge branch 'master' into bucket-scanner-proofreading
fm3 Apr 3, 2025
ddfa55d
Merge branch 'master' into bucket-scanner-proofreading
fm3 Apr 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,8 @@ import com.github.sbt.jni.nativeLoader

@nativeLoader("webknossosJni0")
class NativeBucketScanner() {
@native def collectSegmentIds(bucketBytes: Array[Byte], bytesPerElement: Int, isSigned: Boolean): Array[Long]
@native def collectSegmentIds(bucketBytes: Array[Byte],
bytesPerElement: Int,
isSigned: Boolean,
skipZeroes: Boolean): Array[Long]
}
4 changes: 2 additions & 2 deletions webknossos-jni/src/bucketScanner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ size_t getElementCount(jsize inputLengthBytes, jint bytesPerElement) {
}

JNIEXPORT jlongArray JNICALL Java_com_scalableminds_webknossos_datastore_helpers_NativeBucketScanner_collectSegmentIds(
JNIEnv *env, jobject instance, jbyteArray bucketBytesJavaArray, jint bytesPerElement, jboolean isSigned) {
JNIEnv *env, jobject instance, jbyteArray bucketBytesJavaArray, jint bytesPerElement, jboolean isSigned, jboolean skipZeroes) {

const jsize inputLengthBytes = env->GetArrayLength(bucketBytesJavaArray);
jbyte *bucketBytes = env->GetByteArrayElements(bucketBytesJavaArray, nullptr);
Expand All @@ -69,7 +69,7 @@ JNIEXPORT jlongArray JNICALL Java_com_scalableminds_webknossos_datastore_helpers

for (size_t i = 0; i < elementCount; ++i) {
const int64_t currentValue = segmentIdAtIndex(bucketBytes, i, bytesPerElement, isSigned);
if (currentValue != 0) {
if (!skipZeroes || currentValue != 0) {
uniqueSegmentIds.insert(currentValue);
}
}
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,13 @@ class EditableMappingBucketProvider(layer: EditableMappingLayer)
additionalCoordinates = readInstruction.bucket.additionalCoordinates
)
unmappedData <- editableMappingService.getFallbackBucketFromDataStore(remoteFallbackLayer, dataRequest)(ec, tc)
unmappedDataTyped <- editableMappingService.bytesToSegmentInt(unmappedData, layer.tracing.elementClass)
segmentIds = editableMappingService.collectSegmentIds(unmappedDataTyped)
segmentIds <- editableMappingService.collectSegmentIds(unmappedData, layer.elementClass).toFox
relevantMapping <- editableMappingService.generateCombinedMappingForSegmentIds(segmentIds,
editableMappingInfo,
version,
layer.tracingId,
remoteFallbackLayer)(tc)
mappedData <- editableMappingService.mapData(unmappedDataTyped, relevantMapping, elementClassProto)
mappedData <- editableMappingService.mapData(unmappedData, relevantMapping, elementClassProto)
} yield mappedData
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,15 @@ import com.scalableminds.webknossos.datastore.SegmentToAgglomerateProto.SegmentT
import com.scalableminds.webknossos.datastore.SkeletonTracing.{Edge, Tree, TreeTypeProto}
import com.scalableminds.webknossos.datastore.VolumeTracing.VolumeTracing
import com.scalableminds.webknossos.datastore.VolumeTracing.VolumeTracing.ElementClassProto
import com.scalableminds.webknossos.datastore.helpers.{NodeDefaults, ProtoGeometryImplicits, SkeletonTracingDefaults}
import com.scalableminds.webknossos.datastore.helpers.{
NativeBucketScanner,
NodeDefaults,
ProtoGeometryImplicits,
SkeletonTracingDefaults
}
import com.scalableminds.webknossos.datastore.models.DataRequestCollection.DataRequestCollection
import com.scalableminds.webknossos.datastore.models._
import com.scalableminds.webknossos.datastore.models.datasource.ElementClass
import com.scalableminds.webknossos.datastore.models.requests.DataServiceDataRequest
import com.scalableminds.webknossos.datastore.services.{
AdHocMeshRequest,
Expand Down Expand Up @@ -104,7 +110,7 @@ class EditableMappingService @Inject()(

val defaultSegmentToAgglomerateChunkSize: Int = 64 * 1024 // max. 1 MiB chunks (two 8-byte numbers per element)

val binaryDataService = new BinaryDataService(Paths.get(""), None, None, None, None)
private val binaryDataService = new BinaryDataService(Paths.get(""), None, None, None, None)

adHocMeshServiceHolder.tracingStoreAdHocMeshConfig = (binaryDataService, 30 seconds, 1)
private val adHocMeshService: AdHocMeshService = adHocMeshServiceHolder.tracingStoreAdHocMeshService
Expand All @@ -115,6 +121,8 @@ class EditableMappingService @Inject()(
private lazy val agglomerateToGraphCache: AlfuCache[(String, Long, Long), AgglomerateGraph] =
AlfuCache(maxCapacity = 50)

private lazy val nativeBucketScanner: NativeBucketScanner = new NativeBucketScanner()

def infoJson(tracingId: String, editableMappingInfo: EditableMappingInfo): JsObject =
Json.obj(
"tracingId" -> tracingId,
Expand Down Expand Up @@ -340,27 +348,31 @@ class EditableMappingService @Inject()(
} yield segmentIdsOrdered.zip(agglomerateIdsOrdered).toMap
}

def collectSegmentIds(data: Array[SegmentInteger]): Set[Long] =
data.toSet.map { u: SegmentInteger =>
u.toLong
}
def collectSegmentIds(bytes: Array[Byte], elementClass: ElementClass.Value): Box[Set[Long]] =
tryo(
nativeBucketScanner
.collectSegmentIds(bytes,
ElementClass.bytesPerElement(elementClass),
ElementClass.isSigned(elementClass),
skipZeroes = false)
.toSet)

def mapData(unmappedData: Array[SegmentInteger],
def mapData(unmappedData: Array[Byte],
relevantMapping: Map[Long, Long],
elementClass: ElementClassProto): Fox[Array[Byte]] = {
val mappedDataLongs = unmappedData.map(element => relevantMapping(element.toLong))
elementClass: ElementClassProto): Fox[Array[Byte]] =
for {
unmappedDataTyped <- bytesToSegmentInt(unmappedData, elementClass)
mappedDataLongs = unmappedDataTyped.map(element => relevantMapping(element.toLong))
bytes <- longsToBytes(mappedDataLongs, elementClass)
} yield bytes
}

private def bytesToLongs(bytes: Array[Byte], elementClass: ElementClassProto): Fox[Array[Long]] =
for {
_ <- bool2Fox(!elementClass.isuint64)
segmentIntArray <- tryo(SegmentIntegerArray.fromByteArray(bytes, elementClass)).toFox
} yield segmentIntArray.map(_.toLong)

def bytesToSegmentInt(bytes: Array[Byte], elementClass: ElementClassProto): Fox[Array[SegmentInteger]] =
private def bytesToSegmentInt(bytes: Array[Byte], elementClass: ElementClassProto): Fox[Array[SegmentInteger]] =
for {
_ <- bool2Fox(!elementClass.isuint64)
segmentIntArray <- tryo(SegmentIntegerArray.fromByteArray(bytes, elementClass)).toFox
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,10 @@ class VolumeSegmentIndexService @Inject()(val tracingDataStore: TracingDataStore
private def collectSegmentIds(bytes: Array[Byte], elementClass: ElementClass.Value): Box[Set[Long]] =
tryo(
nativeBucketScanner
.collectSegmentIds(bytes, ElementClass.bytesPerElement(elementClass), ElementClass.isSigned(elementClass))
.collectSegmentIds(bytes,
ElementClass.bytesPerElement(elementClass),
ElementClass.isSigned(elementClass),
skipZeroes = true)
.toSet)

def getSegmentToBucketIndex(tracing: VolumeTracing,
Expand Down