100 million
live records.

Memory2 gives C# and .NET one fast, queryable, reactive, and durable state for the entire application.

Measured in .NET

One live store. Every workload.

Memory2 created and populated 100 million connected records, then ran reads, writes, native LINQ, mixed traffic, deletion, synchronization, persistence, verification, and restore against that same live C# store.

52.966 secto create 100 million records

1.89 million fully assigned records per second.

723,295/secfull object reads

Memory2 built complete application objects for the caller.

7.35M/secwrite speed

136.12 nanoseconds per tracked write. Zero allocation.

196.32M/secconnected query speed

Records checked while following relationships across the populated store.

47.62M/seccomplex query speed

Filtering, grouping, ranking, and composition in one measured query.

7.25M/secmixed traffic

80% reads, 15% queries, 5% writes. Zero allocation.

5.23 GBtotal measured memory

For the complete 100-million-record store.

Durablesave, verify, restore

The complete store can be written to disk, checked, reopened, and used again.

3.083 GBsaved size

30.84 bytes on disk per live record.

One store. Every test.

No reduced query sample. No separate read dataset. No tiny write benchmark. The full population remained present while Memory2 was read, changed, queried, mixed, deleted, synchronized, and saved.

Query, hydrate, or read

Ask first. Build objects only when you need them.

Memory2 separates answering a question from constructing application models. Queries can inspect millions of stored values without creating every object they examine. Hydration turns only the selected matches into complete C# object graphs.

196.32M/sec

Query

Filter, count, rank, join, group, or project directly from Memory2. The engine creates only the final answer requested by the query.

THEN
Hydrate()

Build the selected objects

After narrowing the results, hydrate the matches into complete C# models, including their stored owned descendants.

Query decides which objects matter. Hydrate builds those objects. A known object can still be read directly by identity when no search is required.
Filter first, hydrate secondcomplete graphs for selected results
// The filter runs inside Memory2.
Fleet[] fleets = db.From<Fleet>()
    .Where(x => x.Name.StartsWith("fleet-east"))
    .Take(10)
    .Hydrate();

// Each result is now a complete stored C# object graph.
Console.WriteLine(fleets[0].Vehicles[0].Tires.Count);

Need an answer?

Stay in LINQ. Return a scalar, projection, count, ranked window, group, or final result without constructing complete models.

Need complete matching models?

Call Hydrate() after filtering. Memory2 builds only the selected object graphs.

Know the identity already?

Use a direct read such as GetRequired to retrieve the complete stored object graph.

Need one inner object?

Query that type directly and save it directly. Hydrating its owner is optional.

Native LINQ

It looks like LINQ.
It does not run like ordinary LINQ.

Memory2 accepts familiar C# query expressions, then executes them through its own prepared engine. The release suite verified the common operators, composed plans, joins, grouping, ranking, set work, and terminal operations.

LINQ across 100 million live records.

This is not LINQ-to-Objects walking a collection. Memory2 accepts familiar LINQ expressions and executes them through its own query engine.

196.32M/secconnected query throughput
47.62M/seccomplex query throughput
0 bytesallocated by measured scan workloads
24verified LINQ operations

Every number below comes from the 100-million-record release run. “Per item” measures each input examined. “Per call” measures operations that return directly from a prepared view.

Filtering, projection, and totals

LINQ operationMeasured speed
Contains2.2 ns/item
Aggregate2.3 ns/item
SequenceEqual2.3 ns/item
Select2.6 ns/item
All2.9 ns/item
Zip2.9 ns/item
Min / Max4.4 ns/item
Where5.5 ns/item

Sets, groups, joins, and ranking

LINQ operationMeasured speed
Union12.6 ns/item
GroupBy13.1 ns/item
Join13.6 ns/item
Except15.1 ns/item
Distinct15.2 ns/item
Intersect15.4 ns/item
Connected result16.9 ns/item
Rank48.1 ns/item

Direct prepared operations

LINQ operationMeasured speed
Pure terminal result671.4 ns/call
Count / LongCount2.7 ns/call
Concat / Append / Prepend3.1 ns/call
ElementAt3.6 ns/call
DefaultIfEmpty4.4 ns/call
First / Last4.8 ns/call
Skip / Take / Reverse8.2 ns/call
Any17.7 ns/call

