Merge branch 'master' into library-use-case

This commit is contained in:
Nicolas Luck
2023-12-04 20:06:14 +01:00
47 changed files with 1622 additions and 821 deletions

52
.github/actions/setup-rust/action.yml vendored Normal file
View File

@@ -0,0 +1,52 @@
name: 'Setup Rust'
inputs:
rust-version:
required: true
type: string
targets:
required: true
type: string
components:
required: false
default:
cache-context:
required: true
type: string
runs:
using: "composite"
steps:
- uses: dtolnay/rust-toolchain@master
id: toolchain
with:
toolchain: ${{ inputs.rust-version }}
targets: ${{ inputs.targets }}
components: ${{ inputs.components }}
- name: Install i686 dependencies
if: "contains(inputs.targets,'i686')"
shell: bash
run: |
sudo dpkg --add-architecture i386
sudo apt-get update
sudo apt-get install libssl-dev:i386 gcc-multilib clang -y
echo "CC=clang" >> $GITHUB_ENV
echo "PKG_CONFIG_SYSROOT_DIR=/" >> $GITHUB_ENV
- uses: actions/cache@v3
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ inputs.cache-context }}_${{ inputs.targets }}_rustc-${{ steps.toolchain.outputs.cachekey }}_cargo-${{ hashFiles('**/Cargo.lock') }}
# Remove build artifacts for the current crate, since it will be rebuilt every
# run anyway, but keep dependency artifacts to cache them.
# Must be placed after actions/cache so its post step runs first.
- uses: pyTooling/Actions/with-post-step@v0.4.6
with:
main: bash ./.github/actions/setup-rust/cleanup.sh
post: bash ./.github/actions/setup-rust/cleanup.sh

13
.github/actions/setup-rust/cleanup.sh vendored Executable file
View File

@@ -0,0 +1,13 @@
#!/usr/bin/env bash
set -e
echo Cleanup workspace build artifacts and extra target output
# clean just the direct members of the current workspace, use cargo metadata to generalize to all rust projects
cargo clean -p `cargo metadata --no-deps --offline --format-version 1 | jq -r '[.workspace_members[]|split(" ")|.[0]]|join(" ")'`
# remove directories in /target/ that are not named `debug` or `release`
before=`du -s target | awk '{print $1}'`
find ./target -maxdepth 1 -type d ! -name debug ! -name release ! -name target -exec rm -r {} \;
after=`du -s target | awk '{print $1}'`
echo Deleted $(($before - $after)) bytes from target directory

View File

