Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
116 changes: 116 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
name: Benchmark Rust version

on: [push, pull_request]

env:
toolchain: nightly-2024-01-01
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

jobs:
benchmark:
runs-on: ubuntu-latest

services:
neo4j:
image: neo4j:5.15.0
env:
NEO4J_AUTH: neo4j/password
NEO4J_PLUGINS: '[]'
options: >-
--health-cmd "wget -q --spider http://localhost:7474 || exit 1"
--health-interval 10s
--health-timeout 10s
--health-retries 10
--health-start-period 30s
ports:
- 7474:7474
- 7687:7687

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Setup Rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{env.toolchain}}
components: rustfmt, clippy

- name: Wait for Neo4j to be ready
run: |
echo "Waiting for Neo4j to be fully ready..."
for i in {1..30}; do
if curl -s http://localhost:7474 > /dev/null; then
echo "Neo4j is ready!"
break
fi
echo "Attempt $i: Neo4j not ready yet..."
sleep 2
done

- name: Build benchmark
run: cargo build --release --all-features --manifest-path rust/Cargo.toml

- name: Run benchmark
working-directory: rust
env:
NEO4J_URI: bolt://localhost:7687
NEO4J_USER: neo4j
NEO4J_PASSWORD: password
run: cargo bench --bench bench -- --output-format bencher | tee out.txt

- name: Prepare benchmark results
run: |
git config --global user.email "[email protected]"
git config --global user.name "LinksPlatformBencher"
cd rust
pip install numpy matplotlib
python3 out.py
cd ..

# Create Docs directory if it doesn't exist
mkdir -p Docs

# Copy generated images
cp -f rust/bench_rust.png Docs/
cp -f rust/bench_rust_log_scale.png Docs/

# Update README with latest results
if [ -f rust/results.md ]; then
# Replace the results section in README.md
python3 -c "
import re

with open('rust/results.md', 'r') as f:
results = f.read()

with open('README.md', 'r') as f:
readme = f.read()

# Pattern to find and replace the results table
pattern = r'(\| Operation.*?\n\|[-|]+\n(?:\|.*?\n)*)'
if re.search(pattern, readme):
readme = re.sub(pattern, results.strip() + '\n', readme)

with open('README.md', 'w') as f:
f.write(readme)
"
fi

# Commit changes if any
git add Docs README.md
if git diff --staged --quiet; then
echo "No changes to commit"
else
git commit -m "Update benchmark results"
git push origin HEAD
fi

- name: Save benchmark results
uses: actions/upload-artifact@v4
with:
name: Benchmark results
path: |
rust/bench_rust.png
rust/bench_rust_log_scale.png
rust/out.txt
Binary file added Docs/bench_rust.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Docs/bench_rust_log_scale.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
49 changes: 48 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,48 @@
# Comparisons.Neo4jVSDoublets
# Comparisons.Neo4jVSDoublets

The comparison between Neo4j and LinksPlatform's Doublets (links) on basic database operations with links (create, read, delete, update).
All benchmarks ran with 3000 links in background to increase size of indexes and 1000 are actively created/updated/deleted.

In this particular benchmark we decided not to increase the number of links as Neo4j will not be able to handle it at all in timeframe what GitHub Actions limit allows to use for free. Remember that to get accurate result we ran this benchmark multiple times.

## Task

Both databases used to store and retrieve doublet-links representation. To support storage, and all basic CRUD operations that provide Turing completeness for links as in [the links theory](https://habr.com/ru/articles/895896).

## Operations
- **Create** – insert point link (link with id = source = target)
- **Update** – basic link update operation
- **Delete** – basic link delete operation
- **Each All** – take all links matching `[*, *, *]` constraint
- **Each Incoming** – take all links matching `[*, *, target]` constraint
- **Each Outgoing** – take all links matching `[*, source, *]` constraint
- **Each Concrete** – take all links matching `[*, source, target]` constraint
- **Each Identity** – take all links matching `[id, *, *]` constraint

## Results
The results below represent the amount of time (ns) the operation takes per iteration.
- First picture shows time in a pixel scale (for doublets just minimum value is shown, otherwise it will be not present on the graph).
- Second picture shows time in a logarithmic scale (to see difference clearly, because it is around 2-3 orders of magnitude).

### Rust
![Image of Rust benchmark (pixel scale)](https://github.com/linksplatform/Comparisons.Neo4jVSDoublets/blob/main/Docs/bench_rust.png?raw=true)
![Image of Rust benchmark (log scale)](https://github.com/linksplatform/Comparisons.Neo4jVSDoublets/blob/main/Docs/bench_rust_log_scale.png?raw=true)

### Raw benchmark results (all numbers are in nanoseconds)

| Operation | Doublets United Volatile | Doublets United NonVolatile | Doublets Split Volatile | Doublets Split NonVolatile | Neo4j NonTransaction | Neo4j Transaction |
|---------------|--------------------------|-----------------------------|-------------------------|----------------------------|----------------------|-------------------|
| Create | 99201 (0.3x faster) | 101660 (0.3x faster) | 85197 (0.4x faster) | 83516 (0.4x faster) | 3206352817 | 31808 |
| Update | N/A | N/A | N/A | N/A | N/A | N/A |
| Delete | N/A | N/A | N/A | N/A | 1955576426 | N/A |
| Each All | N/A | N/A | N/A | N/A | N/A | N/A |
| Each Identity | N/A | N/A | N/A | N/A | N/A | N/A |
| Each Concrete | N/A | N/A | N/A | N/A | N/A | N/A |
| Each Outgoing | N/A | N/A | N/A | N/A | N/A | N/A |
| Each Incoming | N/A | N/A | N/A | N/A | N/A | N/A |

## Conclusion

The benchmark results will be automatically updated once the GitHub Actions workflow runs. Results are expected to show that Doublets significantly outperforms Neo4j in both write and read operations due to its specialized data structure for storing doublet-links.

To get fresh numbers, please fork the repository and rerun benchmark in GitHub Actions.
2 changes: 2 additions & 0 deletions rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[toolchain]
channel = "nightly-2022-08-22"
Loading