# Multiprocessor

Multiprocessor is a powerful package enabling developers to **manually spawn EVM instances** for parallel tasks, allows the programmer to fully leverage the computation power of modern multiple processor design. These jobs run in isolated EU instances and are **subject to the same commutativity checks** as EOA-submitted transactions. The system automatically detects conflicts and merges safe updates deterministically.

## How It Works

The parent execution unit (EU) spawns multiple child EUs to execute transactions in parallel. Each child runs in isolation, and its state changes are passed to the Conflict Detector. Only clean, non-conflicting changes are merged back into the parent EU, ensuring deterministic and safe parallelism.

* **Storage:** Child EUs receive a *snapshot copy* of the parent’s storage state at the moment of forking. They do not write directly to global storage during execution. They accumulate tentative state changes in a local cache. After execution, these changes are passed to the **Conflict Detector**, only **clean, non-conflicting storage changes** are committed back to the parent EU.
* **Memory**: Each spawned EU gets its **own memory** space.  Memory is **not shared**, **not snapshotted**, and **has no impact on conflict detection**. You can use memory freely within each parallel job without restriction. Since they have no impact on the global state.

{% hint style="success" %}
The conflict rules applied to EOA transitions are also applicable to these multiprocessor-spawned tasks.
{% endhint %}

***

<figure><img src="https://1085453317-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQ4LfmpelZIw0vWz1C66M%2Fuploads%2FkZouLqpnewvw6PS5GGd4%2Fimage.png?alt=media&#x26;token=e367fb54-6eed-4948-93ae-4bcfa76755fc" alt=""><figcaption><p>This diagram is a <strong>visual explanation</strong> showing how <strong>a parent execution unit (EU)</strong> spawns <strong>child EUs</strong> and how <strong>conflict detection and state merging</strong> work.</p></figcaption></figure>

## Implementation

The  [`Multiprocess` ](https://github.com/arcology-network/concurrentlib/tree/main/lib/multiprocess/Multiprocess.sol)offers a queue, which **temporarily holds a list of tasks** before execution begins. State consistency is rigorously guaranteed all the time.  The actual parallel execution of the task in the queue won't start until the function `run()` is called. After execution, it checks for **conflicts (e.g., write to same storage)** and **only merges non-conflicting (commutative) state changes** back into the main state.

<table><thead><tr><th width="217.0301513671875">Function</th><th>Descriptions</th></tr></thead><tbody><tr><td><code>Multiprocess</code></td><td>Manages job queue and controls execution across isolated EUs.</td></tr><tr><td><code>addJob(...)</code></td><td>Adds a job to the queue — each job includes a gas limit, value to transfer (optional, <strong>0</strong> by default), target contract, and calldata.</td></tr><tr><td><code>run()</code></td><td>Executes all jobs in parallel.</td></tr><tr><td><strong>Conflict Detection</strong></td><td>After <code>run()</code> is called, Arcology <strong>automatically</strong> performs conflict detection to ensure state consistency. Any jobs that violate commutativity (e.g., write to the same storage key) are reverted and excluded from the final state merge.</td></tr></tbody></table>

## Examples

The examples highlight the main features and applications of Arcology's multiprocessing capability. They demonstrate how Arcology's technology can enhance the execution of smart contracts and provide significant benefits in terms of scalability and efficiency.&#x20;

### Parallel Assignment

This contract illustrates how two independent value assignments can be executed **in parallel**, safely merged back into the global state, and verified for correctness — all within a single transaction.

```solidity
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.0 <0.9.0;

import "@arcologynetwork/concurrentlib/lib/multiprocess/Multiprocess.sol";

contract ParaAssigment {
    uint256 _1;
    uint256 _2;
    
    function call() public  { 
       Multiprocess mp = new Multiprocess(2); // Two parallel processors.
       
       // Adds two parallel jobs into the queue, each with a gas limit, value, 
       // address and calldata-very similar to address.call{}() syntax. 
       mp.addJob(50000, 0, address(this), abi.encodeWithSignature("assigner(uint256)", 0));
       mp.addJob(50000, 0, address(this), abi.encodeWithSignature("assigner(uint256)", 1));
       
       /* 
        Run two jobs with two new EU instances in parallel.
        Conflict detector will check the state they touch to preserve 
        commutativity after execution. Only clean state changs are merged back to the 
        parent EVM instance.
        */
       mp.run(); 

       assert(_1 == 10); // Check if the changes have been merged back.
       assert(_2 == 12);
    }

    function assigner(uint256 v)  public {
        if (v == 0) {
            _1 = v + 10; // For the first call
            return;
        }
        _2 = v + 11; // For the first call
    }
}   
 
```

{% stepper %}
{% step %}
The contract declares two state variables, `_1` and `_2`, and sets up two parallel jobs using the `Multiprocess` API with two execution units. Job 0 runs `assigner(0)`, setting `_1 = 10`, while Job 1 runs `assigner(1)`, setting `_2 = 11`.&#x20;
{% endstep %}

{% step %}
These jobs are executed in parallel inside isolated EU instances using `mp.run()`. After execution, Arcology’s conflict detector verifies that the two jobs modify separate, non-overlapping state, confirming no conflicts.&#x20;
{% endstep %}

{% step %}
Since the updates are commutative and independent, their changes are safely merged back into the parent context. The final assertions verify that `_1` is 10 and `_2` is 11, confirming the success of deterministic parallel execution.
{% endstep %}
{% endstepper %}

***

### A Conflict Example

Similar to the example above, the contract `ParaAssignmentWithConflict` contains a function `call()` that utilizes the `Multiprocess` contract to execute the function `assigner()` in parallel. The `assigner()` function takes an input `v` and assigns the value `v + 10` to the element at index `v` in the `results` array.&#x20;

The major difference is when `assigner()` is no longer thread-safe, a **write-write conflict** happens at runtime. But the system reverts one transaction to preserve determinism. Only non-conflicting results are merged into the final state.

<pre class="language-solidity"><code class="lang-solidity">// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.0 &#x3C;0.9.0;

import "@arcologynetwork/concurrentlib/lib/multiprocess/Multiprocess.sol";

<strong>contract ParaAssignmentWithConflict {
</strong>    uint256[2] results;
    function call() public  { 
       Multiprocess mp = new Multiprocess(2); 
       mp.addJob(50000, 0, address(this), abi.encodeWithSignature("assigner(uint256)", 0));
       mp.addJob(50000, 0, address(this), abi.encodeWithSignature("assigner(uint256)", 0));
       mp.addJob(50000, 0, address(this), abi.encodeWithSignature("assigner(uint256)", 1));
       mp.run();
       
       assert(results[0] == 10);
       assert(results[1] == 11);
    }

    function assigner(uint256 v)  public {
        results[v] = v + 10;
    }
} 
</code></pre>

Here's a step-by-step explanation of what happens:

{% stepper %}
{% step %}
The `call()` function is called, and it creates a new instance of the `Multiprocess` contract with 2 threads (processes).
{% endstep %}

{% step %}
Two messages are added into the `Multiprocess` container with the function signature "assigner(uint256)" and respective values `0` and `1`.
{% endstep %}

{% step %}
The `mp.run()` function is called, which executes the two messages in parallel using the specified number of threads (2).
{% endstep %}

{% step %}
Arcology detects the overlapping write to the same storage key. One of the conflicting jobs is **reverted**. The third job (`assigner(1)`) writes to `results[1]` and is **conflict-free**, so it’s committed.
{% endstep %}

{% step %}
Only one write to `results[0]` is retained.`results[0] == 10`, `results[1] == 11` so the assertions pass.
{% endstep %}
{% endstepper %}
