Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.png filter=lfs diff=lfs merge=lfs -text
28 changes: 28 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,45 @@
# 1.0.5
ADDED new keys to `commando.impl.utils/*execute-config*`. Added hooks keys
- `:hook-execute-start` if not nil, call procedure at the start of `commando.core/execute` function.
- `:hook-execute-end` if not nil, call procedure at the end of `commando.core/execute` function.

ADDED microsecs time measurement. All steps inside the `commando.core/execute` measure time. Every measurement adding to _status-map_ structure under `:stats` key.

UPDATED `status-map` structure. Was added two keys
- `:uuid` autogenerated unique invocation identifier gotted for each `commando.core/execute` call
- `:stats` contains vector of tuples like `["execute", 1085471, "1.085471ms"]` where `[<step-id>, <microsecs>, <formatted time>]`. Counts of steps depended from `*execute-config*` key `:debug-mode`.

UPDATED sort-commands-by-deps. Straightforward sets joining in base Kahn's algorithm was rewrited with in-degree counting optimization.

UPDATED build-deps-tree. Instead of searching dependency using the list of commandmaps by iterating across the list(O(n^2) in worst case), before starting to build a dependency graph, we quickly building a path-trie structure efficiently. This gave as fast way to resolve point/all-inside dependency only in O(n) time.

FIXED find-commands. StackOverflowException in case of long lists of dependencies in the one level.


# 1.0.4
ADDED commando.commands.builtin/commando-macro-spec. The new type of command that allow to group instructions by its functionality and use it as a single command. Added Readme information about the macro.

ADDED documentation for commando.commands.builtin commands. Now each built-in command have explanation of its behavior and examples of usage.

UPDATED upgrade commando.commands.query-dsl. Function `resolve-query` was removed and replaced by `resolve-fn`, `resolve-instruction`, `resolve-instruction-qe` function called a **resolvers**. Explanations about the resolvers added to _docs/query-dsl.md_ file.

UPDATED error serialization. `commando.impl.utils` contains new way to serialize errors for CLJ/CLJS. Now all errors are serialized to map with keys: `:type`, `:class`, `:message`, `:data` (if exists) and `:stacktrace` (if exists), `:cause` (if exists). See `commando.impl.utils/serialize-exception` for more information. You can expand the error handlers using `serialize-exception-fn` multimethod (but for CLJ only).

ADDED tests for macro-spec, errors and query-dsl changes.

UPDATED README.md 'Debugging section' was replaced on 'Configuring Execution Behavior' which contains more detailed information how to modify execution behavior.

UPDATED dynamic variable *debug-mode* replaced by the `*execute-config*` which is a map that can contain multiple configuration options.

FIXED Removed `detach-instruction-commands` call from `commando.core/build-compiler`. In `commando.core/build-compiler`, the line that detached instruction commands from the registry was removed. This means the compiled registry now includes internal commands (_map, _value, _vector).

# 1.0.3
UPDATED behavior `:validate-params-fn`. If the function return anything except `true` it ment validation failure. If the function return data, they will be attached to returned error inside status map. Added tests.

FIXED align serialization of exeption for CLJ/CLJS

ADDED function normalization for :commando/fn, :commando/apply, :commando/from commands. In CLJ it will acept the symbols,vars,functions,keywords. In CLJS acceptable is only function and keywords.

FIXED QueryDSL. QueryExpression passing by :keys and :strs(for string Instruction keys)