@@ -10,47 +10,26 @@ on:
- cron: '0 0 * * 3' # At 12:00 AM, only on Wednesday - cron: '0 0 * * 3' # At 12:00 AM, only on Wednesday
workflow_dispatch: workflow_dispatch:
permissions:
checks: write
jobs: jobs:
style: style:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@master - name: Setup Rust
id: toolchain uses: ./.github/actions/setup-rust
with: with:
toolchain: nightly rust-version: nightly
targets: x86_64-unknown-linux-gnu targets: x86_64-unknown-linux-gnu
components: clippy, rustfmt components: clippy, rustfmt
- run: cargo install cargo2junit --force cache-context: style
- uses: actions/cache@v3
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: style_rustc-${{ steps.toolchain.outputs.cachekey }}_cargo-${{ hashFiles('**/Cargo.lock') }}
- name: Check formatting - name: Check formatting
run: cargo fmt --check run: cargo fmt --check
- name: Check clippy - name: Check clippy
run: cargo clippy --no-deps --all-targets run: cargo clippy --no-deps --all-targets
- name: Test and report
run: |
RUSTC_BOOTSTRAP=1 cargo test --all -- -Z unstable-options --format json --report-time | cargo2junit > cargo_test_results.xml
- name: Publish cargo test results artifact
uses: actions/upload-artifact@v3
with:
name: cargo-test-results
path: cargo_test_results.xml
- name: Publish cargo test summary
uses: EnricoMi/publish-unit-test-result-action/composite@master
with:
check_name: Cargo test summary
files: cargo_test_results.xml
fail_on: nothing
comment_mode: off
build-test: build-test:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
@@ -75,34 +54,19 @@ jobs:
shell: bash shell: bash
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@master - name: Setup Rust
id: toolchain uses: ./.github/actions/setup-rust
with: with:
toolchain: ${{ matrix.rust-version }} rust-version: ${{ matrix.rust-version }}
targets: ${{ matrix.target }} targets: ${{ matrix.target }}
- name: Install i686 dependencies cache-context: ${{ matrix.os }}
if: "contains(matrix.target,'i686')"
run: |
sudo dpkg --add-architecture i386
sudo apt-get update
sudo apt-get install libssl-dev:i386 gcc-multilib clang -y
echo "CC=clang" >> $GITHUB_ENV
echo "PKG_CONFIG_SYSROOT_DIR=/" >> $GITHUB_ENV
- uses: actions/cache@v3
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ matrix.os }}_${{ matrix.target }}_rustc-${{ steps.toolchain.outputs.cachekey }}_cargo-${{ hashFiles('**/Cargo.lock') }}
# Build and test. # Build and test.
- name: Build library - name: Build library
run: cargo rustc --lib --target ${{ matrix.target }} ${{ matrix.args }} --verbose run: cargo rustc --lib --target ${{ matrix.target }} ${{ matrix.args }} --verbose
- name: Test - name: Test
run: cargo test --target ${{ matrix.target }} ${{ matrix.args }} --all --verbose || echo "::warning ::Tests failed" continue-on-error: ${{ contains(matrix.target,'wasm32') }} # allow wasm builds to fail tests for now
run: cargo test --target ${{ matrix.target }} ${{ matrix.args }} --all --verbose
# On stable rust builds, build a binary and publish as a github actions # On stable rust builds, build a binary and publish as a github actions
# artifact. These binaries could be useful for testing the pipeline but # artifact. These binaries could be useful for testing the pipeline but
@@ -166,6 +130,53 @@ jobs:
fail_on: nothing fail_on: nothing
comment_mode: off comment_mode: off
report:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- name: Setup Rust
uses: ./.github/actions/setup-rust
with:
rust-version: nightly
targets: x86_64-unknown-linux-gnu
cache-context: report
- run: |
cargo install cargo2junit --force
# cargo install iai-callgrind-runner --force --version `cargo metadata --format-version 1 | jq -r '.resolve.nodes[].id|split(" ")|select(.[0]=="iai-callgrind")|.[1]'`
cargo install iai-callgrind-runner --force --git https://github.com/iai-callgrind/iai-callgrind --rev c77bc3c83d7f4e976cc42d4597236a8db259e772
sudo apt install valgrind -y
- name: Test and report
run: |
RUSTC_BOOTSTRAP=1 cargo test --all -- -Z unstable-options --format json --report-time | cargo2junit > cargo_test_results.xml
- name: Publish cargo test results artifact
uses: actions/upload-artifact@v3
with:
name: cargo-test-results
path: cargo_test_results.xml
- name: Publish cargo test summary
uses: EnricoMi/publish-unit-test-result-action/composite@master
with:
check_name: Cargo test summary
files: cargo_test_results.xml
fail_on: nothing
comment_mode: off
- run: cargo build --all-targets --release
- run: cargo test --bench setup --release
- run: cargo bench --bench run_iai -- --save-summary=json
- run: cargo bench --bench run_criterion
- run: cargo bench --bench run_criterion -- --profile-time 60
- name: Publish benchmark results
uses: actions/upload-artifact@v3
with:
name: benchmark-results
path: |
target/criterion/*
target/iai/*
target/benchmark_inference_counts.json
# Publish binaries when building for a tag # Publish binaries when building for a tag
release: release:
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04

541
Cargo.lock generated
View File

@@ -17,6 +17,19 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "ahash"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a"
dependencies = [
"cfg-if",
"getrandom",
"once_cell",
"version_check",
"zerocopy",
]
[[package]] [[package]]
name = "aho-corasick" name = "aho-corasick"
version = "1.0.2" version = "1.0.2"
@@ -41,12 +54,30 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "anes"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
[[package]]
name = "anstyle"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
[[package]] [[package]]
name = "arrayvec" name = "arrayvec"
version = "0.5.2" version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
[[package]]
name = "arrayvec"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
[[package]] [[package]]
name = "assert_cmd" name = "assert_cmd"
version = "1.0.8" version = "1.0.8"
@@ -100,6 +131,15 @@ version = "0.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2"
[[package]]
name = "bincode"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "bit-set" name = "bit-set"
version = "0.5.3" version = "0.5.3"
@@ -213,6 +253,12 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
[[package]]
name = "bytemuck"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6"
[[package]] [[package]]
name = "byteorder" name = "byteorder"
version = "1.4.3" version = "1.4.3"
@@ -225,6 +271,12 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
[[package]]
name = "cast"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.83" version = "1.0.83"
@@ -254,6 +306,58 @@ dependencies = [
"windows-targets", "windows-targets",
] ]
[[package]]
name = "ciborium"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926"
dependencies = [
"ciborium-io",
"ciborium-ll",
"serde",
]
[[package]]
name = "ciborium-io"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656"
[[package]]
name = "ciborium-ll"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b"
dependencies = [
"ciborium-io",
"half",
]
[[package]]
name = "clap"
version = "4.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b"
dependencies = [
"clap_builder",
]
[[package]]
name = "clap_builder"
version = "4.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663"
dependencies = [
"anstyle",
"clap_lex",
]
[[package]]
name = "clap_lex"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
[[package]] [[package]]
name = "clipboard-win" name = "clipboard-win"
version = "4.5.0" version = "4.5.0"
@@ -275,16 +379,6 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "console_log"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be8aed40e4edbf4d3b4431ab260b63fdc40f5780a4766824329ea0f1eefe3c0f"
dependencies = [
"log",
"web-sys",
]
[[package]] [[package]]
name = "core-foundation" name = "core-foundation"
version = "0.9.3" version = "0.9.3"
@@ -301,6 +395,15 @@ version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
[[package]]
name = "cpp_demangle"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e8227005286ec39567949b33df9896bcadfa6051bccca2488129f108ca23119"
dependencies = [
"cfg-if",
]
[[package]] [[package]]
name = "cpu-time" name = "cpu-time"
version = "1.0.0" version = "1.0.0"
@@ -320,6 +423,75 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "criterion"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f"
dependencies = [
"anes",
"cast",
"ciborium",
"clap",
"criterion-plot",
"is-terminal",
"itertools",
"num-traits",
"once_cell",
"oorandom",
"plotters",
"rayon",
"regex",
"serde",
"serde_derive",
"serde_json",
"tinytemplate",
"walkdir",
]
[[package]]
name = "criterion-plot"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
dependencies = [
"cast",
"itertools",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
dependencies = [
"cfg-if",
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
"memoffset",
"scopeguard",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
dependencies = [
"cfg-if",
]
[[package]] [[package]]
name = "crossterm" name = "crossterm"
version = "0.20.0" version = "0.20.0"
@@ -472,6 +644,15 @@ dependencies = [
"num-order", "num-order",
] ]
[[package]]
name = "debugid"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d"
dependencies = [
"uuid",
]
[[package]] [[package]]
name = "derive_deref" name = "derive_deref"
version = "1.1.1" version = "1.1.1"
@@ -563,6 +744,12 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d"
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]] [[package]]
name = "errno" name = "errno"
version = "0.3.3" version = "0.3.3"
@@ -611,6 +798,18 @@ dependencies = [
"windows-sys", "windows-sys",
] ]
[[package]]
name = "findshlibs"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40b9e59cd0f7e0806cca4be089683ecb6434e602038df21fe6bf6711b2f07f64"
dependencies = [
"cc",
"lazy_static",
"libc",
"winapi",
]
[[package]] [[package]]
name = "fnv" name = "fnv"
version = "1.0.7" version = "1.0.7"
@@ -827,13 +1026,19 @@ dependencies = [
"futures-sink", "futures-sink",
"futures-util", "futures-util",
"http", "http",
"indexmap", "indexmap 1.9.3",
"slab", "slab",
"tokio", "tokio",
"tokio-util", "tokio-util",
"tracing", "tracing",
] ]
[[package]]
name = "half"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.12.3" version = "0.12.3"
@@ -990,6 +1195,35 @@ dependencies = [
"tokio-native-tls", "tokio-native-tls",
] ]
[[package]]
name = "iai-callgrind"
version = "0.8.0"
source = "git+https://github.com/iai-callgrind/iai-callgrind.git?rev=c77bc3c83d7f4e976cc42d4597236a8db259e772#c77bc3c83d7f4e976cc42d4597236a8db259e772"
dependencies = [
"bincode",
"iai-callgrind-macros",
"iai-callgrind-runner",
]
[[package]]
name = "iai-callgrind-macros"
version = "0.1.0"
source = "git+https://github.com/iai-callgrind/iai-callgrind.git?rev=c77bc3c83d7f4e976cc42d4597236a8db259e772#c77bc3c83d7f4e976cc42d4597236a8db259e772"
dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"syn 2.0.37",
]
[[package]]
name = "iai-callgrind-runner"
version = "0.8.0"
source = "git+https://github.com/iai-callgrind/iai-callgrind.git?rev=c77bc3c83d7f4e976cc42d4597236a8db259e772#c77bc3c83d7f4e976cc42d4597236a8db259e772"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "iana-time-zone" name = "iana-time-zone"
version = "0.1.57" version = "0.1.57"
@@ -1033,6 +1267,34 @@ dependencies = [
"hashbrown 0.12.3", "hashbrown 0.12.3",
] ]
[[package]]
name = "indexmap"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad227c3af19d4914570ad36d30409928b75967c298feb9ea1969db3a610bb14e"
dependencies = [
"equivalent",
"hashbrown 0.14.0",
]
[[package]]
name = "inferno"
version = "0.11.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c50453ec3a6555fad17b1cd1a80d16af5bc7cb35094f64e429fd46549018c6a3"
dependencies = [
"ahash",
"indexmap 2.0.1",
"is-terminal",
"itoa",
"log",
"num-format",
"once_cell",
"quick-xml",
"rgb",
"str_stack",
]
[[package]] [[package]]
name = "instant" name = "instant"
version = "0.1.12" version = "0.1.12"
@@ -1048,6 +1310,17 @@ version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6"
[[package]]
name = "is-terminal"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
dependencies = [
"hermit-abi",
"rustix",
"windows-sys",
]
[[package]] [[package]]
name = "itertools" name = "itertools"
version = "0.10.5" version = "0.10.5"
@@ -1103,7 +1376,7 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe"
dependencies = [ dependencies = [
"arrayvec", "arrayvec 0.5.2",
"bitflags 1.3.2", "bitflags 1.3.2",
"cfg-if", "cfg-if",
"ryu", "ryu",
@@ -1147,9 +1420,9 @@ dependencies = [
[[package]] [[package]]
name = "linux-raw-sys" name = "linux-raw-sys"
version = "0.4.7" version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128" checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829"
[[package]] [[package]]
name = "lock_api" name = "lock_api"
@@ -1217,6 +1490,24 @@ version = "2.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c"
[[package]]
name = "memmap2"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43a5a03cefb0d953ec0be133036f14e109412fa594edc2f77227249db66cc3ed"
dependencies = [
"libc",
]
[[package]]
name = "memoffset"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
dependencies = [
"autocfg",
]
[[package]] [[package]]
name = "mime" name = "mime"
version = "0.3.17" version = "0.3.17"
@@ -1376,6 +1667,16 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "num-format"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3"
dependencies = [
"arrayvec 0.7.4",
"itoa",
]
[[package]] [[package]]
name = "num-modular" name = "num-modular"
version = "0.6.1" version = "0.6.1"
@@ -1393,9 +1694,9 @@ dependencies = [
[[package]] [[package]]
name = "num-traits" name = "num-traits"
version = "0.2.16" version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
dependencies = [ dependencies = [
"autocfg", "autocfg",
] ]
@@ -1425,6 +1726,12 @@ version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]]
name = "oorandom"
version = "11.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
[[package]] [[package]]
name = "opaque-debug" name = "opaque-debug"
version = "0.2.3" version = "0.2.3"
@@ -1433,9 +1740,9 @@ checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
[[package]] [[package]]
name = "openssl" name = "openssl"
version = "0.10.57" version = "0.10.60"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c" checksum = "79a4c6c3a2b158f7f8f2a2fc5a969fa3a068df6fc9dbb4a43845436e3af7c800"
dependencies = [ dependencies = [
"bitflags 2.4.0", "bitflags 2.4.0",
"cfg-if", "cfg-if",
@@ -1465,9 +1772,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]] [[package]]
name = "openssl-sys" name = "openssl-sys"
version = "0.9.93" version = "0.9.96"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d" checksum = "3812c071ba60da8b5677cc12bcb1d42989a65553772897a7e0355545a819838f"
dependencies = [ dependencies = [
"cc", "cc",
"libc", "libc",
@@ -1658,6 +1965,56 @@ version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
[[package]]
name = "plotters"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45"
dependencies = [
"num-traits",
"plotters-backend",
"plotters-svg",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "plotters-backend"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609"
[[package]]
name = "plotters-svg"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab"
dependencies = [
"plotters-backend",
]
[[package]]
name = "pprof"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef5c97c51bd34c7e742402e216abdeb44d415fbe6ae41d56b114723e953711cb"
dependencies = [
"backtrace",
"cfg-if",
"criterion",
"findshlibs",
"inferno",
"libc",
"log",
"nix 0.26.4",
"once_cell",
"parking_lot 0.12.1",
"smallvec",
"symbolic-demangle",
"tempfile",
"thiserror",
]
[[package]] [[package]]
name = "ppv-lite86" name = "ppv-lite86"
version = "0.2.17" version = "0.2.17"
@@ -1697,6 +2054,30 @@ dependencies = [
"termtree", "termtree",
] ]
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn 1.0.109",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
]
[[package]] [[package]]
name = "proc-macro-hack" name = "proc-macro-hack"
version = "0.5.20+deprecated" version = "0.5.20+deprecated"
@@ -1712,6 +2093,15 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "quick-xml"
version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f50b1c63b38611e7d4d7f68b82d3ad0cc71a2ad2e7f61fc10f1328d917c93cd"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.33" version = "1.0.33"
@@ -1767,6 +2157,26 @@ dependencies = [
"getrandom", "getrandom",
] ]
[[package]]
name = "rayon"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1"
dependencies = [
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed"
dependencies = [
"crossbeam-deque",
"crossbeam-utils",
]
[[package]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.2.16" version = "0.2.16"
@@ -1874,6 +2284,15 @@ dependencies = [
"winreg", "winreg",
] ]
[[package]]
name = "rgb"
version = "0.8.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05aaa8004b64fd573fc9d002f4e632d51ad4f026c2b5ba95fcb6c2f32c2c47d8"
dependencies = [
"bytemuck",
]
[[package]] [[package]]
name = "ring" name = "ring"
version = "0.16.20" version = "0.16.20"
@@ -1933,9 +2352,9 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "0.38.14" version = "0.38.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "747c788e9ce8e92b12cd485c49ddf90723550b654b32508f979b71a7b1ecda4f" checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3"
dependencies = [ dependencies = [
"bitflags 2.4.0", "bitflags 2.4.0",
"errno", "errno",
@@ -2042,8 +2461,8 @@ dependencies = [
"bytes", "bytes",
"chrono", "chrono",
"console_error_panic_hook", "console_error_panic_hook",
"console_log",
"cpu-time", "cpu-time",
"criterion",
"crossterm", "crossterm",
"crrl", "crrl",
"ctrlc", "ctrlc",
@@ -2056,7 +2475,9 @@ dependencies = [
"getrandom", "getrandom",
"git-version", "git-version",
"hostname", "hostname",
"indexmap", "iai-callgrind",
"indexmap 1.9.3",
"js-sys",
"lazy_static", "lazy_static",
"lexical", "lexical",
"libc", "libc",
@@ -2068,6 +2489,7 @@ dependencies = [
"num-order", "num-order",
"ordered-float", "ordered-float",
"phf 0.9.0", "phf 0.9.0",
"pprof",
"predicates-core", "predicates-core",
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -2351,6 +2773,12 @@ version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
[[package]]
name = "stable_deref_trait"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]] [[package]]
name = "static_assertions" name = "static_assertions"
version = "1.1.0" version = "1.1.0"
@@ -2363,6 +2791,12 @@ version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0" checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0"
[[package]]
name = "str_stack"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9091b6114800a5f2141aee1d1b9d6ca3592ac062dc5decb3764ec5895a47b4eb"
[[package]] [[package]]
name = "string_cache" name = "string_cache"
version = "0.8.7" version = "0.8.7"
@@ -2420,6 +2854,29 @@ version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
[[package]]
name = "symbolic-common"
version = "12.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "405af7bd5edd866cef462e22ef73f11cf9bf506c9d62824fef8364eb69d4d4ad"
dependencies = [
"debugid",
"memmap2",
"stable_deref_trait",
"uuid",
]
[[package]]
name = "symbolic-demangle"
version = "12.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bcd041ccfb77d9c70639efcd5b804b508ac7a273e9224d227379e225625daf9"
dependencies = [
"cpp_demangle",
"rustc-demangle",
"symbolic-common",
]
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.109" version = "1.0.109"
@@ -2498,6 +2955,16 @@ dependencies = [
"syn 2.0.37", "syn 2.0.37",
] ]
[[package]]
name = "tinytemplate"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
dependencies = [
"serde",
"serde_json",
]
[[package]] [[package]]
name = "tinyvec" name = "tinyvec"
version = "1.6.0" version = "1.6.0"
@@ -2751,6 +3218,12 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "uuid"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88ad59a7560b41a70d191093a945f0b87bc1deeda46fb237479708a1d6b6cdfc"
[[package]] [[package]]
name = "vcpkg" name = "vcpkg"
version = "0.2.15" version = "0.2.15"
@@ -3056,3 +3529,23 @@ name = "xmlparser"
version = "0.13.5" version = "0.13.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d25c75bf9ea12c4040a97f829154768bbbce366287e2dc044af160cd79a13fd" checksum = "4d25c75bf9ea12c4040a97f829154768bbbce366287e2dc044af160cd79a13fd"
[[package]]
name = "zerocopy"
version = "0.7.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cd369a67c0edfef15010f980c3cbe45d7f651deac2cd67ce097cd801de16557"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.7.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2f140bda219a26ccc0cdb03dba58af72590c53b22642577d88a927bc5c87d6b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.37",
]

View File

@@ -85,19 +85,21 @@ tokio = { version = "1.28.2", features = ["full"] }
[target.'cfg(target_arch = "wasm32")'.dependencies] [target.'cfg(target_arch = "wasm32")'.dependencies]
getrandom = { version = "0.2.10", features = ["js"] } getrandom = { version = "0.2.10", features = ["js"] }
tokio = { version = "1.28.2", features = ["sync", "macros", "io-util", "rt", "time"] } tokio = { version = "1.28.2", features = [
"sync",
"macros",
"io-util",
"rt",
"time",
] }
[target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies] [target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies]
console_error_panic_hook = "0.1" console_error_panic_hook = "0.1"
console_log = "1.0"
wasm-bindgen = "0.2.87" wasm-bindgen = "0.2.87"
wasm-bindgen-futures = "0.4" wasm-bindgen-futures = "0.4"
serde-wasm-bindgen = "0.5" serde-wasm-bindgen = "0.5"
web-sys = { version = "0.3", features = [ web-sys = { version = "0.3", features = ["Document", "Window", "Element", "Performance"] }
"Document", js-sys = "0.3"
"Window",
"Element",
]}
[target.'cfg(target_os = "wasi")'.dependencies] [target.'cfg(target_os = "wasi")'.dependencies]
ring-wasi = { version = "0.16.25" } ring-wasi = { version = "0.16.25" }
@@ -110,6 +112,27 @@ assert_cmd = "1.0.3"
predicates-core = "1.0.2" predicates-core = "1.0.2"
maplit = "1.0.2" maplit = "1.0.2"
serial_test = "2.0.0" serial_test = "2.0.0"
iai-callgrind = { git = "https://github.com/iai-callgrind/iai-callgrind.git", rev = "c77bc3c83d7f4e976cc42d4597236a8db259e772" }
criterion = "0.5.1"
[target.'cfg(not(target_os = "windows"))'.dev-dependencies]
pprof = { version = "0.13.0", features = ["criterion", "flamegraph"] }
[patch.crates-io] [patch.crates-io]
modular-bitfield = { git = "https://github.com/mthom/modular-bitfield" } modular-bitfield = { git = "https://github.com/mthom/modular-bitfield" }
[profile.bench]
lto = true
opt-level = 3
[profile.release]
lto = true
opt-level = 3
[[bench]]
name = "run_criterion"
harness = false
[[bench]]
name = "run_iai"
harness = false

View File

@@ -5,13 +5,6 @@
X = "Scryer Prolog!". X = "Scryer Prolog!".
``` ```
``` =html
<div style="border: solid #00007f 3px;padding-left: 15px;padding-right: 15px;font-style: italic;background-color: #00007f30;">
<h4>Scryer Prolog Meetup 2023</h4>
<p>The first annual Scryer Prolog meetup is going to happen in Düsseldorf (Germany) on the 9th and 10th of November 2023. Join us to discover the present and future of Scryer Prolog! Participation is free, registration not required. <a href="https://hsd-pbsa.de/veranstaltung/scryer-prolog-meetup-2023/">More details here.</a></p>
</div>
```
![scryer](scryer.png){width=128 style=float:right;} [Scryer Prolog](https://github.com/mthom/scryer-prolog) is a free software ISO Prolog system intended to be an industrial ![scryer](scryer.png){width=128 style=float:right;} [Scryer Prolog](https://github.com/mthom/scryer-prolog) is a free software ISO Prolog system intended to be an industrial
strength production environment *and* a testbed for bleeding edge research in strength production environment *and* a testbed for bleeding edge research in
logic and constraint programming. logic and constraint programming.
@@ -28,6 +21,7 @@ Some of the Scryer Prolog features are:
* [Cryptographical predicates](/crypto.html) * [Cryptographical predicates](/crypto.html)
* [Foreign Function Interface](/ffi.html) * [Foreign Function Interface](/ffi.html)
* WebAssembly support * WebAssembly support
* Usable as a library
* WAM based engine, cross-platform made in Rust * WAM based engine, cross-platform made in Rust
* _and more..._ * _and more..._
@@ -80,4 +74,4 @@ an [issue](https://github.com/mthom/scryer-prolog/issues).
To get in touch with the Scryer Prolog community, participate in To get in touch with the Scryer Prolog community, participate in
[discussions](https://github.com/mthom/scryer-prolog/discussions) [discussions](https://github.com/mthom/scryer-prolog/discussions)
or visit our #scryer IRC channel on [Libera](https://libera.chat)! or visit our #scryer IRC channel on [Libera](https://libera.chat)!

View File

@@ -6,7 +6,10 @@ source industrial strength production environment that is also a
testbed for bleeding edge research in logic and constraint testbed for bleeding edge research in logic and constraint
programming, which is itself written in a high-level language. programming, which is itself written in a high-level language.
As of July 2023, **Scryer Prolog passes all [syntactic conformity&nbsp;tests](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/conformity_testing)**. **Scryer Prolog passes all tests** of
[syntactic&nbsp;conformity](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/conformity_testing),
[`variable_names/1`](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/variable_names) and
[`dif/2`](https://www.complang.tuwien.ac.at/ulrich/iso-prolog/dif).
The homepage of the project is: [**https://www.scryer.pl**](https://www.scryer.pl) The homepage of the project is: [**https://www.scryer.pl**](https://www.scryer.pl)

95
benches/README.md Normal file
View File

@@ -0,0 +1,95 @@
# About benches
The `benches` directory contains benchmarks that test scryer-prolog performance.
Benchmarks are run via two harnesses:
* `criterion` - criterion performs statistical analysis of benchmark runs and is
great for benchmarking locally.
* `iai-callgrind` - this runs the benchmark with callgrind, which is able to
precisely track the number of instructions executed during the run. This is
especially helpful in a public CI runner context where neighboring VMs can
cause a very high wall time variance. While instructions executed is only
correlated with the desired metric (wall time), this is a good tradeoff for CI
where that metric is unreliable.
Run benchmarks with the following commands:
```
cargo bench --bench run_criterion
# run a particular criterion benchmark
cargo bench --bench run_criterion -- <benchmark_name>
# run in profiling mode which outputs flamegraphs. Set profile time in seconds:
cargo bench --bench run_criterion -- --profile-time <time>
# to run iai, you need valgrind installed and to install iai-callgrind-runner
# at the same version as is in Cargo.toml:
cargo install iai-callgrind-runner --version 0.7.3
cargo bench --bench run_iai
```
For consistency, both runners -- `run_iai.rs` and `run_criterion.rs` -- import
the same setup code from `setup.rs`.
## Setup
`setup.rs` contains the setup code to run benchmarks. `fn prolog_benches()` at
the top of the file is where the benchmarks are defined.
Benchmarks are organized around running queries against a prolog module file.
Before a benchmark starts, `benchmark.setup()` is called which reads the module
file and initializes a new `scryer_prolog::machine::Machine`.
Each benchmark measurement is done by running a query against the machine. In
the case of criterion each query is run many times, in the case of iai it's run
once.
## Adding benchmarks
This design is meant to suppoort defining lots of benchmarks.
To add a new benchmark:
* Add a new file `benches/[module].pl` that contains setup prolog code. Import
libraries, define predicates, etc.
* Add a new section in `setup.rs::prolog_benchmarks()` that refers to to the
file and write a query to be benchmarked.
* If the query mutates the machine, then use `Strategy::Fresh` so the criterion
benchmark will recreate a new machine for each benchmark run, otherwise use
`Strategy::Reuse` which has lower overhead. (This is not used by the iai
benchmark because it only runs once anyway.)
Some tips:
* The goal of benchmarking is to know if a library or engine change improved
performance or not.
* Once a benchmark is defined and named, avoid changing it's definition. If a
benchmark needs to change to be more useful, give the new definition a new
name instead. This will prevent charts from showing wild changes in
performance just because the definition changed (see previous).
* Aim for queries to execute in less than 0.5s realtime. Longer runtimes make it
easier for humans to see big differences, but benchmarks either run 10x slower
(iai) or execute repeatedly to attain statistical significance (criterion) and
in both cases benchmarking queries that take longer than about 0.5s are
cumbersome to run.
* Consider that the library runtime actually parses the text output of the top
level. So don't use custom outputs or it will fail to parse. Also keep the
output small so it doesn't just benchmark the ouput parsing code.
* DO test the output of the benchmark run, we don't want to count broken
benchmarks.
## CI
Both benchmark harnesses are run in `.github/workflows/ci.yaml` in the `report`
job, and the results are published as build artifacts.
## Todo
- [ ] Currently, the execution time to load a module is not benchmarked. It
would be nice to have at least one benchmark for loading a module (probably a
big one).
- [ ] Write a new action that downloads the test and benchmark results
artifacts, plots them over time, and publishes a report to github pages.

41
benches/csv.pl Normal file

File diff suppressed because one or more lines are too long

130
benches/edges.pl Normal file
View File

@@ -0,0 +1,130 @@
:- use_module(library(clpb)).
:- use_module(library(assoc)).
:- use_module(library(lists)).
:- use_module(library(pairs)).
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Contiguous United States and DC as they appear in SGB:
http://www-cs-faculty.stanford.edu/~uno/sgb.html
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
edge(al, fl).
edge(al, ga).
edge(al, ms).
edge(al, tn).
edge(ar, la).
edge(ar, mo).
edge(ar, ms).
edge(ar, ok).
edge(ar, tn).
edge(ar, tx).
edge(az, ca).
edge(az, nm).
edge(az, nv).
edge(az, ut).
edge(ca, nv).
edge(ca, or).
edge(co, ks).
edge(co, ne).
edge(co, nm).
edge(co, ok).
edge(co, ut).
edge(co, wy).
edge(ct, ma).
edge(ct, ny).
edge(ct, ri).
edge(dc, md).
edge(dc, va).
edge(de, md).
edge(de, nj).
edge(de, pa).
edge(fl, ga).
edge(ga, nc).
edge(ga, sc).
edge(ga, tn).
edge(ia, il).
edge(ia, mn).
edge(ia, mo).
edge(ia, ne).
edge(ia, sd).
edge(ia, wi).
edge(id, mt).
edge(id, nv).
edge(id, or).
edge(id, ut).
edge(id, wa).
edge(id, wy).
edge(il, in).
edge(il, ky).
edge(il, mo).
edge(il, wi).
edge(in, ky).
edge(in, mi).
edge(in, oh).
edge(ks, mo).
edge(ks, ne).
edge(ks, ok).
edge(ky, mo).
edge(ky, oh).
edge(ky, tn).
edge(ky, va).
edge(ky, wv).
edge(la, ms).
edge(la, tx).
edge(ma, nh).
edge(ma, ny).
edge(ma, ri).
edge(ma, vt).
edge(md, pa).
edge(md, va).
edge(md, wv).
edge(me, nh).
edge(mi, oh).
edge(mi, wi).
edge(mn, nd).
edge(mn, sd).
edge(mn, wi).
edge(mo, ne).
edge(mo, ok).
edge(mo, tn).
edge(ms, tn).
edge(mt, nd).
edge(mt, sd).
edge(mt, wy).
edge(nc, sc).
edge(nc, tn).
edge(nc, va).
edge(nd, sd).
edge(ne, sd).
edge(ne, wy).
edge(nh, vt).
edge(nj, ny).
edge(nj, pa).
edge(nm, ok).
edge(nm, tx).
edge(nv, or).
edge(nv, ut).
edge(ny, pa).
edge(ny, vt).
edge(oh, pa).
edge(oh, wv).
edge(ok, tx).
edge(or, wa).
edge(pa, wv).
edge(sd, wy).
edge(tn, va).
edge(ut, wy).
edge(va, wv).
independent_set(G, *(NBs)) :-
findall(U-V, (edge(U, V),G@<U), Edges),
setof(U, V^(member(U-V, Edges);member(V-U, Edges)), Nodes),
pairs_keys_values(Pairs, Nodes, _),
list_to_assoc(Pairs, Assoc),
maplist(not_both(Assoc), Edges, NBs).
not_both(Assoc, U-V, ~BU + ~BV) :-
get_assoc(U, Assoc, BU),
get_assoc(V, Assoc, BV).
independent_set_count(G, Count) :- independent_set(G, Sat), sat_count(Sat, Count).

2
benches/numlist.pl Normal file
View File

@@ -0,0 +1,2 @@
:- use_module(library(between)).
run_numlist(Upper, Head) :- numlist(1, Upper, L), L = [Head|_].

36
benches/run_criterion.rs Normal file
View File

@@ -0,0 +1,36 @@
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
#[cfg(not(target_os = "windows"))]
use pprof::criterion::{Output, PProfProfiler};
mod setup;
fn bench_criterion(c: &mut Criterion) {
for (&name, bench) in setup::prolog_benches().iter() {
match bench.strategy {
setup::Strategy::Fresh => c.bench_function(name, |b| {
b.iter_batched(|| bench.setup(), |mut r| r(), BatchSize::LargeInput)
}),
setup::Strategy::Reuse => c.bench_function(name, |b| b.iter(bench.setup())),
};
}
}
#[cfg(not(target_os = "windows"))]
fn config() -> Criterion {
Criterion::default()
.sample_size(20)
.with_profiler(PProfProfiler::new(100, Output::Flamegraph(None)))
}
#[cfg(target_os = "windows")]
fn config() -> Criterion {
Criterion::default().sample_size(20)
}
criterion_group!(
name = benches;
config = config();
targets = bench_criterion
);
criterion_main!(benches);

18
benches/run_iai.rs Normal file
View File

@@ -0,0 +1,18 @@
use iai_callgrind::{library_benchmark, library_benchmark_group, main};
use scryer_prolog::machine::parsed_results::QueryResolution;
mod setup;
#[library_benchmark]
#[bench::count_edges(setup::prolog_benches()["count_edges"].setup())]
#[bench::numlist(setup::prolog_benches()["numlist"].setup())]
#[bench::csv_codename(setup::prolog_benches()["csv_codename"].setup())]
fn bench(mut run: impl FnMut() -> QueryResolution) -> QueryResolution {
run()
}
library_benchmark_group!(
name = benches;
benchmarks = bench
);
main!(library_benchmark_groups = benches);

132
benches/setup.rs Normal file
View File

@@ -0,0 +1,132 @@
use std::{collections::BTreeMap, fs, path::Path};
use maplit::btreemap;
use scryer_prolog::machine::{
parsed_results::{QueryResolution, Value},
Machine,
};
pub fn prolog_benches() -> BTreeMap<&'static str, PrologBenchmark> {
[
(
"count_edges", // name of the benchmark
"benches/edges.pl", // name of the prolog module file to load. use the same file in multiple benchmarks
"independent_set_count(ky, Count).", // query to benchmark in the context of the loaded module. consider making the query adjustable to tune the run time to ~0.1s
Strategy::Reuse,
btreemap! { "Count" => Value::try_from("2869176".to_string()).unwrap() },
),
(
"numlist",
"benches/numlist.pl",
"run_numlist(1000000, Head).",
Strategy::Reuse,
btreemap! { "Head" => Value::try_from("1".to_string()).unwrap()},
),
(
"csv_codename",
"benches/csv.pl",
"get_codename(\"0020\",Name).",
Strategy::Reuse,
btreemap! { "Name" => Value::try_from("SPACE".to_string()).unwrap()},
),
]
.map(|b| {
(
b.0,
PrologBenchmark {
name: b.0,
filename: b.1,
query: b.2,
strategy: b.3,
bindings: b.4,
},
)
})
.into()
}
pub enum Strategy {
#[allow(dead_code)]
Fresh,
Reuse,
}
pub struct PrologBenchmark {
pub name: &'static str,
pub filename: &'static str,
pub query: &'static str,
pub strategy: Strategy,
pub bindings: BTreeMap<&'static str, Value>,
}
impl PrologBenchmark {
pub fn make_machine(&self) -> Machine {
let program = fs::read_to_string(self.filename).unwrap();
let module_name = Path::new(self.filename)
.file_stem()
.and_then(|s| s.to_str())
.unwrap();
let mut machine = Machine::new_lib();
machine.load_module_string(module_name, program);
machine
}
pub fn setup(&self) -> impl FnMut() -> QueryResolution {
let mut machine = self.make_machine();
let query = self.query;
move || {
use criterion::black_box;
black_box(machine.run_query(black_box(query.to_string()))).unwrap()
}
}
}
#[cfg(test)]
mod test {
#[test]
fn validate_benchmarks() {
use super::prolog_benches;
use scryer_prolog::machine::parsed_results::{QueryMatch, QueryResolution};
use std::{fmt::Write, fs};
struct BenchResult {
pub name: &'static str,
pub setup_inference_count: u64,
pub query_inference_count: u64,
}
let mut results: Vec<BenchResult> = vec![];
for (_, r) in prolog_benches() {
let mut machine = r.make_machine();
let setup_inference_count = machine.get_inference_count();
let result = machine.run_query(r.query.to_string()).unwrap();
let query_inference_count = machine.get_inference_count() - setup_inference_count;
let expected = QueryResolution::Matches(vec![QueryMatch::from(r.bindings.clone())]);
assert_eq!(result, expected, "validating benchmark {}", r.name);
results.push(BenchResult {
name: r.name,
setup_inference_count,
query_inference_count,
})
}
let mut json: String = Default::default();
json.push('[');
for r in results {
json.push('\n');
write!(
json,
r#"{{"name":"{}","setup_inference_count":{},"query_inference_count":{}}},"#,
r.name, r.setup_inference_count, r.query_inference_count
)
.unwrap();
}
json.pop(); // trailing comma
json.push_str("\n]");
fs::write("target/benchmark_inference_counts.json", json).expect("Unable to write file");
}
}

View File

@@ -329,6 +329,8 @@ enum SystemClauseType {
InstallSCCCleaner, InstallSCCCleaner,
#[strum_discriminants(strum(props(Arity = "3", Name = "$install_inference_counter")))] #[strum_discriminants(strum(props(Arity = "3", Name = "$install_inference_counter")))]
InstallInferenceCounter, InstallInferenceCounter,
#[strum_discriminants(strum(props(Arity = "1", Name = "$inference_count")))]
InferenceCount,
#[strum_discriminants(strum(props(Arity = "1", Name = "$lh_length")))] #[strum_discriminants(strum(props(Arity = "1", Name = "$lh_length")))]
LiftedHeapLength, LiftedHeapLength,
#[strum_discriminants(strum(props(Arity = "3", Name = "$load_library_as_stream")))] #[strum_discriminants(strum(props(Arity = "3", Name = "$load_library_as_stream")))]
@@ -573,6 +575,8 @@ enum SystemClauseType {
ForeignCall, ForeignCall,
#[strum_discriminants(strum(props(Arity = "2", Name = "$define_foreign_struct")))] #[strum_discriminants(strum(props(Arity = "2", Name = "$define_foreign_struct")))]
DefineForeignStruct, DefineForeignStruct,
#[strum_discriminants(strum(props(Arity = "2", Name = "$js_eval")))]
JsEval,
#[strum_discriminants(strum(props(Arity = "3", Name = "$predicate_defined")))] #[strum_discriminants(strum(props(Arity = "3", Name = "$predicate_defined")))]
PredicateDefined, PredicateDefined,
#[strum_discriminants(strum(props(Arity = "3", Name = "$strip_module")))] #[strum_discriminants(strum(props(Arity = "3", Name = "$strip_module")))]
@@ -1720,6 +1724,7 @@ fn generate_instruction_preface() -> TokenStream {
&Instruction::CallHeadIsDynamic | &Instruction::CallHeadIsDynamic |
&Instruction::CallInstallSCCCleaner | &Instruction::CallInstallSCCCleaner |
&Instruction::CallInstallInferenceCounter | &Instruction::CallInstallInferenceCounter |
&Instruction::CallInferenceCount |
&Instruction::CallLiftedHeapLength | &Instruction::CallLiftedHeapLength |
&Instruction::CallLoadLibraryAsStream | &Instruction::CallLoadLibraryAsStream |
&Instruction::CallModuleExists | &Instruction::CallModuleExists |
@@ -1774,6 +1779,7 @@ fn generate_instruction_preface() -> TokenStream {
&Instruction::CallLoadForeignLib | &Instruction::CallLoadForeignLib |
&Instruction::CallForeignCall | &Instruction::CallForeignCall |
&Instruction::CallDefineForeignStruct | &Instruction::CallDefineForeignStruct |
&Instruction::CallJsEval |
&Instruction::CallPredicateDefined | &Instruction::CallPredicateDefined |
&Instruction::CallStripModule | &Instruction::CallStripModule |
&Instruction::CallCurrentTime | &Instruction::CallCurrentTime |
@@ -1954,6 +1960,7 @@ fn generate_instruction_preface() -> TokenStream {
&Instruction::ExecuteHeadIsDynamic | &Instruction::ExecuteHeadIsDynamic |
&Instruction::ExecuteInstallSCCCleaner | &Instruction::ExecuteInstallSCCCleaner |
&Instruction::ExecuteInstallInferenceCounter | &Instruction::ExecuteInstallInferenceCounter |
&Instruction::ExecuteInferenceCount |
&Instruction::ExecuteLiftedHeapLength | &Instruction::ExecuteLiftedHeapLength |
&Instruction::ExecuteLoadLibraryAsStream | &Instruction::ExecuteLoadLibraryAsStream |
&Instruction::ExecuteModuleExists | &Instruction::ExecuteModuleExists |
@@ -2008,6 +2015,7 @@ fn generate_instruction_preface() -> TokenStream {
&Instruction::ExecuteLoadForeignLib | &Instruction::ExecuteLoadForeignLib |
&Instruction::ExecuteForeignCall | &Instruction::ExecuteForeignCall |
&Instruction::ExecuteDefineForeignStruct | &Instruction::ExecuteDefineForeignStruct |
&Instruction::ExecuteJsEval |
&Instruction::ExecutePredicateDefined | &Instruction::ExecutePredicateDefined |
&Instruction::ExecuteStripModule | &Instruction::ExecuteStripModule |
&Instruction::ExecuteCurrentTime | &Instruction::ExecuteCurrentTime |

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 4.8 MiB

View File

View File

@@ -29,7 +29,7 @@ pub(crate) trait Allocator {
&mut self, &mut self,
var_num: usize, var_num: usize,
lvl: Level, lvl: Level,
cell: &'a Cell<VarReg>, cell: &Cell<VarReg>,
term_loc: GenContext, term_loc: GenContext,
code: &mut CodeDeque, code: &mut CodeDeque,
r: RegType, r: RegType,
@@ -42,7 +42,7 @@ pub(crate) trait Allocator {
&mut self, &mut self,
var_num: usize, var_num: usize,
lvl: Level, lvl: Level,
cell: &'a Cell<VarReg>, cell: &Cell<VarReg>,
context: GenContext, context: GenContext,
code: &mut CodeDeque, code: &mut CodeDeque,
); );

View File

@@ -10,6 +10,7 @@ use crate::parser::ast::*;
use crate::targets::*; use crate::targets::*;
use crate::temp_v; use crate::temp_v;
use crate::types::*; use crate::types::*;
use crate::variable_records::*;
use crate::instr; use crate::instr;
use crate::machine::disjuncts::*; use crate::machine::disjuncts::*;
@@ -60,6 +61,7 @@ impl BranchCodeStack {
marker: &mut DebrayAllocator, marker: &mut DebrayAllocator,
) -> SubsumedBranchHits { ) -> SubsumedBranchHits {
let mut subsumed_hits = SubsumedBranchHits::with_hasher(FxBuildHasher::default()); let mut subsumed_hits = SubsumedBranchHits::with_hasher(FxBuildHasher::default());
let mut propagated_var_nums = IndexSet::with_hasher(FxBuildHasher::default());
for idx in (self.stack.len() - depth..self.stack.len()).rev() { for idx in (self.stack.len() - depth..self.stack.len()).rev() {
let branch = &mut marker.branch_stack[idx]; let branch = &mut marker.branch_stack[idx];
@@ -85,9 +87,17 @@ impl BranchCodeStack {
} }
} }
if idx > self.stack.len() - depth {
propagated_var_nums.insert(var_num);
}
subsumed_hits.insert(var_num); subsumed_hits.insert(var_num);
} }
} }
for var_num in propagated_var_nums.drain(..) {
marker.branch_stack[idx - 1].add_branch_occurrence(var_num);
}
} }
subsumed_hits subsumed_hits
@@ -277,7 +287,6 @@ impl DebrayAllocator {
code: &mut CodeDeque, code: &mut CodeDeque,
) -> RegType { ) -> RegType {
self.mark_var::<QueryInstruction>(var_num, Level::Shallow, vr, term_loc, code); self.mark_var::<QueryInstruction>(var_num, Level::Shallow, vr, term_loc, code);
vr.get().norm() vr.get().norm()
} }
@@ -296,7 +305,14 @@ impl DebrayAllocator {
self.mark_var_in_non_callable(var_num, term_loc, vr, code); self.mark_var_in_non_callable(var_num, term_loc, vr, code);
temp_v!(arg) temp_v!(arg)
} else { } else {
self.increment_running_count(var_num); if let VarAlloc::Perm(_, PermVarAllocation::Pending) =
&self.var_data.records[var_num].allocation
{
self.mark_var_in_non_callable(var_num, term_loc, vr, code);
} else {
self.increment_running_count(var_num);
}
RegType::Perm(p) RegType::Perm(p)
} }
} }

View File

@@ -39,6 +39,19 @@ impl BranchOccurrences {
subsumed_hits: SubsumedBranchHits::with_hasher(FxBuildHasher::default()), subsumed_hits: SubsumedBranchHits::with_hasher(FxBuildHasher::default()),
} }
} }
pub(crate) fn add_branch_occurrence(&mut self, var_num: usize) {
debug_assert!(self.current_branch < self.num_branches);
let num_branches = self.num_branches;
let entry = self
.hits
.entry(var_num)
.or_insert_with(|| BitVec::repeat(false, num_branches));
entry.set(self.current_branch, true);
self.subsumed_hits.insert(var_num);
}
} }
#[derive(Debug)] #[derive(Debug)]
@@ -92,17 +105,7 @@ impl BranchStack {
pub(crate) fn add_branch_occurrence(&mut self, var_num: usize) { pub(crate) fn add_branch_occurrence(&mut self, var_num: usize) {
if let Some(occurrences) = self.last_mut() { if let Some(occurrences) = self.last_mut() {
debug_assert!(occurrences.current_branch < occurrences.num_branches); occurrences.add_branch_occurrence(var_num);
let num_branches = occurrences.num_branches;
let entry = occurrences
.hits
.entry(var_num)
.or_insert_with(|| BitVec::repeat(false, num_branches));
entry.set(occurrences.current_branch, true);
occurrences.subsumed_hits.insert(var_num);
} }
} }
@@ -166,30 +169,26 @@ impl DebrayAllocator {
for var_num in subsumed_hits { for var_num in subsumed_hits {
match &mut self.var_data.records[var_num].allocation { match &mut self.var_data.records[var_num].allocation {
VarAlloc::Perm(_, ref mut allocation) => { VarAlloc::Perm(_, ref mut allocation) => {
match allocation { if let PermVarAllocation::Done {
PermVarAllocation::Done { shallow_safety,
shallow_safety, deep_safety,
deep_safety, ..
.. } = allocation
} => { {
if !self if !self
.branch_stack .branch_stack
.safety_unneeded_in_branch(shallow_safety, &branch_designator) .safety_unneeded_in_branch(shallow_safety, &branch_designator)
{ {
let branch_occurrences = self.branch_stack.last_mut().unwrap(); let branch_occurrences = self.branch_stack.last_mut().unwrap();
branch_occurrences.shallow_safety.insert(var_num); branch_occurrences.shallow_safety.insert(var_num);
}
if !self
.branch_stack
.safety_unneeded_in_branch(deep_safety, &branch_designator)
{
let branch_occurrences = self.branch_stack.last_mut().unwrap();
branch_occurrences.deep_safety.insert(var_num);
}
} }
_ => {
unreachable!(); if !self
.branch_stack
.safety_unneeded_in_branch(deep_safety, &branch_designator)
{
let branch_occurrences = self.branch_stack.last_mut().unwrap();
branch_occurrences.deep_safety.insert(var_num);
} }
} }
@@ -740,7 +739,7 @@ impl Allocator for DebrayAllocator {
&mut self, &mut self,
var_num: usize, var_num: usize,
lvl: Level, lvl: Level,
cell: &'a Cell<VarReg>, cell: &Cell<VarReg>,
term_loc: GenContext, term_loc: GenContext,
code: &mut CodeDeque, code: &mut CodeDeque,
) { ) {
@@ -748,11 +747,11 @@ impl Allocator for DebrayAllocator {
RegType::Temp(0) => { RegType::Temp(0) => {
let o = self.alloc_reg_to_var::<Target>(var_num, lvl, term_loc, code); let o = self.alloc_reg_to_var::<Target>(var_num, lvl, term_loc, code);
cell.set(VarReg::Norm(RegType::Temp(o))); cell.set(VarReg::Norm(RegType::Temp(o)));
(RegType::Temp(o), true) (RegType::Temp(o), true)
} }
RegType::Perm(0) => { RegType::Perm(0) => {
let p = self.alloc_perm_var(var_num, term_loc.chunk_num()); let p = self.alloc_perm_var(var_num, term_loc.chunk_num());
cell.set(VarReg::Norm(RegType::Perm(p)));
(RegType::Perm(p), true) (RegType::Perm(p), true)
} }
r @ RegType::Perm(_) => { r @ RegType::Perm(_) => {
@@ -780,7 +779,7 @@ impl Allocator for DebrayAllocator {
&mut self, &mut self,
var_num: usize, var_num: usize,
lvl: Level, lvl: Level,
cell: &'a Cell<VarReg>, cell: &Cell<VarReg>,
term_loc: GenContext, term_loc: GenContext,
code: &mut CodeDeque, code: &mut CodeDeque,
r: RegType, r: RegType,
@@ -846,8 +845,21 @@ impl Allocator for DebrayAllocator {
fn mark_cut_var(&mut self, var_num: usize, chunk_num: usize) -> RegType { fn mark_cut_var(&mut self, var_num: usize, chunk_num: usize) -> RegType {
match self.get_binding(var_num) { match self.get_binding(var_num) {
RegType::Perm(0) | RegType::Temp(0) => { RegType::Perm(0) => RegType::Perm(self.alloc_perm_var(var_num, chunk_num)),
RegType::Perm(self.alloc_perm_var(var_num, chunk_num)) RegType::Temp(0) => {
let t = self.alloc_reg_to_non_var();
match &mut self.var_data.records[var_num].allocation {
VarAlloc::Temp {
temp_reg, safety, ..
} => {
*temp_reg = t;
*safety = VarSafetyStatus::GloballyUnneeded;
}
_ => unreachable!(),
};
RegType::Temp(t)
} }
r => r, r => r,
} }

View File

@@ -744,7 +744,11 @@ impl Number {
Number::Float(f) => Number::Float(OrderedFloat(f.signum())), Number::Float(f) => Number::Float(OrderedFloat(f.signum())),
_ => { _ => {
if self.is_positive() { if self.is_positive() {
Number::Fixnum(Fixnum::build_with(1)) if self.is_zero() {
Number::Fixnum(Fixnum::build_with(0))
} else {
Number::Fixnum(Fixnum::build_with(1))
}
} else if self.is_negative() { } else if self.is_negative() {
Number::Fixnum(Fixnum::build_with(-1)) Number::Fixnum(Fixnum::build_with(-1))
} else { } else {
@@ -876,7 +880,7 @@ impl ClauseIndexInfo {
} }
} }
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug, Default)]
pub(crate) struct PredicateInfo { pub(crate) struct PredicateInfo {
pub(crate) is_extensible: bool, pub(crate) is_extensible: bool,
pub(crate) is_discontiguous: bool, pub(crate) is_discontiguous: bool,
@@ -885,19 +889,6 @@ pub(crate) struct PredicateInfo {
pub(crate) has_clauses: bool, pub(crate) has_clauses: bool,
} }
impl Default for PredicateInfo {
#[inline]
fn default() -> Self {
PredicateInfo {
is_extensible: false,
is_discontiguous: false,
is_dynamic: false,
is_multifile: false,
has_clauses: false,
}
}
}
impl PredicateInfo { impl PredicateInfo {
#[inline] #[inline]
pub(crate) fn compile_incrementally(&self) -> bool { pub(crate) fn compile_incrementally(&self) -> bool {

View File

@@ -1762,7 +1762,7 @@ impl<'a, Outputter: HCValueOutputter> HCPrinter<'a, Outputter> {
while let Some(loc_data) = self.state_stack.pop() { while let Some(loc_data) = self.state_stack.pop() {
match loc_data { match loc_data {
TokenOrRedirect::Atom(atom) => self.print_impromptu_atom(atom), TokenOrRedirect::Atom(atom) => self.print_impromptu_atom(atom),
TokenOrRedirect::BarAsOp => append_str!(self, " | "), TokenOrRedirect::BarAsOp => append_str!(self, "|"),
TokenOrRedirect::Char(c) => print_char!(self, self.quoted, c), TokenOrRedirect::Char(c) => print_char!(self, self.quoted, c),
TokenOrRedirect::Op(atom, op) => { TokenOrRedirect::Op(atom, op) => {
self.print_op(&atom.as_str()); self.print_op(&atom.as_str());

View File

@@ -51,7 +51,8 @@ use wasm_bindgen::prelude::*;
#[wasm_bindgen] #[wasm_bindgen]
pub fn eval_code(s: &str) -> String { pub fn eval_code(s: &str) -> String {
use machine::mock_wam::*; use machine::mock_wam::*;
use web_sys::console;
console_error_panic_hook::set_once();
let mut wam = Machine::with_test_streams(); let mut wam = Machine::with_test_streams();
let bytes = wam.test_load_string(s); let bytes = wam.test_load_string(s);

View File

@@ -1173,8 +1173,13 @@ clause(H, B) :-
% The clause will be inserted at the beginning of the module. % The clause will be inserted at the beginning of the module.
asserta(Clause0) :- asserta(Clause0) :-
loader:strip_subst_module(Clause0, user, Module, Clause), loader:strip_subst_module(Clause0, user, Module, Clause),
iso_ext:asserta(Module, Clause). asserta_(Module, Clause).
asserta_(Module, (Head :- Body)) :-
!,
'$asserta'(Module, Head, Body).
asserta_(Module, Fact) :-
'$asserta'(Module, Fact, true).
:- meta_predicate assertz(:). :- meta_predicate assertz(:).
@@ -1184,7 +1189,13 @@ asserta(Clause0) :-
% The clase will be inserted at the end of the module. % The clase will be inserted at the end of the module.
assertz(Clause0) :- assertz(Clause0) :-
loader:strip_subst_module(Clause0, user, Module, Clause), loader:strip_subst_module(Clause0, user, Module, Clause),
iso_ext:assertz(Module, Clause). assertz_(Module, Clause).
assertz_(Module, (Head :- Body)) :-
!,
'$assertz'(Module, Head, Body).
assertz_(Module, Fact) :-
'$assertz'(Module, Fact, true).
:- meta_predicate retract(:). :- meta_predicate retract(:).
@@ -1203,6 +1214,9 @@ retract(Clause0) :-
Body = true, Body = true,
retract_module_clause(Head, Body, Module) retract_module_clause(Head, Body, Module)
; Clause = (Head :- Body) -> ; Clause = (Head :- Body) ->
( var(Module) -> Module = user
; true
),
retract_module_clause(Head, Body, Module) retract_module_clause(Head, Body, Module)
). ).

View File

@@ -7968,11 +7968,11 @@ coeff_var_term(C-V, T) :- ( C =:= 1 -> T = #V ; T = C * #V ).
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#=(X, Y, T) :- #=(X, Y, T) :-
X #= Y #<==> B, X #= Y #<==> #B,
zo_t(B, T). zo_t(B, T).
#<(X, Y, T) :- #<(X, Y, T) :-
X #< Y #<==> B, X #< Y #<==> #B,
zo_t(B, T). zo_t(B, T).
zo_t(0, false). zo_t(0, false).

View File

@@ -33,11 +33,13 @@ remove_goal([G0|G0s], Goal0, Goals) :-
vars_remove_goal([], _). vars_remove_goal([], _).
vars_remove_goal([Var|Vars], Goal0) :- vars_remove_goal([Var|Vars], Goal0) :-
get_atts(Var, +dif(Goals0)), ( get_atts(Var, +dif(Goals0)) ->
remove_goal(Goals0, Goal0, Goals), remove_goal(Goals0, Goal0, Goals),
( Goals = [] -> ( Goals = [] ->
put_atts(Var, -dif(_)) put_atts(Var, -dif(_))
; put_atts(Var, +dif(Goals)) ; put_atts(Var, +dif(Goals))
)
; true
), ),
vars_remove_goal(Vars, Goal0). vars_remove_goal(Vars, Goal0).

View File

@@ -17,9 +17,7 @@ but they're not part of the ISO Prolog standard at the moment.
succ/2, succ/2,
call_nth/2, call_nth/2,
countall/2, countall/2,
copy_term_nat/2, copy_term_nat/2]).
asserta/2,
assertz/2]).
:- use_module(library(error), [can_be/2, :- use_module(library(error), [can_be/2,
domain_error/3, domain_error/3,
@@ -384,21 +382,3 @@ countall(Goal, N) :-
copy_term_nat(Source, Dest) :- copy_term_nat(Source, Dest) :-
'$copy_term_without_attr_vars'(Source, Dest). '$copy_term_without_attr_vars'(Source, Dest).
%% asserta(Module, Rule_Fact).
%
% Similar to `asserta/1` but allows specifying a Module
asserta(Module, (Head :- Body)) :-
!,
'$asserta'(Module, Head, Body).
asserta(Module, Fact) :-
'$asserta'(Module, Fact, true).
%% assertz(Module, Rule_Fact).
%
% Similar to `assertz/1` but allows specifying a Module
assertz(Module, (Head :- Body)) :-
!,
'$assertz'(Module, Head, Body).
assertz(Module, Fact) :-
'$assertz'(Module, Fact, true).

View File

@@ -37,6 +37,7 @@
atomic_si/1, atomic_si/1,
list_si/1, list_si/1,
character_si/1, character_si/1,
term_si/1,
chars_si/1, chars_si/1,
dif_si/2]). dif_si/2]).
@@ -68,6 +69,11 @@ character_si(Ch) :-
atom(Ch), atom(Ch),
atom_length(Ch,1). atom_length(Ch,1).
term_si(Term) :-
( ground(Term) -> acyclic_term(Term)
; throw(error(instantiation_error, term_si/1))
).
chars_si(Chs0) :- chars_si(Chs0) :-
'$skip_max_list'(_,_, Chs0,Chs), '$skip_max_list'(_,_, Chs0,Chs),
( nonvar(Chs) -> Chs == [] ; true ), % fails for infinite lists too ( nonvar(Chs) -> Chs == [] ; true ), % fails for infinite lists too

View File

@@ -96,7 +96,7 @@ sleep(T) :-
:- meta_predicate time(0). :- meta_predicate time(0).
:- dynamic(time_id/1). :- dynamic(time_id/1).
:- dynamic(time_state/2). :- dynamic(time_state/3).
time_next_id(N) :- time_next_id(N) :-
( retract(time_id(N0)) -> ( retract(time_id(N0)) ->
@@ -111,9 +111,9 @@ time_next_id(N) :-
% Reports the execution time of Goal. % Reports the execution time of Goal.
time(Goal) :- time(Goal) :-
'$cpu_now'(T0), cputime_inferences(T0, I0),
time_next_id(ID), time_next_id(ID),
setup_call_cleanup(asserta(time_state(ID, T0)), setup_call_cleanup(asserta(time_state(ID, T0, I0)),
( call_cleanup(catch(Goal, E, (report_time(ID),throw(E))), ( call_cleanup(catch(Goal, E, (report_time(ID),throw(E))),
Det = true), Det = true),
time_true(ID), time_true(ID),
@@ -123,49 +123,72 @@ time(Goal) :-
; report_time(ID), ; report_time(ID),
false false
), ),
retract(time_state(ID, _))). retract(time_state(ID, _, _))).
cputime_inferences(T, I) :-
'$cpu_now'(T),
'$inference_count'(I).
time_true(ID) :- time_true(ID) :-
report_time(ID). report_time(ID).
time_true(ID) :- time_true(ID) :-
% on backtracking, update the stored CPU time for this ID % on backtracking, update the stored CPU time for this ID
retract(time_state(ID, _)), retract(time_state(ID, _, _)),
'$cpu_now'(T0), cputime_inferences(T0, I0),
asserta(time_state(ID, T0)), asserta(time_state(ID, T0, I0)),
false. false.
report_time(ID) :- report_time(ID) :-
time_state(ID, T0), time_state(ID, T0, I0),
'$cpu_now'(T), cputime_inferences(T, I),
Time is T - T0, Time is T - T0,
Inferences0 is I - I0,
% we must subtract the number of inferences that time/1 itself takes;
% this may have to be adapted if the implementation changes,
% so that (for example) true/1 takes exactly 1 inference.
( bb_get('$answer_count', 0) -> ( bb_get('$answer_count', 0) ->
Inferences is Inferences0 - 60,
Pre = " ", Post = "" Pre = " ", Post = ""
; Pre = "", Post = " " ; Inferences is Inferences0 - 9,
Pre = "", Post = " "
), ),
format("~s% CPU time: ~3fs~n~s", [Pre,Time,Post]). phrase((Pre,"% CPU time: ", format_("~3f", [Time]), "s, ",
format_("~U", [Inferences])," inference",s_if_necessary(Inferences),"\n",
Post), Cs),
format("~s", [Cs]).
s_if_necessary(Inferences) -->
{ compare(C, 1, Inferences) },
s_(C).
s_(=) --> "".
s_(<) --> "s".
s_(>) --> " (exception?)".
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
?- time((true;false)). ?- time((true;false)).
%@ % CPU time: 0.006s % CPU time: 0.000s, 1 inference
%@ true true
%@ ; % CPU time: 0.001s ; % CPU time: 0.000s, 0 inference (exception?)
%@ false. false.
:- time(use_module(library(clpz))). :- time(use_module(library(clpz))).
%@ % CPU time: 3.711s % CPU time: 0.343s, 409_874 inferences
%@ true. true.
:- time(use_module(library(lists))). :- time(use_module(library(lists))).
%@ % CPU time: 0.006s % CPU time: 0.000s, 19 inferences
%@ true. true.
?- time(member(X, "abc")). ?- time(member(X, "abc")).
%@ % CPU time: 0.005s % CPU time: 0.000s, 1 inference
%@ X = a X = a
%@ ; % CPU time: 0.000s ; % CPU time: 0.000s, 3 inferences
%@ X = b X = b
%@ ; % CPU time: 0.000s ; % CPU time: 0.000s, 3 inferences
%@ X = c X = c.
%@ ; % CPU time: 0.000s
%@ false. ?- time((repeat,false)).
% CPU time: 2.726s, 53_330_502 inferences
error('$interrupt_thrown',repl/0).
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

29
src/lib/wasm.pl Normal file
View File

@@ -0,0 +1,29 @@
/** Predicates for the WebAssembly platform
This module contains predicates that are only available in
the WASM (WebAssembly) version of Scryer Prolog.
*/
:- module(wasm, [js_eval/2]).
:- use_module(library(error)).
%% js_eval(+JsCode, -Result).
%
% Executes a JavaScript snippet `JsCode` using the platform
% `eval` function. `Result` takes the return value of that code.
% Strings, booleans, numbers, null and undefined are directly mapped to Prolog.
% Arrays, objects, bigints, symbols and functions are not mapped.
% Instead, a `js_{type}` atom will be returned.
%
% Example (on a browser):
%
% ```
% ?- js_eval("prompt('What is your name?')", Name).
% % A prompt is showed, with a textbox.
% Name = "Whatever was written on the textbox".
% ```
js_eval(JsCode, Result) :-
must_be(chars, JsCode),
can_be(chars, Result),
'$js_eval'(JsCode, Result).

