{"id":17166420,"url":"https://github.com/andreaferretti/kmeans","last_synced_at":"2025-08-21T18:33:46.081Z","repository":{"id":23105776,"uuid":"26460048","full_name":"andreaferretti/kmeans","owner":"andreaferretti","description":"KMeans benchmark","archived":false,"fork":false,"pushed_at":"2017-06-06T08:50:20.000Z","size":2429,"stargazers_count":136,"open_issues_count":1,"forks_count":40,"subscribers_count":9,"default_branch":"master","last_synced_at":"2024-12-10T12:11:40.736Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/andreaferretti.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-11-10T23:05:06.000Z","updated_at":"2024-09-20T15:24:47.000Z","dependencies_parsed_at":"2022-08-21T20:10:47.013Z","dependency_job_id":null,"html_url":"https://github.com/andreaferretti/kmeans","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andreaferretti%2Fkmeans","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andreaferretti%2Fkmeans/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andreaferretti%2Fkmeans/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/andreaferretti%2Fkmeans/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/andreaferretti","download_url":"https://codeload.github.com/andreaferretti/kmeans/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230527875,"owners_count":18240052,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-14T23:05:31.183Z","updated_at":"2024-12-20T03:10:23.708Z","avatar_url":"https://github.com/andreaferretti.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"This benchmark is born to compare the performance of Pharo 3 in executing a simple machine learning algorithm with a reference implementation in Python and Scala. Since then, it got a little out of hand, and a few other implementations are available.\n\n**This benchmark is not going to be updated anymore after 30/3/2017. I don't have the time to reinstall all languages on the original machine. Still, you can play with it and test everything on your computer. Thus, I will not accept any more PRs to this repository.**\n\nRules\n=====\n\nThe implementations should all follow the same algorithm, and be optimized for idiomatic code and not for speed. The example is intended to compare time of execution for a typical machine learning algorithm, ideally during an interactive session, instead of highly optimized production code. As such, it is important that the code is straightforward and that there is no separate phase to prepare the caches.\n\nThe points are in `points.json`, and are to be grouped into 10 clusters, using 15 iterations of kmeans. The initial centroids are initialized to the first 10 points, and we take an average over 100 runs.\n\nResults\n=======\n\nTime for running on my laptop are available under `results`. A few surprises:\n\n* Writing a working Rust implementation was surprisingly difficult; writing one that would perform decently even more so. I had to rely frequently on help from people online.\n* PyPy is able to outperform Scala\n* Factor is pretty impressive, given that it is a fairly small project with a dedicated VM. With an implementation in 8 (!) lines, we get the a fairly performing dynamic language\n* Nim was also quite impressive: my first implementation was as easy as Python, and it was just behind Rust; when an unnecessary copy was removed, it turned out to be the fastest.\n\nContribute\n==========\n\nIf you want to contribute an implementation in a different language, please file a PR. Try to follow the same logic that is used in the examples in other languages - for instance, using a group by operation where available. As you may notice, the algorithm is not optimized, and intentionally so: while K-means in particular has various possible optimizations, other similar algorithms may fail to have the particular shape that makes these optimizations viable.\n\nFor the curious folks, I have tried a more optimized (single-threaded) implementation in Nim, that avoids the square root in the distance and accumulates the sum of points near a centroid, rather than putting them into a data structure. For comparison, this version runs in 67 ms. Computers are actually quite fast, these days!\n\nHow to run\n==========\n\n**C**\n\n    sudo apt-get install libglib2.0-0\n    sudo apt-get install libjansson-dev # or equivalent for your OS\n    ./compile.sh\n    ./kmeans\n\n**C++**\n\nWe use [BiiCode](https://www.biicode.com/) for building. Assuming you have it installed, from the `cpp` directory do\n\n    bii init -L\n    bii configure -DCMAKE_BUILD_TYPE=RELEASE\n    bii build\n    bin/user_cpp_benchmark\n\n**Chapel**\n\nBefore compiling chapel please do:\n\n    export CHPL_LLVM=llvm\n\nto enable LLVM support (this is used for the json import in C). Then, make sure that `chpl` is on your `$PATH` (for instance with `source source util/setchplenv.sh`). Finally:\n\n    make\n    ./kmeans\n\n**Clojure**: `lein with-profile uberjar run`\n\n**Common Lisp**: `sbcl --script kmeans.lisp`\n\n**Crystal**:\n\n    crystal build kmeans.cr --release\n    ./kmeans\n\n**CUDA**\n\n    sudo apt-get install libjansson-dev # or equivalent for your OS (e.g. on Mac you can: brew install jansson)\n    cmake .\n    make\n\nthen\n\n    ./kmeans.out [ input_file.json number_of_points number_of_centroids ]\n\n**D**:\n\n    dmd -O -inline -release -noboundscheck main.d\n    ./main\n\n**Elixir**:\n\n    elixirc kmeans.ex\n    elixir main.exs\n\n**Erlang**:\n\n    erl\n    1\u003e c(main).\n    2\u003e c(kmeans).\n    3\u003e main:run().\n\n**F#**:\n\n    make\n    make run\n\n**Factor**:\n\n    USE: kmeans.benchmark\n    100 \"../points.json\" kmeans-benchmark\n\n**Go**\n\n    go build main.go\n    ./main\n\n**Haskell**:\n\n    cabal install --only-dependencies\n    cabal build\n    dist/build/kmeans/kmeans\n\n**Java**:\n\n    mvn compile\n    mvn exec:java\n\n\n**Java 8 (Streams and Lambdas)**:\n\n    mvn compile\n    mvn exec:java\n\n**Julia**:\n\n    julia -e 'Pkg.add(\"JSON\")'\n    julia kmeans.jl\n\n**Kotlin**:\n\n    mvn compile exec:java\n\n**Lua**: download [this JSON library](http://dkolf.de/src/dkjson-lua.fsl/home) and put it in the same folder as the main file. Then run\n\n    lua kmeans.lua\n    luajit kmeans.lua\n\n**Nim**:\n\n    nim c -d:release benchmark\n    ./benchmark\n\n**Node**:\n\n    npm install\n    node kmeans.js\n\n**OCaml**:\n\n    opam install core yojson\n    corebuild -pkg yojson main.native\n    ./main.native\n\n**OpenCL**: need to have Nim and Nimble installed, as well as a NVIDIA GPU.\nCheck the library paths in `kmeans.nimble`, then run `nimble kmeans`.\n\n**OpenMP**\n\n    make\n\n    ./kmeans.out [ inputfile.json number_of_points number_of_centroids number_of_threads ]\n\n    or:\n\n    ./kmeans.out [number_of_threads]\n\n**Parasail**: assume `pslc.csh` is on `$PATH`. Then\n\n    pslc.csh -O3 point.psl kmeans.psl benchmark.psl -o benchmark\n    ./benchmark\n\n**Perl**:\n\n    perl kmeans.pl\n\n**Pharo3**: install `NeoJSON` and file-in `Kmeans.st`, then open a workspace and write something like\n\n    | path points kmeans |\n\n    path := '../points.json'.\n\n    kmeans := KMeans new\n      iterations: 15;\n      clusters: 10;\n      yourself.\n\n    StandardFileStream readOnlyFileNamed: path\n      do: [ :stream |\n        points := (NeoJSONReader on: stream) next collect: [ :each |\n          (each first) @ (each second)\n        ].\n      ].\n\n    kmeans benchmark: points repeating: 100\n\n**Python**:\n\n    python kmeans.py\n    pypy kmeans.py\n\n**Pony**:\n\n    make\n    make run\n\n**Ruby**:\n\n    ruby kmeans.rb\n    rbx kmeans.rb\n\n**Rust**:\n\n    cargo run --release\n\n**Scala**: `sbt run`\n\n**Scala-Js**:\n\nFirst, generate the compiled javascript with `sbt fullOptJS`. Then,\n`cd target/scala-2.11`, open `node` and\n\n    \u003e require('./kmeans-opt')\n    \u003e require('./kmeans-launcher')\n\nAt first, it seems that nothing is going on, but after a while you should see\nthe results printed.\n\n**Scala-Native**: `sbt run`\n\n**Stanza**:\n\nInstall Stanza following instructions [here](http://lbstanza.org/chapter1.html#anchor1) and put it into your PATH.\n\n    ./compile.sh\n    ./kmeans\n\n**Swift**:\n\n    swiftc -Ounchecked kmeans.swift main.swift\n    ./main\n\n**X10**: First\n\n    mkdir cbin\n    mkdir javabin\n\nMake sure that the `bin` folder of X10 is on your path. Then, for the Java target, decomment lines with `Java json import` inside `Main.x10` and\n\n    make java\n    make runJava\n\nFor the native target, decomment lines with `C json import` inside `Main.x10` and\n\n    make c\n    make runC\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandreaferretti%2Fkmeans","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fandreaferretti%2Fkmeans","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fandreaferretti%2Fkmeans/lists"}