# 1.0.2
Expand Down
57 changes: 55 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
- [Configuring Execution Behavior](#configuring-execution-behavior)
- [`:debug-result`](#debug-result)
- [`:error-data-string`](#error-data-string)
- [Performance](#performance)
- [Integrations](#integrations)
- [Versioning](#versioning)
- [License](#license)
Expand All @@ -33,10 +34,10 @@

```clojure
;; deps.edn with git
{org.clojars.funkcjonariusze/commando {:mvn/version "1.0.4"}}
{org.clojars.funkcjonariusze/commando {:mvn/version "1.0.5"}}

;; leiningen
[org.clojars.funkcjonariusze/commando "1.0.4"]
[org.clojars.funkcjonariusze/commando "1.0.5"]
```

## Quick Start
Expand Down Expand Up @@ -413,6 +414,9 @@ On successful execution (`:ok`), you get:
;; RETURN =>
{:status :ok,
:instruction {"1" 1, "2" 1, "3" 1}
:stats
[["execute-commands!" 95838 "95.838µs"]
["execute" 1085471 "1.085471ms"]]
:successes
[{:message
"Commando. parse-instruction-map. Entities was successfully collected"}
Expand Down Expand Up @@ -500,6 +504,13 @@ Here's an example of how to use `:debug-result`:
;; RETURN =>
{:status :ok,
:instruction {"1" 1, "2" 1, "3" 1}
:stats
[["use-registry" 111876 "111.876µs"]
["find-commands" 303062 "303.062µs"]
["build-deps-tree" 134049 "134.049µs"]
["sort-commands-by-deps" 292206 "292.206µs"]
["execute-commands!" 53762 "53.762µs"]
["execute" 1074110 "1.07411ms"]]
:registry
[{:type :commando/from,
:recognize-fn #function[commando.commands.builtin/fn],
Expand Down Expand Up @@ -577,6 +588,48 @@ When `:error-data-string` is `true`, the `:data` key within serialized `Exceptio
;; :value {:commando/from "BROKEN"}}}
```


### Performance

Commando is designed for high performance, using efficient algorithms for dependency resolution and command execution to process complex instructions swiftly.

All benchmarks were conducted on an **Intel Core i9-13980HX**. The primary metric for performance is the number of dependencies within an instruction.

#### Total Execution Time (Typical Workloads)

The graph below illustrates the total execution time for instructions with a typical number of dependencies, ranging from 1,250 to 80,000. As you can see, the execution time scales linearly and remains in the low millisecond range, demonstrating excellent performance for common use cases.

<div align="center">
<img width="100%" src="./test/perf/commando/execute(normal) milisecs_x_deps.png">
</div>

#### Execution Step Analysis

To provide deeper insight, we've broken down the execution into five distinct steps:
1. **use-registry**: Builds the command registry from the provided specs.
2. **find-commands**: Scans the instruction map to identify all command instances.
3. **build-deps-tree**: Constructs a Directed Acyclic Graph (DAG) of dependencies between commands.
4. **sort-commands-by-deps**: Sorts the commands based on the dependency graph to determine the correct execution order.
5. **execute-commands!**: Executes the commands in the resolved order.

The following graphs show the performance of each step under both normal and extreme load conditions.

**Normal Workloads (up to 80,000 dependencies)**

Under normal conditions, each execution step completes in just a few milliseconds. The overhead of parsing, dependency resolution, and execution is minimal, ensuring a fast and responsive system.

<div align="center">
<img width="100%" src="./test/perf/commando/execute-steps(normal) milisecs_x_deps.png">
</div>

**Massive Workloads (up to 5,000,000 dependencies)**

To test the limits of the library, we benchmarked it with instructions containing up to 5 million dependencies. The graph below shows that while the system scales, the `find-commands` (parsing) and `build-deps-tree` (dependency graph construction) phases become the primary bottlenecks. This demonstrates that the core execution remains fast, but performance at extreme scales is dominated by the initial analysis steps.

<div align="center">
<img width="100%" src="./test/perf/commando/execute-steps(massive dep grow) secs_x_deps.png">
</div>

# Integrations

- [Work with JSON](./doc/json.md)
Expand Down
6 changes: 4 additions & 2 deletions deps.edn
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
:main-opts ["-m" "cljs-test-runner.main" "-d" "test/unit"]
:patterns [".*-test$"]}
;;For local performance regression tests
:dev {:extra-deps {criterium/criterium {:mvn/version "0.4.6"}}
:extra-paths ["test/perf"]}
:performance
{:extra-deps {cljfreechart/cljfreechart {:mvn/version "0.2.0"}}
:main-opts ["-m" "commando.core-perf-test"]
:extra-paths ["test/perf"]}
;;Build jar
:build {:deps {io.github.clojure/tools.build {:mvn/version "0.10.10"}}
:ns-default build
Expand Down
Binary file modified logo/Commando(bottom outlined).png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified logo/Commando(bottom).png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified logo/Commando(outlined).png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified logo/Commando(top outlined).png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified logo/Commando.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<packaging>jar</packaging>
<groupId>org.clojars.funkcjonariusze</groupId>
<artifactId>commando</artifactId>
<version>1.0.4</version>
<version>1.0.5</version>
<name>commando</name>
<dependencies>
<dependency>
Expand Down Expand Up @@ -42,6 +42,6 @@
<url>https://github.com/funkcjonariusze/commando</url>
<connection>scm:git:git://github.com/funkcjonariusze/commando.git</connection>
<developerConnection>scm:git:ssh://git@github.com:funkcjonariusze/commando.git</developerConnection>
<tag>1.0.4</tag>
<tag>1.0.5</tag>
</scm>
</project>
2 changes: 1 addition & 1 deletion src/commando/commands/builtin.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,7 @@
`commando.commands.builtin/command-macro`
`commando.commands.builtin/command-macro-spec`"}
command-macro-json-spec
{:type :commando/macro
{:type :commando/macro-json
:recognize-fn #(and
(map? %)
(contains? % "commando-macro"))
Expand Down
Loading
Loading