View File

@@ -348,7 +348,7 @@ pub(crate) fn int_pow(n1: Number, n2: Number, arena: &mut Arena) -> Result<Numbe
(Number::Fixnum(n1), Number::Integer(n2)) => { (Number::Fixnum(n1), Number::Integer(n2)) => {
let n1_i = n1.get_num(); let n1_i = n1.get_num();
if !(n1_i == 1 || n1_i == 0 || n1_i == -1) && n2.is_zero() { if !(n1_i == 1 || n1_i == 0 || n1_i == -1) && n2.is_negative() {
let n = Number::Fixnum(n1); let n = Number::Fixnum(n1);
Err(numerical_type_error(ValidType::Float, n, stub_gen)) Err(numerical_type_error(ValidType::Float, n, stub_gen))
} else { } else {
@@ -359,7 +359,7 @@ pub(crate) fn int_pow(n1: Number, n2: Number, arena: &mut Arena) -> Result<Numbe
(Number::Integer(n1), Number::Fixnum(n2)) => { (Number::Integer(n1), Number::Fixnum(n2)) => {
let n2_i = n2.get_num(); let n2_i = n2.get_num();
if !(*n1 == Integer::from(1) || n1.is_zero() || *n1 == Integer::from(-1)) && n2_i < 0 { if !(n1.is_one() || n1.is_zero() || n1.num_eq(&-1)) && n2_i < 0 {
let n = Number::Integer(n1); let n = Number::Integer(n1);
Err(numerical_type_error(ValidType::Float, n, stub_gen)) Err(numerical_type_error(ValidType::Float, n, stub_gen))
} else { } else {
@@ -368,9 +368,7 @@ pub(crate) fn int_pow(n1: Number, n2: Number, arena: &mut Arena) -> Result<Numbe
} }
} }
(Number::Integer(n1), Number::Integer(n2)) => { (Number::Integer(n1), Number::Integer(n2)) => {
if !(*n1 == Integer::from(1) || n1.is_zero() || *n1 == Integer::from(-1)) if !(n1.is_one() || n1.is_zero() || n1.num_eq(&-1)) && n2.is_negative() {
&& n2.is_zero()
{
let n = Number::Integer(n1); let n = Number::Integer(n1);
Err(numerical_type_error(ValidType::Float, n, stub_gen)) Err(numerical_type_error(ValidType::Float, n, stub_gen))
} else { } else {
@@ -711,11 +709,8 @@ pub(crate) fn shl(n1: Number, n2: Number, arena: &mut Arena) -> Result<Number, M
(Number::Fixnum(n1), Number::Integer(n2)) => { (Number::Fixnum(n1), Number::Integer(n2)) => {
let n1 = Integer::from(n1.get_num()); let n1 = Integer::from(n1.get_num());
match (&*n2).try_into() as Result<u32, _> { match (&*n2).try_into() as Result<usize, _> {
Ok(n2) => { Ok(n2) => Ok(Number::arena_from(n1 << n2, arena)),
let n1: u64 = n1.try_into().unwrap();
Ok(Number::arena_from(n1 << n2, arena))
}
_ => Ok(Number::arena_from(n1 << usize::max_value(), arena)), _ => Ok(Number::arena_from(n1 << usize::max_value(), arena)),
} }
} }
@@ -726,11 +721,8 @@ pub(crate) fn shl(n1: Number, n2: Number, arena: &mut Arena) -> Result<Number, M
arena, arena,
)), )),
}, },
(Number::Integer(n1), Number::Integer(n2)) => match (&*n2).try_into() as Result<u32, _> { (Number::Integer(n1), Number::Integer(n2)) => match (&*n2).try_into() as Result<usize, _> {
Ok(n2) => { Ok(n2) => Ok(Number::arena_from(Integer::from(&*n1 << n2), arena)),
let n1: u64 = (&*n1).try_into().unwrap();
Ok(Number::arena_from(Integer::from(n1 << n2), arena))
}
_ => Ok(Number::arena_from( _ => Ok(Number::arena_from(
Integer::from(&*n1 << usize::max_value()), Integer::from(&*n1 << usize::max_value()),
arena, arena,

View File

@@ -18,6 +18,7 @@ pub trait CopierTarget: IndexMut<usize, Output = HeapCellValue> {
fn store(&self, value: HeapCellValue) -> HeapCellValue; fn store(&self, value: HeapCellValue) -> HeapCellValue;
fn deref(&self, value: HeapCellValue) -> HeapCellValue; fn deref(&self, value: HeapCellValue) -> HeapCellValue;
fn push(&mut self, value: HeapCellValue); fn push(&mut self, value: HeapCellValue);
fn push_attr_var_queue(&mut self, attr_var_loc: usize);
fn stack(&mut self) -> &mut Stack; fn stack(&mut self) -> &mut Stack;
fn threshold(&self) -> usize; fn threshold(&self) -> usize;
} }
@@ -73,7 +74,6 @@ impl<T: CopierTarget> CopyTermState<T> {
if h >= self.old_h { if h >= self.old_h {
*self.value_at_scan() = list_loc_as_cell!(h); *self.value_at_scan() = list_loc_as_cell!(h);
self.scan += 1; self.scan += 1;
return; return;
} }
} }
@@ -96,14 +96,19 @@ impl<T: CopierTarget> CopyTermState<T> {
.store(self.target.deref(heap_loc_as_cell!(addr + 1))); .store(self.target.deref(heap_loc_as_cell!(addr + 1)));
if !cdr.is_var() { if !cdr.is_var() {
// mark addr + 1 as a list back edge in the cdr of the list
self.trail_list_cell(addr + 1, threshold); self.trail_list_cell(addr + 1, threshold);
self.target[addr + 1].set_mark_bit(true);
self.target[addr + 1].set_forwarding_bit(true);
} else { } else {
let car = self let car = self
.target .target
.store(self.target.deref(heap_loc_as_cell!(addr))); .store(self.target.deref(heap_loc_as_cell!(addr)));
if !car.is_var() { if !car.is_var() {
// mark addr as a list back edge in the car of the list
self.trail_list_cell(addr, threshold); self.trail_list_cell(addr, threshold);
self.target[addr].set_mark_bit(true);
} }
} }
@@ -178,6 +183,7 @@ impl<T: CopierTarget> CopyTermState<T> {
for (threshold, list_loc) in iter { for (threshold, list_loc) in iter {
self.target[threshold] = list_loc_as_cell!(self.target.threshold()); self.target[threshold] = list_loc_as_cell!(self.target.threshold());
self.target.push_attr_var_queue(threshold - 1);
self.copy_attr_var_list(list_loc); self.copy_attr_var_list(list_loc);
} }
} }
@@ -263,6 +269,7 @@ impl<T: CopierTarget> CopyTermState<T> {
} }
fn copy_var(&mut self, addr: HeapCellValue) { fn copy_var(&mut self, addr: HeapCellValue) {
let index = addr.get_value() as usize;
let rd = self.target.deref(addr); let rd = self.target.deref(addr);
let ra = self.target.store(rd); let ra = self.target.store(rd);
@@ -271,7 +278,20 @@ impl<T: CopierTarget> CopyTermState<T> {
if h >= self.old_h { if h >= self.old_h {
*self.value_at_scan() = ra; *self.value_at_scan() = ra;
self.scan += 1; self.scan += 1;
return;
}
}
(HeapCellValueTag::Lis, h) => {
if h >= self.old_h && self.target[index].get_mark_bit() {
*self.value_at_scan() = heap_loc_as_cell!(
if ra.get_forwarding_bit() {
h + 1
} else {
h
}
);
self.scan += 1;
return; return;
} }
} }
@@ -356,12 +376,16 @@ impl<T: CopierTarget> CopyTermState<T> {
} }
} }
fn unwind_trail(&mut self) { fn unwind_trail(mut self) {
for (r, value) in self.trail.drain(0..) { for (r, value) in self.trail {
let index = r.get_value() as usize; let index = r.get_value() as usize;
match r.get_tag() { match r.get_tag() {
RefTag::AttrVar | RefTag::HeapCell => self.target[index] = value, RefTag::AttrVar | RefTag::HeapCell => {
self.target[index] = value;
self.target[index].set_mark_bit(false);
self.target[index].set_forwarding_bit(false);
}
RefTag::StackCell => self.target.stack()[index] = value, RefTag::StackCell => self.target.stack()[index] = value,
} }
} }

View File

@@ -663,12 +663,16 @@ impl VariableClassifier {
state_stack.last(), state_stack.last(),
Some(TraversalState::RemoveBranchNum) Some(TraversalState::RemoveBranchNum)
) { ) {
// check if the second-to-last element is a regular BuildDisjunct, as we don't // check if the second-to-last element
// want to add GetPrevLevel in case of a TrustMe. // is a regular BuildDisjunct, as we
matches!( // don't want to add GetPrevLevel in
state_stack.iter().rev().nth(1), // case of a TrustMe.
Some(TraversalState::BuildDisjunct(..)) match state_stack.iter().rev().nth(1) {
) Some(&TraversalState::BuildDisjunct(preceding_len)) => {
preceding_len + 1 == build_stack.len()
}
_ => false,
}
} else { } else {
false false
}; };

View File

@@ -36,7 +36,7 @@ macro_rules! try_or_throw {
macro_rules! increment_call_count { macro_rules! increment_call_count {
($s:expr) => {{ ($s:expr) => {{
if !($s.increment_call_count_fn)(&mut $s) { if !$s.increment_call_count() {
$s.backtrack(); $s.backtrack();
continue; continue;
} }
@@ -208,6 +208,7 @@ impl MachineState {
l l
} }
(HeapCellValueTag::Fixnum | (HeapCellValueTag::Fixnum |
HeapCellValueTag::CutPoint |
HeapCellValueTag::Char | HeapCellValueTag::Char |
HeapCellValueTag::F64) => { HeapCellValueTag::F64) => {
c c
@@ -3675,6 +3676,16 @@ impl Machine {
try_or_throw!(self.machine_st, self.install_inference_counter()); try_or_throw!(self.machine_st, self.install_inference_counter());
step_or_fail!(self, self.machine_st.p = self.machine_st.cp); step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
} }
&Instruction::CallInferenceCount => {
let global_count = self.machine_st.cwil.global_count.clone();
self.inference_count(self.machine_st.registers[1], global_count);
step_or_fail!(self, self.machine_st.p += 1);
}
&Instruction::ExecuteInferenceCount => {
let global_count = self.machine_st.cwil.global_count.clone();
self.inference_count(self.machine_st.registers[1], global_count);
step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
}
&Instruction::CallLiftedHeapLength => { &Instruction::CallLiftedHeapLength => {
self.lifted_heap_length(); self.lifted_heap_length();
step_or_fail!(self, self.machine_st.p += 1); step_or_fail!(self, self.machine_st.p += 1);
@@ -4128,6 +4139,14 @@ impl Machine {
try_or_throw!(self.machine_st, self.define_foreign_struct()); try_or_throw!(self.machine_st, self.define_foreign_struct());
step_or_fail!(self, self.machine_st.p = self.machine_st.cp); step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
} }
&Instruction::CallJsEval => {
try_or_throw!(self.machine_st, self.js_eval());
step_or_fail!(self, self.machine_st.p += 1);
}
&Instruction::ExecuteJsEval => {
try_or_throw!(self.machine_st, self.js_eval());
step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
}
&Instruction::CallCurrentTime => { &Instruction::CallCurrentTime => {
self.current_time(); self.current_time();
step_or_fail!(self, self.machine_st.p += 1); step_or_fail!(self, self.machine_st.p += 1);

View File

@@ -57,10 +57,11 @@ impl Machine {
or_frame.prelude.attr_var_queue_len = 0; or_frame.prelude.attr_var_queue_len = 0;
self.machine_st.b = stub_b; self.machine_st.b = stub_b;
self.machine_st.hb = self.machine_st.heap.len();
} }
pub fn run_query(&mut self, query: String) -> QueryResult { pub fn run_query(&mut self, query: String) -> QueryResult {
println!("Query: {}", query); // println!("Query: {}", query);
// Parse the query so we can analyze and then call the term // Parse the query so we can analyze and then call the term
let mut parser = Parser::new( let mut parser = Parser::new(
Stream::from_owned_string(query, &mut self.machine_st.arena), Stream::from_owned_string(query, &mut self.machine_st.arena),
@@ -87,6 +88,7 @@ impl Machine {
.expect("couldn't get code index") .expect("couldn't get code index")
.local() .local()
.unwrap(); .unwrap();
self.machine_st.b0 = self.machine_st.b;
let var_names: IndexMap<_, _> = term_write_result let var_names: IndexMap<_, _> = term_write_result
.var_dict .var_dict
@@ -192,7 +194,7 @@ impl Machine {
let outputter = printer.print(); let outputter = printer.print();
let output: String = outputter.result(); let output: String = outputter.result();
println!("Result: {} = {}", var_key.to_string(), output); // println!("Result: {} = {}", var_key.to_string(), output);
bindings.insert(var_key.to_string(), Value::try_from(output).expect("asdfs")); bindings.insert(var_key.to_string(), Value::try_from(output).expect("asdfs"));
} }
@@ -444,10 +446,7 @@ mod tests {
} }
// Check if the block is a query // Check if the block is a query
if block.starts_with("query") { if let Some(query) = block.strip_prefix("query") {
// Extract the query from the block
let query = &block[5..];
i += 1; i += 1;
println!("query #{}: {}", i, query); println!("query #{}: {}", i, query);
// Parse and execute the query // Parse and execute the query
@@ -457,10 +456,7 @@ mod tests {
// Print the result // Print the result
println!("{:?}", result); println!("{:?}", result);
} else if block.starts_with("consult") { } else if let Some(code) = block.strip_prefix("consult") {
// Extract the code from the block
let code = &block[7..];
println!("load code: {}", code); println!("load code: {}", code);
// Load the code into the machine // Load the code into the machine

View File

@@ -148,9 +148,10 @@ impl<'a, LS: LoadState<'a>> Drop for Loader<'a, LS> {
} }
} }
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Default)]
pub enum CompilationTarget { pub enum CompilationTarget {
Module(Atom), Module(Atom),
#[default]
User, User,
} }
@@ -163,13 +164,6 @@ impl fmt::Display for CompilationTarget {
} }
} }
impl Default for CompilationTarget {
#[inline]
fn default() -> Self {
CompilationTarget::User
}
}
impl CompilationTarget { impl CompilationTarget {
#[inline] #[inline]
pub(crate) fn module_name(&self) -> Atom { pub(crate) fn module_name(&self) -> Atom {

View File

@@ -492,13 +492,22 @@ impl MachineState {
self.permission_error(Permission::Modify, atom!("static_module"), module) self.permission_error(Permission::Modify, atom!("static_module"), module)
} }
SessionError::ExistenceError(err) => self.existence_error(err), SessionError::ExistenceError(err) => self.existence_error(err),
SessionError::ModuleDoesNotContainExport(..) => { SessionError::ModuleDoesNotContainExport(module_name, key) => {
let error_atom = atom!("module_does_not_contain_claimed_export"); let functor_stub = functor_stub(key.0, key.1);
let stub = functor!(
atom!("module_does_not_contain_claimed_export"),
[
atom(module_name),
str(self.heap.len() + 4, 0)
],
[functor_stub]
);
self.permission_error( self.permission_error(
Permission::Access, Permission::Access,
atom!("private_procedure"), atom!("private_procedure"),
functor!(error_atom), stub,
) )
} }
SessionError::ModuleCannotImportSelf(module_name) => { SessionError::ModuleCannotImportSelf(module_name) => {

View File

@@ -96,7 +96,6 @@ pub struct MachineState {
pub(crate) unify_fn: fn(&mut MachineState), pub(crate) unify_fn: fn(&mut MachineState),
pub(crate) bind_fn: fn(&mut MachineState, Ref, HeapCellValue), pub(crate) bind_fn: fn(&mut MachineState, Ref, HeapCellValue),
pub(crate) run_cleaners_fn: fn(&mut Machine) -> bool, pub(crate) run_cleaners_fn: fn(&mut Machine) -> bool,
pub(crate) increment_call_count_fn: fn(&mut MachineState) -> bool,
} }
impl fmt::Debug for MachineState { impl fmt::Debug for MachineState {
@@ -290,6 +289,11 @@ impl<'a> CopierTarget for CopyTerm<'a> {
self.state.heap.push(hcv); self.state.heap.push(hcv);
} }
#[inline(always)]
fn push_attr_var_queue(&mut self, attr_var_loc: usize) {
self.state.attr_var_init.attr_var_queue.push(attr_var_loc);
}
#[inline(always)] #[inline(always)]
fn store(&self, value: HeapCellValue) -> HeapCellValue { fn store(&self, value: HeapCellValue) -> HeapCellValue {
self.state.store(value) self.state.store(value)
@@ -308,6 +312,7 @@ impl<'a> CopierTarget for CopyTerm<'a> {
#[derive(Debug)] #[derive(Debug)]
pub(super) struct CopyBallTerm<'a> { pub(super) struct CopyBallTerm<'a> {
attr_var_queue: &'a mut Vec<usize>,
stack: &'a mut Stack, stack: &'a mut Stack,
heap: &'a mut Heap, heap: &'a mut Heap,
heap_boundary: usize, heap_boundary: usize,
@@ -315,10 +320,16 @@ pub(super) struct CopyBallTerm<'a> {
} }
impl<'a> CopyBallTerm<'a> { impl<'a> CopyBallTerm<'a> {
pub(super) fn new(stack: &'a mut Stack, heap: &'a mut Heap, stub: &'a mut Heap) -> Self { pub(super) fn new(
attr_var_queue: &'a mut Vec<usize>,
stack: &'a mut Stack,
heap: &'a mut Heap,
stub: &'a mut Heap,
) -> Self {
let hb = heap.len(); let hb = heap.len();
CopyBallTerm { CopyBallTerm {
attr_var_queue,
stack, stack,
heap, heap,
heap_boundary: hb, heap_boundary: hb,
@@ -360,6 +371,11 @@ impl<'a> CopierTarget for CopyBallTerm<'a> {
self.stub.push(value); self.stub.push(value);
} }
#[inline(always)]
fn push_attr_var_queue(&mut self, attr_var_loc: usize) {
self.attr_var_queue.push(attr_var_loc);
}
fn store(&self, value: HeapCellValue) -> HeapCellValue { fn store(&self, value: HeapCellValue) -> HeapCellValue {
read_heap_cell!(value, read_heap_cell!(value,
(HeapCellValueTag::Var | HeapCellValueTag::AttrVar, h) => { (HeapCellValueTag::Var | HeapCellValueTag::AttrVar, h) => {
@@ -417,15 +433,17 @@ impl MachineState {
return true; return true;
} }
self.cwil.global_count += 1;
if let Some(&(ref limit, block)) = self.cwil.limits.last() { if let Some(&(ref limit, block)) = self.cwil.limits.last() {
if self.cwil.count == *limit { if self.cwil.local_count == *limit {
self.cwil.inference_limit_exceeded = true; self.cwil.inference_limit_exceeded = true;
self.block = block; self.block = block;
self.unwind_stack(); self.unwind_stack();
return false; return false;
} else { } else {
self.cwil.count += 1; self.cwil.local_count += 1;
} }
} }
@@ -967,7 +985,8 @@ impl MachineState {
#[allow(clippy::upper_case_acronyms)] #[allow(clippy::upper_case_acronyms)]
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct CWIL { pub(crate) struct CWIL {
count: Integer, local_count: Integer,
pub(crate) global_count: Integer,
limits: Vec<(Integer, usize)>, limits: Vec<(Integer, usize)>,
pub(crate) inference_limit_exceeded: bool, pub(crate) inference_limit_exceeded: bool,
} }
@@ -975,22 +994,22 @@ pub(crate) struct CWIL {
impl CWIL { impl CWIL {
pub(crate) fn new() -> Self { pub(crate) fn new() -> Self {
CWIL { CWIL {
count: Integer::from(0), local_count: Integer::from(0),
global_count: Integer::from(0),
limits: vec![], limits: vec![],
inference_limit_exceeded: false, inference_limit_exceeded: false,
} }
} }
pub(crate) fn add_limit(&mut self, limit: usize, block: usize) -> &Integer { pub(crate) fn add_limit(&mut self, mut limit: Integer, block: usize) -> &Integer {
let mut limit = Integer::from(limit); limit += &self.local_count;
limit += &self.count;
match self.limits.last() { match self.limits.last() {
Some((ref inner_limit, _)) if *inner_limit <= limit => {} Some((ref inner_limit, _)) if *inner_limit <= limit => {}
_ => self.limits.push((limit, block)), _ => self.limits.push((limit, block)),
}; }
&self.count &self.local_count
} }
#[inline(always)] #[inline(always)]
@@ -1001,12 +1020,12 @@ impl CWIL {
} }
} }
&self.count &self.local_count
} }
#[inline(always)] #[inline(always)]
pub(crate) fn reset(&mut self) { pub(crate) fn reset(&mut self) {
self.count = Integer::from(0); self.local_count = Integer::from(0);
self.limits.clear(); self.limits.clear();
self.inference_limit_exceeded = false; self.inference_limit_exceeded = false;
} }

View File

@@ -60,7 +60,6 @@ impl MachineState {
unify_fn: MachineState::unify, unify_fn: MachineState::unify,
bind_fn: MachineState::bind, bind_fn: MachineState::bind,
run_cleaners_fn: |_| false, run_cleaners_fn: |_| false,
increment_call_count_fn: |_| true,
} }
} }
@@ -335,7 +334,12 @@ impl MachineState {
self.ball.boundary = self.heap.len(); self.ball.boundary = self.heap.len();
copy_term( copy_term(
CopyBallTerm::new(&mut self.stack, &mut self.heap, &mut self.ball.stub), CopyBallTerm::new(
&mut self.attr_var_init.attr_var_queue,
&mut self.stack,
&mut self.heap,
&mut self.ball.stub,
),
addr, addr,
AttrVarPolicy::DeepCopy, AttrVarPolicy::DeepCopy,
); );

View File

@@ -159,6 +159,14 @@ impl<'a> CopierTarget for TermCopyingMockWAM<'a> {
self.wam.machine_st.heap.push(val); self.wam.machine_st.heap.push(val);
} }
fn push_attr_var_queue(&mut self, attr_var_loc: usize) {
self.wam
.machine_st
.attr_var_init
.attr_var_queue
.push(attr_var_loc);
}
fn stack(&mut self) -> &mut Stack { fn stack(&mut self) -> &mut Stack {
&mut self.wam.machine_st.stack &mut self.wam.machine_st.stack
} }

View File

@@ -211,6 +211,15 @@ impl Machine {
) )
} }
pub fn get_inference_count(&mut self) -> u64 {
self.machine_st
.cwil
.global_count
.clone()
.try_into()
.unwrap()
}
pub fn throw_session_error(&mut self, err: SessionError, key: PredicateKey) { pub fn throw_session_error(&mut self, err: SessionError, key: PredicateKey) {
let err = self.machine_st.session_error(err); let err = self.machine_st.session_error(err);
let stub = functor_stub(key.0, key.1); let stub = functor_stub(key.0, key.1);

View File

@@ -27,6 +27,7 @@ use std::io::{Cursor, ErrorKind, Read, Seek, SeekFrom, Write};
use std::mem; use std::mem;
use std::net::{Shutdown, TcpStream}; use std::net::{Shutdown, TcpStream};
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::path::PathBuf;
use std::ptr; use std::ptr;
#[cfg(feature = "tls")] #[cfg(feature = "tls")]
@@ -1837,42 +1838,55 @@ impl MachineState {
} }
}; };
let file = match open_options.open(&*file_spec.as_str()) { let mut path = PathBuf::from(&*file_spec.as_str());
Ok(file) => file,
Err(err) => {
match err.kind() {
ErrorKind::NotFound => {
// 8.11.5.3j)
let stub = functor_stub(atom!("open"), 4);
let err = loop {
self.existence_error(ExistenceError::SourceSink(self[temp_v!(1)])); let file = match open_options.open(&path) {
Ok(file) => file,
Err(err) => {
match err.kind() {
ErrorKind::NotFound => {
// 8.11.5.3j)
let stub = functor_stub(atom!("open"), 4);
return Err(self.error_form(err, stub)); let err =
self.existence_error(ExistenceError::SourceSink(self[temp_v!(1)]));
return Err(self.error_form(err, stub));
}
ErrorKind::PermissionDenied => {
// 8.11.5.3k)
return Err(self.open_permission_error(
self.registers[1],
atom!("open"),
4,
));
}
_ => {
// assume the OS is out of file descriptors.
let stub = functor_stub(atom!("open"), 4);
let err = self.resource_error(ResourceError::OutOfFiles);
return Err(self.error_form(err, stub));
}
} }
ErrorKind::PermissionDenied => { }
// 8.11.5.3k) };
return Err(self.open_permission_error(
self.registers[1],
atom!("open"),
4,
));
}
_ => {
// assume the OS is out of file descriptors.
let stub = functor_stub(atom!("open"), 4);
let err = self.resource_error(ResourceError::OutOfFiles);
return Err(self.error_form(err, stub)); if path.extension().is_none() {
if let Some(metadata) = file.metadata().ok() {
if metadata.is_dir() {
path.set_extension("pl");
continue;
} }
} }
} }
};
Ok(if is_input_file { return Ok(if is_input_file {
Stream::from_file_as_input(file_spec, file, &mut self.arena) Stream::from_file_as_input(file_spec, file, &mut self.arena)
} else { } else {
Stream::from_file_as_output(file_spec, file, in_append_mode, &mut self.arena) Stream::from_file_as_output(file_spec, file, in_append_mode, &mut self.arena)
}) });
}
} }
} }

View File

@@ -1,8 +1,7 @@
use crate::parser::ast::*; use crate::parser::ast::*;
use crate::parser::parser::*; use crate::parser::parser::*;
use dashu::integer::Sign; use dashu::integer::{Sign, UBig};
use dashu::integer::UBig;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use num_order::NumOrd; use num_order::NumOrd;
@@ -828,8 +827,12 @@ impl MachineState {
) -> usize { ) -> usize {
let threshold = self.lifted_heap.len() - lh_offset; let threshold = self.lifted_heap.len() - lh_offset;
let mut copy_ball_term = let mut copy_ball_term = CopyBallTerm::new(
CopyBallTerm::new(&mut self.stack, &mut self.heap, &mut self.lifted_heap); &mut self.attr_var_init.attr_var_queue,
&mut self.stack,
&mut self.heap,
&mut self.lifted_heap,
);
copy_ball_term.push(list_loc_as_cell!(threshold + 1)); copy_ball_term.push(list_loc_as_cell!(threshold + 1));
copy_ball_term.push(heap_loc_as_cell!(threshold + 3)); copy_ball_term.push(heap_loc_as_cell!(threshold + 3));
@@ -4188,7 +4191,14 @@ impl Machine {
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
#[inline(always)] #[inline(always)]
pub(crate) fn cpu_now(&mut self) { pub(crate) fn cpu_now(&mut self) {
// TODO let millisecs = web_sys::window()
.expect("window global object should be available")
.performance()
.expect("performance property in window should be available")
.now();
let secs = float_alloc!(millisecs / 1000.0, self.machine_st.arena);
self.machine_st.unify_f64(secs, self.deref_register(1));
} }
#[inline(always)] #[inline(always)]
@@ -4879,6 +4889,70 @@ impl Machine {
Ok(()) Ok(())
} }
#[cfg(not(target_arch = "wasm32"))]
#[inline(always)]
pub(crate) fn js_eval(&mut self) -> CallResult {
unimplemented!()
}
#[cfg(target_arch = "wasm32")]
#[inline(always)]
pub(crate) fn js_eval(&mut self) -> CallResult {
let code = self.deref_register(1);
let result_reg = self.deref_register(2);
if let Some(code) = self.machine_st.value_to_str_like(code) {
match js_sys::eval(&code.as_str()) {
Ok(result) => self.unify_js_value(result, result_reg),
Err(result) => self.unify_js_value(result, result_reg),
};
return Ok(());
}
self.machine_st.fail = true;
Ok(())
}
#[cfg(target_arch = "wasm32")]
fn unify_js_value(&mut self, result: wasm_bindgen::JsValue, result_reg: HeapCellValue) {
match result.as_bool() {
Some(result) => match result {
true => self.machine_st.unify_atom(atom!("true"), result_reg),
false => self.machine_st.unify_atom(atom!("false"), result_reg),
},
None => match result.as_f64() {
Some(result) => {
let n = float_alloc!(result, self.machine_st.arena);
self.machine_st.unify_f64(n, result_reg);
}
None => match result.as_string() {
Some(result) => {
let result = AtomTable::build_with(&self.machine_st.atom_tbl, &result);
self.machine_st.unify_complete_string(result, result_reg);
}
None => {
if result.is_null() {
self.machine_st.unify_atom(atom!("null"), result_reg);
} else if result.is_undefined() {
self.machine_st.unify_atom(atom!("undefined"), result_reg);
} else if result.is_symbol() {
self.machine_st.unify_atom(atom!("js_symbol"), result_reg);
} else if result.is_object() {
self.machine_st.unify_atom(atom!("js_object"), result_reg);
} else if result.is_array() {
self.machine_st.unify_atom(atom!("js_array"), result_reg);
} else if result.is_function() {
self.machine_st.unify_atom(atom!("js_function"), result_reg);
} else if result.is_bigint() {
self.machine_st.unify_atom(atom!("js_bigint"), result_reg);
} else {
self.machine_st
.unify_atom(atom!("js_unknown_type"), result_reg);
}
}
},
},
}
}
#[inline(always)] #[inline(always)]
pub(crate) fn current_time(&mut self) { pub(crate) fn current_time(&mut self) {
let timestamp = self.systemtime_to_timestamp(SystemTime::now()); let timestamp = self.systemtime_to_timestamp(SystemTime::now());
@@ -5516,11 +5590,8 @@ impl Machine {
let a2 = self.deref_register(2); let a2 = self.deref_register(2);
let n = match Number::try_from(a2) { let n = match Number::try_from(a2) {
Ok(Number::Fixnum(bp)) => bp.get_num() as usize, Ok(Number::Fixnum(bp)) => Integer::from(bp.get_num() as usize),
Ok(Number::Integer(n)) => { Ok(Number::Integer(n)) => (*n).clone(),
let value: usize = (&*n).try_into().unwrap();
value
}
_ => { _ => {
let stub = functor_stub(atom!("call_with_inference_limit"), 3); let stub = functor_stub(atom!("call_with_inference_limit"), 3);
@@ -5531,21 +5602,24 @@ impl Machine {
let bp = cell_as_fixnum!(a1).get_num() as usize; let bp = cell_as_fixnum!(a1).get_num() as usize;
let a3 = self.deref_register(3); let a3 = self.deref_register(3);
let count = self.machine_st.cwil.add_limit(n, bp);
let result = count.try_into(); let count = self.machine_st.cwil.add_limit(n, bp).clone();
if let Ok(value) = result { self.inference_count(a3, count);
self.machine_st.unify_fixnum(Fixnum::build_with(value), a3);
} else {
let count = arena_alloc!(count.clone(), &mut self.machine_st.arena);
self.machine_st.unify_big_int(count, a3);
}
self.machine_st.increment_call_count_fn = MachineState::increment_call_count;
Ok(()) Ok(())
} }
#[inline(always)]
pub(crate) fn inference_count(&mut self, count_var: HeapCellValue, count: Integer) {
if let Some(value) = <&Integer as TryInto<i64>>::try_into(&count).ok() {
self.machine_st
.unify_fixnum(Fixnum::build_with(value), count_var);
} else {
let count = arena_alloc!(count, &mut self.machine_st.arena);
self.machine_st.unify_big_int(count, count_var);
}
}
#[inline(always)] #[inline(always)]
pub(crate) fn module_exists(&mut self) { pub(crate) fn module_exists(&mut self) {
let module = self.deref_register(1); let module = self.deref_register(1);
@@ -5671,7 +5745,6 @@ impl Machine {
if bp == self.machine_st.b && self.machine_st.cwil.is_empty() { if bp == self.machine_st.b && self.machine_st.cwil.is_empty() {
self.machine_st.cwil.reset(); self.machine_st.cwil.reset();
self.machine_st.increment_call_count_fn = |_| true;
} }
} }
@@ -6791,6 +6864,7 @@ impl Machine {
copy_term( copy_term(
CopyBallTerm::new( CopyBallTerm::new(
&mut self.machine_st.attr_var_init.attr_var_queue,
&mut self.machine_st.stack, &mut self.machine_st.stack,
&mut self.machine_st.heap, &mut self.machine_st.heap,
&mut ball.stub, &mut ball.stub,

View File

@@ -327,8 +327,9 @@ impl DoubleQuotes {
} }
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy, Default)]
pub enum Unknown { pub enum Unknown {
#[default]
Error, Error,
Fail, Fail,
Warn, Warn,
@@ -348,13 +349,6 @@ impl Unknown {
} }
} }
impl Default for Unknown {
#[inline]
fn default() -> Self {
Unknown::Error
}
}
pub fn default_op_dir() -> OpDir { pub fn default_op_dir() -> OpDir {
let mut op_dir = OpDir::with_hasher(FxBuildHasher::default()); let mut op_dir = OpDir::with_hasher(FxBuildHasher::default());

View File

@@ -207,6 +207,13 @@ test("scryer-prolog#2056",(
\+ E=[] \+ E=[]
)). )).
% https://github.com/mthom/scryer-prolog/issues/2175
test("scryer-prolog#2175",(
dif(A,B),
A=_C*[],
A=[]*D*B,D=[]
)).
main :- main :-
findall(test(Name, Goal), test(Name, Goal), Tests), findall(test(Name, Goal), test(Name, Goal), Tests),
run_tests(Tests, Failed), run_tests(Tests, Failed),

View File

@@ -452,4 +452,4 @@ print_exception_with_check(E) :-
% is expected to be printed instead. % is expected to be printed instead.
; print_exception(E) ; print_exception(E)
). ).

View File

@@ -783,10 +783,10 @@ test_217_181_290_317 :-
( op(1105,xfy,'|'), ( op(1105,xfy,'|'),
read_from_chars("(a-->b,c|d).", T0), read_from_chars("(a-->b,c|d).", T0),
writeq_term_to_chars(T0, C0), writeq_term_to_chars(T0, C0),
C0 == "a-->b,c | d", C0 == "a-->b,c|d",
read_from_chars("[(a|b)].", T1), read_from_chars("[(a|b)].", T1),
writeq_term_to_chars(T1, C1), writeq_term_to_chars(T1, C1),
C1 == "[(a | b)]", C1 == "[(a|b)]",
read_from_chars("[a,(b,c)|[]].", T2), read_from_chars("[a,(b,c)|[]].", T2),
writeq_term_to_chars(T2, C2), writeq_term_to_chars(T2, C2),
C2 == "[a,(b,c)]" C2 == "[a,(b,c)]"