C-style structs on the JVM!
APACHE-2.0 License
C-style structs on the JVM!
ZERO garbage, ZERO reflection, ZERO code generation. EASY to use, and with INCREDIBLE performance.
Strukts
to manage your Strukt
First you need to create a Strukts
which manages your Strukt
(allocates, frees, etc.).
Fixed: uses a single memory allocation and addresses are given with an offset. This is the fastest option available.
object Points : FixedStrukts(Point::class, capacity)
Persisted fixed: same as above, except uses a memory-mapped file rather than system memory and persists to disk.
This means you can allocate with as much space as disk space, rather than RAM. There is practically no performance
penalty for using this over .fixed(capacity)
!
object Points : FixedStrukts(Point::class, capacity, persistedTo: File|String)
Elastic: similar to .fixed(capacity)
except will dynamically resize, and has a performance penalty of a few
operations for that capability.
object Points : ElasticStrukts(Point::class, initialCapacity = 1024, growthFactor = 2.0)
Pointed: uses a laid-out pointer system, this option supports unlimited allocations without any "resizing" necessary. Note: Since a memory allocation and a memory copy is necessary, this has slow allocations.
object Points : PointedStrukts(Point::class)
Strukt
You need to declare your fields within your Strukts
s using delegates to the default value.
The value you delegate to determines the field's type, and the default value for each allocation -- in this case it's
an Int
of the default value 0
for both.
object Points : ElasticStrukts(Point::class) {
val x by 0
val y by 0
}
All primitive types are supported, and even enums:
enum class Type { DEFAULT, SPECIAL }
object Points : ElasticStrukts(Point::class) {
val x by 0
val y by 0
val type by Type.DEFAULT
}
Strukt
inline class Point(override val address: Long = Points()) : Strukt {
var x
get() = Points.x(address)
set(x) = Points.x(address, x)
var y
get() = Points.y(address)
set(y) = Points.y(address, y)
}
Because of the way inlining works, you'll notice that we need to provide virtual fields using our previously defined fields.
You should also notice the use of Strukts
.invoke
as a default value for allocating our address.
Strukt
The syntax for allocation is the same as a regular object!
For the above Point example, this might look like:
val example = Point()
Accessing fields is exactly like normal objects!
example.y = 123
println("x: ${example.x}, y: ${example.y}") // 0, 123
Strukt
To free any Strukt
, you can use:
Points.free(example.address)
The reason this longer syntax is necessary is because we want to avoid "generic" type use of our inline class, to prevent creating boxed instances.
It's easy to make your fields thread-safe! Just annotate them with @ThreadSafe
and use a ThreadSafeType
(NONE
, VOLATILE
, or SYNCHRONIZED
).
import org.jire.strukt.ThreadSafeType.*
object Points : ElasticStrukts(Point::class) {
@ThreadSafe(VOLATILE) val x by 0
@ThreadSafe(SYNCHRONIZED) val y by 0
}
It's not too hard to define and use a Strukt
in Java!
public interface Point extends Strukt {
Strukts points = Strukts.elastic(Point.class);
IntField x = points.intField(0);
IntField y = points.intField(0, ThreadSafeType.VOLATILE);
static long allocate() {
return points.allocate();
}
}
You can then use it like so:
long example = Point.allocate();
Point.y.set(example, 123);
System.out.println("x: " + Point.x.get(example) + ", y: " + Point.y.get(example)); // 0, 123
Benchmark sources: https://github.com/Jire/Strukt/tree/master/src/jmh/kotlin/org/jire/strukt/benchmarks
Benchmark Mode Cnt Score Error Units
HeapRead.read thrpt 434769304.174 ops/s
PointedRead.read thrpt 372296437.498 ops/s
FixedRead.read thrpt 438208243.968 ops/s
ElasticRead.read thrpt 418701531.606 ops/s
HeapWrite.write thrpt 561591456.153 ops/s
PointedWrite.write thrpt 622915634.215 ops/s
FixedWrite.write thrpt 631370517.687 ops/s
ElasticWrite.write thrpt 600571289.654 ops/s
HeapAllocate.allocate ss 96.597 ms/op
PointedAllocate.allocate ss 20556.955 ms/op
FixedAllocate.allocate ss 2436.920 ms/op
ElasticAllocate.allocate ss 1882.812 ms/op
FixedFree.free ss 4836.308 ms/op
ElasticFree.free ss 4876.006 ms/op