Full-query proof

Measured queryResult
Connected query196.32M rows/sec
Complex composed query47.62M rows/sec
Fallback execution0
Temporary models0
Full sort paths0
Measured scan allocation0 bytes

The operator figures are engine measurements, not end-to-end application latency. The full-query rates above show how the prepared operators behave when composed against the live 100-million-record store.

More LINQ does not always mean more runtime.

Memory2 does not run a chain as a stack of separate collection passes. It prepares the complete expression, combines compatible filters into one scan, carries only the state the final answer needs, and removes work that cannot change that answer.

Several Where callscan become one restriction pass
Count, Any, and Sumcan run inside the same scan
Unused orderingcan disappear when the result does not need it
Small ranked windowsretain the window, not the whole population

The release proof recorded one planner compilation, 42,289 fused stages, 28,194 eliminated stages, zero fallback execution, and zero measured allocation. The runtime follows the final question, not the number of method calls written above it.

var urgent = db.Query<WorkItem>()
    .Include<Account>(accounts => accounts
        .Where(x => x.Active && x.Risk >= 900)
        .WhereExists<Event>(
            (account, evt) => account.Id == evt.AccountId,
            evt => evt.Open && evt.Severity >= 8)
        .WhereNotExists<Review>(
            (account, review) => account.Id == review.AccountId,
            review => review.Accepted)
        .Select(x => new WorkItem
        {
            Id = x.Id,
            Priority = x.Risk,
            Action = "Review now"
        }))
    .OrderByDescending(x => x.Priority)
    .Take(100)
    .ToList();

Familiar code.

Where, Select, Any, All, GroupBy, Join, Distinct, Union, Intersect, Except, ranking, windows, and aggregates were all verified in the release run.

Memory2 execution.

The verified plans reported zero fallback paths, zero temporary models, zero full sort paths, and zero measured allocation in the scan workloads.

Direct access

Query the exact part you need.

Memory2 can query an object inside a much larger model without loading the object around it.

When the owner matters, Memory2 can locate it. When only the inner object changes, save that object directly. The surrounding model never has to be loaded or rewritten.

Find the tire. Locate the vehicle only when needed. Save the tire without loading the vehicle.

Hydrate() serves the opposite case: use it when application code needs complete graphs for the filtered results. Direct access remains the lean path when one inner object is enough.

Query directly

Search every tire as its own queryable C# type.

Locate the owner

Recover the vehicle that contains a returned tire when application logic needs it.

Save only the change

Update and save the tire itself. No vehicle load. No vehicle rewrite.

Keep queries fast

Filtering and projection can operate on the exact stored values the answer requires.

Direct query, owner recovery, direct saveone typed Memory2 surface
// Query tires directly. No vehicle is loaded.
var tire = db.Query<Tire>()
    .Where(x => x.Color == TireColor.Green)
    .OrderByDescending(x => x.Pressure)
    .First();

// Locate the containing vehicle only when needed.
Vehicle vehicle = db.Root<Vehicle>(tire);

// Change and save the tire itself.
tire.Pressure = 34;
db.Save(tire);

// The vehicle was not required for the save.
Subscriptions

Logic can watch truth instead of chasing messages.

A region, risk condition, health threshold, machine fault, render set, or physical contact can be expressed once.

When state enters, changes inside, or leaves that condition, Memory2 calls the subscribed logic.

The condition is the contract. Producers only change state.
Subscribe to a regionlogic follows the current answer
var machines = db.Set<MachineState>();

using var hotZone = machines.Subscribe(
    x => x.RegionId == "reactor-7" &&
         x.Enabled &&
         x.Temperature >= 95,
    change =>
    {
        if (change.After is not null)
            cooling.Schedule(change.After.Id);
        else
            cooling.Cancel(change.Before.Id);
    });
Games, robots, AI

The same memory can drive the whole system.

Game rules can watch state directly. Robots can expose only failed parts. AI pipelines can select unresolved cases after exact rules have run.

Memory2 does not force these systems into separate stores, caches, event catalogs, and reporting copies.

One live state. Different questions.
Only unresolved cases reach AIknown facts stay exact
var training = db.Query<LearningCase>()
    .Include<Observation>(items => items
        .Where(x => x.Value < x.Minimum || x.Value > x.Maximum)
        .WhereNotExists<AcceptedAnswer>(
            (x, answer) => x.Id == answer.ObservationId,
            answer => answer.Accepted)
        .Select(x => new LearningCase
        {
            ObservationId = x.Id,
            Prompt = x.Description
        }))
    .ToList();
Durability

Save it. Verify it. Restore it.

The populated store persisted to a verified 3.083 GB file and reopened as usable Memory2 state.

In the supplied comparison, Memory2 used 5.23 GB of measured heap while the same-width conventional CLR graph was estimated at 9.28 GB. The saved store was 900 MB smaller than the compact-row estimate.

Measured Memory2 totals are shown separately from the conventional estimates.
Save, reopen, verifythe live state survives the process
using (var db = MemoryStore.Open(stream, leaveOpen: true))
{
    // 100 million live records
    db.Save();

    if (!db.Verify().IsValid)
        throw new InvalidDataException();
}

stream.Position = 0;
using var reopened = MemoryStore.Open(stream, leaveOpen: true);
Synchronization

Apply a change once.

Memory2 can accept ordered changes from another process, service, device, or replica while keeping track of what has already been applied.

The sender provides its identity and sequence number with the change. Memory2 can distinguish a new update from a replay, a missing sequence, or a conflicting write.

A retry does not become a duplicate change. A missing update does not pass unnoticed. A conflict is returned as a conflict.

Applied

The next expected change is accepted and becomes part of the store.

Already applied

The same sequence arrives again. Memory2 recognizes the replay and does not apply it twice.

Gap

A later sequence arrives before an earlier one. The store reports the missing change instead of silently skipping it.

Conflict

The expected version no longer matches. The caller receives a conflict instead of overwriting newer state.

Ordered, replay-safe changesone public Memory2 operation
var result = db.Apply(
    new MemoryChangeSet()
        .Upsert(
            source: "warehouse-east",
            sequence: 42,
            model: shipment,
            expectedMutationVersion: knownVersion));

switch (result.Status)
{
    case ApplyStatus.Applied:
        PublishCurrentState();
        break;

    case ApplyStatus.AlreadyApplied:
        // Safe retry. Nothing is written twice.
        break;

    case ApplyStatus.Gap:
        RequestMissingChanges();
        break;

    case ApplyStatus.Conflict:
        ResolveConcurrentChange();
        break;
}
C# and .NET

It belongs in the codebase you already have.

Memory2 is built for C# developers. Use typed models, familiar LINQ expressions, tracked changes, subscriptions, synchronization, and durable storage without moving the application into another query language.

Native LINQ execution.

Your expression is prepared by Memory2. It is not handed to LINQ-to-Objects after building millions of CLR objects.

Typed application models.

Read complete C# objects when application code needs them. Query stored values directly when the answer does not require object construction.

Reactive state.

Subscribe to conditions in the same store. Logic runs when data enters, changes within, or leaves the answer.

Durable .NET state.

Save the store to disk, verify it, reopen it, and continue using the same typed surface.

Open

MemoryStore.Open(...)

Create

db.Set<T>().Create(model)

Read

Get builds the complete C# object.

Query

Where, Select, GroupBy, Join, ranking, and aggregates run through Memory2.

Connect

Include, WhereExists, WhereNotExists

Reach inside

Query inner types, recover their owner with Root<T>, and save them directly.

Watch

Subscribe(condition, handler)

Change

Upsert, tracked Save, deletion

Persist

db.Save(), reopen, verify

Synchronize

db.Apply(new MemoryChangeSet()...)

Build the whole system in one memory

One live state.
Every question. Every change.

Memory2 brings storage, LINQ, selective hydration, direct access to inner objects, subscriptions, synchronization, and persistence into one C# system. Your application can read objects, ask large questions, react to changing conditions, and carry the same verified state across process boundaries.

Memory2 was designed by Bryly Maeder.