├── .github
└── workflows
│ ├── deploy.yml
│ └── test.yml
├── .mergify.yml
├── Makefile
├── Manifest.toml
├── Project.toml
├── README.md
├── bin
├── runtests.jl
└── serve
├── scripts
├── collatz.jl
├── false_sharing_demo.jl
└── perf_c2c_demo.jl
└── src
├── .gitignore
├── 404.md
├── _assets
├── hamburger.svg
├── juliafolds-logo.ico
├── rndimg.jpg
├── scripts
│ ├── generate_results.jl
│ ├── output
│ │ ├── script1.out
│ │ └── script2.svg
│ ├── script1.jl
│ └── script2.jl
└── tutorials
│ └── mutations
│ ├── accidental_mutations_1_threads.jl
│ ├── accidental_mutations_2_seq.jl
│ ├── accidental_mutations_3_threads_fixed.jl
│ ├── accidental_mutations_4_floop.jl
│ ├── accidental_mutations_5_floop_fixed.jl
│ ├── adjoining_trick_1.jl
│ ├── adjoining_trick_2.jl
│ ├── combining_containers_1.jl
│ ├── combining_containers_2.jl
│ ├── combining_containers_3.jl
│ ├── example_1.jl
│ ├── example_2.jl
│ ├── example_3.jl
│ ├── example_4.jl
│ ├── example_5.jl
│ ├── filling_1.jl
│ ├── filling_2.jl
│ ├── filling_3.jl
│ ├── floops_init_1.jl
│ ├── floops_reduce_1.jl
│ ├── floops_reduce_2.jl
│ ├── floops_reduce_3.jl
│ ├── floops_reduce_4.jl
│ ├── floops_reduce_5.jl
│ ├── floops_reduce_6_vcat.jl
│ ├── floops_reduce_do_1.jl
│ ├── floops_reduce_do_2.jl
│ ├── floops_reduce_do_3.jl
│ ├── floops_reduce_do_4.jl
│ ├── floops_reduce_do_correct.jl
│ ├── floops_reduce_do_incorrect.jl
│ ├── floops_reduce_do_os_1.jl
│ ├── floops_reduce_do_os_2.jl
│ ├── ownership_passing_style_second_1_intro.jl
│ ├── ownership_passing_style_second_2_wrong.jl
│ ├── ownership_passing_style_second_3_correct.jl
│ ├── test_mutations.jl
│ └── transducers_oninit.jl
├── _css
├── basic.css
└── franklin.css
├── _layout
├── foot.html
├── foot_highlight.html
├── foot_katex.html
├── head.html
├── head_highlight.html
├── head_katex.html
├── header.html
├── page_foot.html
└── tag.html
├── _libs
├── highlight
│ ├── github.min.css
│ └── highlight.pack.js
└── katex
│ ├── auto-render.min.js
│ ├── fonts
│ ├── KaTeX_AMS-Regular.ttf
│ ├── KaTeX_AMS-Regular.woff
│ ├── KaTeX_AMS-Regular.woff2
│ ├── KaTeX_Caligraphic-Bold.ttf
│ ├── KaTeX_Caligraphic-Bold.woff
│ ├── KaTeX_Caligraphic-Bold.woff2
│ ├── KaTeX_Caligraphic-Regular.ttf
│ ├── KaTeX_Caligraphic-Regular.woff
│ ├── KaTeX_Caligraphic-Regular.woff2
│ ├── KaTeX_Fraktur-Bold.ttf
│ ├── KaTeX_Fraktur-Bold.woff
│ ├── KaTeX_Fraktur-Bold.woff2
│ ├── KaTeX_Fraktur-Regular.ttf
│ ├── KaTeX_Fraktur-Regular.woff
│ ├── KaTeX_Fraktur-Regular.woff2
│ ├── KaTeX_Main-Bold.ttf
│ ├── KaTeX_Main-Bold.woff
│ ├── KaTeX_Main-Bold.woff2
│ ├── KaTeX_Main-BoldItalic.ttf
│ ├── KaTeX_Main-BoldItalic.woff
│ ├── KaTeX_Main-BoldItalic.woff2
│ ├── KaTeX_Main-Italic.ttf
│ ├── KaTeX_Main-Italic.woff
│ ├── KaTeX_Main-Italic.woff2
│ ├── KaTeX_Main-Regular.ttf
│ ├── KaTeX_Main-Regular.woff
│ ├── KaTeX_Main-Regular.woff2
│ ├── KaTeX_Math-BoldItalic.ttf
│ ├── KaTeX_Math-BoldItalic.woff
│ ├── KaTeX_Math-BoldItalic.woff2
│ ├── KaTeX_Math-Italic.ttf
│ ├── KaTeX_Math-Italic.woff
│ ├── KaTeX_Math-Italic.woff2
│ ├── KaTeX_SansSerif-Bold.ttf
│ ├── KaTeX_SansSerif-Bold.woff
│ ├── KaTeX_SansSerif-Bold.woff2
│ ├── KaTeX_SansSerif-Italic.ttf
│ ├── KaTeX_SansSerif-Italic.woff
│ ├── KaTeX_SansSerif-Italic.woff2
│ ├── KaTeX_SansSerif-Regular.ttf
│ ├── KaTeX_SansSerif-Regular.woff
│ ├── KaTeX_SansSerif-Regular.woff2
│ ├── KaTeX_Script-Regular.ttf
│ ├── KaTeX_Script-Regular.woff
│ ├── KaTeX_Script-Regular.woff2
│ ├── KaTeX_Size1-Regular.ttf
│ ├── KaTeX_Size1-Regular.woff
│ ├── KaTeX_Size1-Regular.woff2
│ ├── KaTeX_Size2-Regular.ttf
│ ├── KaTeX_Size2-Regular.woff
│ ├── KaTeX_Size2-Regular.woff2
│ ├── KaTeX_Size3-Regular.ttf
│ ├── KaTeX_Size3-Regular.woff
│ ├── KaTeX_Size3-Regular.woff2
│ ├── KaTeX_Size4-Regular.ttf
│ ├── KaTeX_Size4-Regular.woff
│ ├── KaTeX_Size4-Regular.woff2
│ ├── KaTeX_Typewriter-Regular.ttf
│ ├── KaTeX_Typewriter-Regular.woff
│ └── KaTeX_Typewriter-Regular.woff2
│ ├── katex.min.css
│ └── katex.min.js
├── config.md
├── explanation
├── index.html
├── libraries.md
└── semidirect-products.md
├── howto
├── faq.md
└── index.html
├── index.md
├── reference
└── index.html
├── tutorials
├── concurrency-patterns.md
├── index.html
├── mutations.md
└── quick-introduction.md
└── utils.jl
/.github/workflows/deploy.yml:
--------------------------------------------------------------------------------
1 | name: Build and Deploy
2 | on:
3 | push:
4 | # NOTE:
5 | # For a **project** site (username.github.io/project/), push things
6 | # to the **master** branch and make sure to set the line below to
7 | # ` - master`; also, at the end of the file, change to `BRANCH: gh-pages`
8 | #
9 | # For a **personal** site (username.github.io/), push things to a **dev**
10 | # branch and make sure to set the line below to `- dev` this is
11 | # because for user pages GitHub pages **requires** the deployment to be
12 | # on the master branch; also, at the end of the file, change to
13 | # `BRANCH: master`
14 | branches:
15 | - master
16 | - dev
17 | pull_request:
18 | jobs:
19 | build-and-deploy:
20 | runs-on: ubuntu-latest
21 | steps:
22 | - name: Checkout
23 | uses: actions/checkout@v2
24 | with:
25 | persist-credentials: false
26 | - name: Install SSH Client
27 | continue-on-error: true
28 | if: >-
29 | github.event_name != 'pull_request' ||
30 | github.repository == github.event.pull_request.head.repo.full_name
31 | # Checking against github.repository to avoid failing in PRs
32 | # from forks.
33 | uses: webfactory/ssh-agent@v0.4.1
34 | with:
35 | ssh-private-key: ${{ secrets.FRANKLIN_PRIV }}
36 |
37 | - name: Fix URLs for PR preview deployment (pull request previews)
38 | if: github.event_name == 'pull_request'
39 | run: |
40 | echo "JULIA_FRANKLIN_WEBSITE_URL=https://juliafolds.github.io/data-parallelism/previews/PR${{ github.event.number }}/" >> $GITHUB_ENV
41 | echo "JULIA_FRANKLIN_PREPATH=data-parallelism/previews/PR${{ github.event.number }}" >> $GITHUB_ENV
42 | - run: echo $JULIA_FRANKLIN_WEBSITE_URL
43 | - run: echo $JULIA_FRANKLIN_PREPATH
44 |
45 | # NOTE
46 | # Python is necessary for pre-rendering steps as well as to install
47 | # matplotlib which is necessary if you intend to use PyPlot. If you do
48 | # not, then you can remove the `run: pip install matplotlib` line.
49 | - name: Install python
50 | uses: actions/setup-python@v1
51 | with:
52 | python-version: '3.x'
53 | - name: Install Julia
54 | uses: julia-actions/setup-julia@v1
55 | with:
56 | version: 1.6
57 | # NOTE
58 | # The steps below ensure that NodeJS and Franklin are loaded then it
59 | # installs highlight.js which is needed for the prerendering step.
60 | # Then the environment is activated and instantiated to install all
61 | # Julia packages which may be required to successfully build your site.
62 | #
63 | # The last line should be `optimize()` though you may want to give it
64 | # specific arguments, see the documentation or ?optimize in the REPL.
65 | - run: julia --project=. -e 'using Pkg; Pkg.instantiate();'
66 | - run: julia --project=. -e '
67 | cd("src");
68 | using NodeJS;
69 | run(`$(npm_cmd()) install highlight.js`);'
70 | - run: julia --project=. -e '
71 | cd("src");
72 | using Franklin;
73 | optimize()'
74 | env:
75 | GKSwstype: "nul"
76 |
77 | - name: Deploy (preview)
78 | if: >-
79 | github.event_name == 'pull_request' &&
80 | github.repository == github.event.pull_request.head.repo.full_name
81 | # Checking against github.repository to avoid failing in PRs
82 | # from forks.
83 | uses: JamesIves/github-pages-deploy-action@releases/v3
84 | with:
85 | SSH: true
86 | BRANCH: gh-pages
87 | FOLDER: src/__site
88 | TARGET_FOLDER: previews/PR${{ github.event.number }}
89 |
90 | # https://github.com/niteoweb/pull_request_status_action
91 | - name: Set PR status
92 | if: >-
93 | github.event_name == 'pull_request' &&
94 | github.repository == github.event.pull_request.head.repo.full_name
95 | uses: niteoweb/pull_request_status_action@v1.0.0
96 | with:
97 | pr_number: ${{ github.event.number }}
98 | state: success
99 | repository: ${{ github.repository }}
100 | target_url: "https://juliafolds.github.io/data-parallelism/previews/PR${{ github.event.number }}/"
101 | description: "preview is successfully deployed"
102 | context: Franklin/deploy
103 | env:
104 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
105 |
106 | - name: Check build status
107 | run: >-
108 | julia -e 'read("src/.test-result", String) == "" ? nothing : exit(99)'
109 |
110 | - name: Deploy (main)
111 | if: github.event_name == 'push'
112 | uses: JamesIves/github-pages-deploy-action@releases/v3
113 | with:
114 | SSH: true
115 | BRANCH: gh-pages
116 | FOLDER: src/__site
117 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Run tests
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | tags: '*'
8 | pull_request:
9 |
10 | jobs:
11 | test:
12 | runs-on: ubuntu-latest
13 | name: Test
14 | steps:
15 | - uses: actions/checkout@v2
16 | - name: Setup julia
17 | uses: julia-actions/setup-julia@v1
18 | with:
19 | version: '1.6'
20 | - run: make test
21 | env:
22 | JULIA_NUM_THREADS: '2'
23 |
--------------------------------------------------------------------------------
/.mergify.yml:
--------------------------------------------------------------------------------
1 | # [NOTE] This setting relies on "required status check":
2 | # https://docs.github.com/en/github/administering-a-repository/about-required-status-checks
3 |
4 | pull_request_rules:
5 | - name: remove outdated reviews
6 | conditions:
7 | - base=master
8 | actions:
9 | dismiss_reviews: {}
10 | - name: automatic squash-merge when CI passes
11 | conditions:
12 | - base=master
13 | - "#approved-reviews-by>=1"
14 | - label=ready-to-merge:squash
15 | - label!=work-in-progress
16 | actions:
17 | merge:
18 | method: squash
19 | - name: automatic squash-merge when CI passes (@tkf)
20 | conditions:
21 | - base=master
22 | - author=tkf
23 | - label=ready-to-merge:squash
24 | - label!=work-in-progress
25 | actions:
26 | merge:
27 | method: squash
28 | - name: automatic rebase-merge when CI passes
29 | conditions:
30 | - base=master
31 | - "#approved-reviews-by>=1"
32 | - label=ready-to-merge:rebase
33 | - label!=work-in-progress
34 | actions:
35 | merge:
36 | method: rebase
37 | - name: automatic rebase-merge when CI passes (@tkf)
38 | conditions:
39 | - base=master
40 | - author=tkf
41 | - label=ready-to-merge:rebase
42 | - label!=work-in-progress
43 | actions:
44 | merge:
45 | method: rebase
46 | - name: automatic merge when CI passes
47 | conditions:
48 | - base=master
49 | - "#approved-reviews-by>=1"
50 | - label=ready-to-merge:merge
51 | - label!=work-in-progress
52 | actions:
53 | merge:
54 | method: merge
55 | - name: automatic merge when CI passes (@tkf)
56 | conditions:
57 | - base=master
58 | - author=tkf
59 | - label=ready-to-merge:merge
60 | - label!=work-in-progress
61 | actions:
62 | merge:
63 | method: merge
64 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: serve test clean instantiate
2 |
3 | JULIA_CMD = julia --color=yes --startup-file=no
4 |
5 | serve: instantiate
6 | bin/serve
7 |
8 | test: instantiate
9 | bin/runtests.jl
10 |
11 | clean:
12 | rm -rf src/__site
13 |
14 | instantiate:
15 | $(JULIA_CMD) -e 'using Pkg; Pkg.activate("."); Pkg.instantiate()'
16 |
--------------------------------------------------------------------------------
/Project.toml:
--------------------------------------------------------------------------------
1 | [deps]
2 | BangBang = "198e06fe-97b7-11e9-32a5-e1d131e6ad66"
3 | Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b"
4 | FLoops = "cc61a311-1640-44b5-9fba-1b764f453329"
5 | Folds = "41a02a25-b8f0-4f67-bc48-60067656b558"
6 | Franklin = "713c75ef-9fc9-4b05-94a9-213340da978e"
7 | Glob = "c27321d9-0574-5035-807b-f59d2c89b15c"
8 | LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
9 | MicroCollections = "128add7d-3638-4c79-886c-908ea0c25c34"
10 | NodeJS = "2bd173c7-0d6d-553b-b6af-13a54713934c"
11 | OnlineStats = "a15396b6-48d5-5d58-9928-6d29437db91e"
12 | Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
13 | ThreadsX = "ac1d9e8a-700a-412c-b207-f0111f4b6c0d"
14 | Transducers = "28d57a85-8fef-5791-bfe6-a80928e7c999"
15 | UnPack = "3a884ed6-31ef-47d7-9d2a-63182c4928ed"
16 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Data-parallel Julia
2 |
3 | This is the code repository of
4 | [A quick introduction to data parallelism in Julia](https://juliafolds.github.io/data-parallelism/tutorials/quick-introduction/).
5 |
--------------------------------------------------------------------------------
/bin/runtests.jl:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # -*- mode: julia -*-
3 | #=
4 | JULIA="${JULIA:-julia --color=yes --startup-file=no}"
5 | export JULIA_PROJECT="$(dirname "$(dirname "${BASH_SOURCE[0]}")")"
6 | export JULIA_NUM_THREADS=4
7 | export DISPLAY=
8 | export GKSwstype=nul
9 | exec ${JULIA} "${BASH_SOURCE[0]}" "$@"
10 | =#
11 | module TestDataParallelism
12 |
13 | using Test
14 | using Glob
15 |
16 | @testset "$(basename(file))" for file in sort(
17 | readdir(glob"*/*/test_*.jl", joinpath(@__DIR__, "../src/_assets/")),
18 | )
19 | include(file)
20 | end
21 |
22 | end # module TestDataParallelism
23 |
--------------------------------------------------------------------------------
/bin/serve:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # -*- mode: julia -*-
3 | #=
4 | JULIA="${JULIA:-julia --color=yes --startup-file=no}"
5 | export JULIA_PROJECT="$(dirname "$(dirname "${BASH_SOURCE[0]}")")"
6 | export DISPLAY=
7 | export GKSwstype=nul
8 | exec ${JULIA} "${BASH_SOURCE[0]}" "$@"
9 | =#
10 | cd(joinpath(dirname(@__DIR__), "src"))
11 | using Franklin
12 | serve(port=8643)
13 |
--------------------------------------------------------------------------------
/scripts/collatz.jl:
--------------------------------------------------------------------------------
1 | # Load functions related to Collatz examples
2 | include("../src/__site/assets/tutorials/quick-introduction/code/def_collatz.jl")
3 | include("../src/__site/assets/tutorials/quick-introduction/code/def_collatz_stopping_time.jl")
4 | include("../src/__site/assets/tutorials/quick-introduction/code/hist_collatz_stopping_time.jl")
5 |
--------------------------------------------------------------------------------
/scripts/false_sharing_demo.jl:
--------------------------------------------------------------------------------
1 | using BenchmarkTools
2 |
3 | function crowded_inc!(ys, data)
4 | Threads.@threads :static for indices in data
5 | for i in indices
6 | @inbounds ys[i] += 1
7 | end
8 | end
9 | end
10 |
11 | function exclusive_inc!(yss, data)
12 | Threads.@threads :static for indices in data
13 | ys = yss[Threads.threadid()]
14 | for i in indices
15 | @inbounds ys[i] += 1
16 | end
17 | end
18 | end
19 |
20 | cacheline = try
21 | parse(Int, read("/sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size", String))
22 | catch err
23 | @warn "cannot read cache line size" exception = (err, catch_backtrace())
24 | 64
25 | end
26 |
27 | ys = zeros(Threads.nthreads() * 2);
28 | partitioned_indices = reshape(eachindex(ys), Threads.nthreads(), :)'
29 | data = [rand(partitioned_indices[:, i], 2^20) for i in 1:Threads.nthreads()]
30 | yss = [zeros(length(ys) + cld(cacheline, sizeof(eltype(ys)))) for _ in 1:Threads.nthreads()];
31 |
32 |
33 | trial_crowded_inc = @benchmark crowded_inc!(ys, data) setup = fill!(ys, 0)
34 | display(trial_crowded_inc)
35 | trial_exclusive_inc =
36 | @benchmark exclusive_inc!(yss, data) setup = foreach(ys -> fill(ys, 0), yss)
37 | display(trial_exclusive_inc)
38 |
--------------------------------------------------------------------------------
/scripts/perf_c2c_demo.jl:
--------------------------------------------------------------------------------
1 | # A demo for false sharing and its analysis using `perf c2c`.
2 | #
3 | # This script runs two Julia functions that does equivalent computations with
4 | # different memory layouts. The performance counters are analyzed using `perf
5 | # c2c` and the result files are dumped into the current working directory.
6 | #
7 | # This script works probably only on Intel CPU.
8 |
9 | if !success(`perf c2c record -- --output=/dev/null true`)
10 | error("`perf c2c` not supported")
11 | end
12 |
13 | using BenchmarkTools
14 |
15 | function perf_c2c_record(f, output)
16 | # Compile `f` and also (hopefully) let CPUs converge to a stationary state.
17 | f()
18 |
19 | proc = run(
20 | pipeline(`perf c2c record -- --output=$output`; stdout = stdout, stderr = stderr);
21 | wait = false,
22 | )
23 | try
24 | f()
25 | finally
26 | flush(stdout)
27 | flush(stderr)
28 | kill(proc, Base.SIGINT)
29 | wait(proc)
30 | end
31 | end
32 |
33 | function perf_c2c_report(input, output)
34 | # `-c tid,iaddr` for showing the Tid column
35 | cmd = `perf c2c report --input=$input -c tid,iaddr`
36 | @info "$cmd > $output"
37 | open(output; write = true) do io
38 | run(pipeline(cmd; stdout = io))
39 | end
40 | end
41 |
42 | # SYS_gettid == 186 from /usr/include/x86_64-linux-gnu/asm/unistd_64.h
43 | gettid() = @ccall syscall(186::Clong;)::Clong
44 | @assert gettid() == getpid()
45 |
46 | function worker_tids()
47 | tids = zeros(Int, Threads.nthreads())
48 | Threads.@threads :static for _ in 1:Threads.nthreads()
49 | tids[Threads.threadid()] = gettid()
50 | end
51 | return tids
52 | end
53 |
54 | function crowded_inc!(ys, data)
55 | Threads.@threads :static for indices in data
56 | for i in indices
57 | @inbounds ys[i] += 1
58 | end
59 | end
60 | end
61 |
62 | function exclusive_inc!(yss, data)
63 | Threads.@threads :static for indices in data
64 | ys = yss[Threads.threadid()]
65 | for i in indices
66 | @inbounds ys[i] += 1
67 | end
68 | end
69 | end
70 |
71 | cacheline = try
72 | parse(Int, read("/sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size", String))
73 | catch err
74 | @warn "cannot read cache line size" exception = (err, catch_backtrace())
75 | 64
76 | end
77 |
78 | ys = zeros(Threads.nthreads() * 2);
79 | partitioned_indices = reshape(eachindex(ys), Threads.nthreads(), :)'
80 | data = [rand(partitioned_indices[:, i], 2^20) for i in 1:Threads.nthreads()]
81 | yss = [zeros(length(ys) + cld(cacheline, sizeof(eltype(ys)))) for _ in 1:Threads.nthreads()];
82 |
83 |
84 | @noinline bench_crowded_inc() = @btime crowded_inc!(ys, data)
85 | @noinline bench_exclusive_inc() = @btime exclusive_inc!(yss, data)
86 |
87 |
88 | @info "Benchmarking `crowded_inc!`"
89 | perf_c2c_record(bench_crowded_inc, "crowded_inc-perf.data")
90 |
91 | @info "Benchmarking `exclusive_inc!`"
92 | perf_c2c_record(bench_exclusive_inc, "exclusive_inc-perf.data")
93 |
94 |
95 | open("pointers.txt"; write = true) do io
96 | function ln(label, ptr::Ptr)
97 | print(io, label, ",\t")
98 | show(io, UInt(ptr))
99 | println(io)
100 | end
101 | ln("ys[1]", pointer(ys, 1))
102 | ln("ys[end]", pointer(ys, length(ys)))
103 | for (i, ys) in pairs(yss)
104 | ln("yss[$i][1]", pointer(ys, 1))
105 | ln("yss[$i][end]", pointer(ys, length(ys)))
106 | end
107 | end
108 |
109 | open("worker_tids.txt"; write = true) do io
110 | for tid in worker_tids()
111 | println(io, tid)
112 | end
113 | end
114 |
115 | perf_c2c_report("crowded_inc-perf.data", "crowded_inc-perf.txt")
116 | perf_c2c_report("exclusive_inc-perf.data", "exclusive_inc-perf.txt")
117 |
--------------------------------------------------------------------------------
/src/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .test-result
3 | __site/
4 | _rss
5 | franklin
6 | franklin.pub
7 | node_modules/
8 |
--------------------------------------------------------------------------------
/src/404.md:
--------------------------------------------------------------------------------
1 | # 404: File not found
2 |
3 | The requested file was not found.
4 |
5 | Please [click here](/) to go to the home page.
6 |
--------------------------------------------------------------------------------
/src/_assets/hamburger.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/_assets/juliafolds-logo.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JuliaFolds/data-parallelism/0d15d8878d814b3988dbf0a4434cc036c4e20a9b/src/_assets/juliafolds-logo.ico
--------------------------------------------------------------------------------
/src/_assets/rndimg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JuliaFolds/data-parallelism/0d15d8878d814b3988dbf0a4434cc036c4e20a9b/src/_assets/rndimg.jpg
--------------------------------------------------------------------------------
/src/_assets/scripts/generate_results.jl:
--------------------------------------------------------------------------------
1 | # Parent file to run all scripts which may generate
2 | # some output that you want to display on the website.
3 | # this can be used as a tester to check that all the code
4 | # on your website runs properly.
5 |
6 | dir = @__DIR__
7 |
8 | """
9 | genplain(s)
10 |
11 | Small helper function to run some code and redirect the output (stdout) to a file.
12 | """
13 | function genplain(s::String)
14 | open(joinpath(dir, "output", "$(splitext(s)[1]).txt"), "w") do outf
15 | redirect_stdout(outf) do
16 | include(joinpath(dir, s))
17 | end
18 | end
19 | end
20 |
21 | # output
22 |
23 | genplain("script1.jl")
24 |
25 | # plots
26 |
27 | include("script2.jl")
28 |
--------------------------------------------------------------------------------
/src/_assets/scripts/output/script1.out:
--------------------------------------------------------------------------------
1 | *---------1.3
2 | **--------1.3
3 | ***-------1.3
4 | ****------1.3
5 | *****-----1.3
6 |
--------------------------------------------------------------------------------
/src/_assets/scripts/output/script2.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
582 |
--------------------------------------------------------------------------------
/src/_assets/scripts/script1.jl:
--------------------------------------------------------------------------------
1 | using LinearAlgebra # HIDE
2 | using Random:seed! # HIDE
3 | seed!(0) # HIDE
4 | # HIDE
5 | x = randn(5)
6 | y = randn(5)
7 |
8 | for i in 1:5
9 | println(rpad("*"^i, 10, '-'), round(dot(x, y), digits=1))
10 | end
11 |
--------------------------------------------------------------------------------
/src/_assets/scripts/script2.jl:
--------------------------------------------------------------------------------
1 | using PyPlot
2 | x = range(0, stop=1, length=50)
3 | plot(x, sin.(2x).*exp.(-x/3))
4 | savefig(joinpath(@__DIR__, "output", "script2.svg"))
5 |
--------------------------------------------------------------------------------
/src/_assets/tutorials/mutations/accidental_mutations_1_threads.jl:
--------------------------------------------------------------------------------
1 | function f(n = 2^10)
2 | ys = zeros(Int, n)
3 | Threads.@threads for i in 1:n
4 | y = gcd(42, i)
5 | some_function()
6 | ys[i] = y
7 | end
8 |
9 | # Suppose that some unrelated code uses the same variable names as the
10 | # temporary variables in the parallel loop:
11 | if ys[1] > 0
12 | y = 1
13 | end
14 |
15 | return ys
16 | end
17 |
18 | # Some function that Julia does not inline:
19 | @noinline some_function() = _FALSE_[] && error("unreachable")
20 | const _FALSE_ = Ref(false)
21 |
--------------------------------------------------------------------------------
/src/_assets/tutorials/mutations/accidental_mutations_2_seq.jl:
--------------------------------------------------------------------------------
1 | f_seq(n = 2^10) = gcd.(42, 1:n)
2 |
--------------------------------------------------------------------------------
/src/_assets/tutorials/mutations/accidental_mutations_3_threads_fixed.jl:
--------------------------------------------------------------------------------
1 | function f_fixed(n = 2^10)
2 | ys = zeros(Int, n)
3 | Threads.@threads for i in 1:n
4 | local y = gcd(42, i)
5 | some_function()
6 | ys[i] = y
7 | end
8 |
9 | # Suppose that some unrelated code uses the same variable names as the
10 | # temporary variables in the parallel loop:
11 | if ys[1] > 0
12 | y = 1
13 | end
14 |
15 | return ys
16 | end
17 |
--------------------------------------------------------------------------------
/src/_assets/tutorials/mutations/accidental_mutations_4_floop.jl:
--------------------------------------------------------------------------------
1 | using FLoops
2 |
3 | function f_floop(n = 2^10)
4 | ys = zeros(Int, n)
5 | @floop ThreadedEx() for i in 1:n
6 | y = gcd(42, i)
7 | some_function()
8 | ys[i] = y
9 | end
10 |
11 | if ys[1] > 0
12 | y = 1
13 | end
14 |
15 | return ys
16 | end
17 |
--------------------------------------------------------------------------------
/src/_assets/tutorials/mutations/accidental_mutations_5_floop_fixed.jl:
--------------------------------------------------------------------------------
1 | using FLoops
2 |
3 | function f_floop_fixed(n = 2^10)
4 | ys = zeros(Int, n)
5 | @floop ThreadedEx() for i in 1:n
6 | local y = gcd(42, i)
7 | some_function()
8 | ys[i] = y
9 | end
10 |
11 | if ys[1] > 0
12 | y = 1
13 | end
14 |
15 | return ys
16 | end
17 |
--------------------------------------------------------------------------------
/src/_assets/tutorials/mutations/adjoining_trick_1.jl:
--------------------------------------------------------------------------------
1 | struct Init end
2 |
3 | function asmonoid(semigroup)
4 | monoid(a, b) = semigroup(a, b)
5 | monoid(::Init, b) = b
6 | monoid(a, ::Init) = a
7 | monoid(::Init, ::Init) = Init() # disambiguation
8 | return monoid
9 | end
10 |
11 | let ⊗ = asmonoid(min)
12 | @assert 1 ⊗ 2 == 1
13 | @assert 1 ⊗ Init() == 1
14 | @assert Init() ⊗ 2 == 2
15 | @assert Init() ⊗ Init() == Init()
16 | end
17 |
--------------------------------------------------------------------------------
/src/_assets/tutorials/mutations/adjoining_trick_2.jl:
--------------------------------------------------------------------------------
1 | function withinit(f, semigroup!)
2 | monoid!(a, b) = semigroup!(a, b)
3 | monoid!(::Init, b) = semigroup!(f(), b)
4 | monoid!(a, ::Init) = a
5 | monoid!(::Init, ::Init) = Init() # disambiguation
6 | return monoid!
7 | end
8 |
9 | let ⊗ = withinit(() -> [], append!)
10 | @assert [1] ⊗ [2] == [1, 2]
11 | @assert [1] ⊗ Init() == [1]
12 | @assert Init() ⊗ [2] == [2]
13 | @assert Init() ⊗ Init() == Init()
14 | end
15 |
16 | using Folds
17 | ys = Folds.mapreduce(tuple, withinit(() -> Int[], append!), 1:10; init = Init())
18 |
19 | @assert ys == 1:10
20 |
--------------------------------------------------------------------------------
/src/_assets/tutorials/mutations/combining_containers_1.jl:
--------------------------------------------------------------------------------
1 | using Folds
2 | using Transducers: OnInit
3 | ys1 = Folds.mapreduce(x -> [x], append!, 1:10; init = OnInit(() -> Int[]))
4 | ys2 = Folds.mapreduce(x -> Set([x]), union!, 1:10; init = OnInit(Set{Int}))
5 | ys3 = Folds.mapreduce(x -> Dict(x => x^2), merge!, 1:10; init = OnInit(Dict{Int,Int}))
6 | ys4 = Folds.mapreduce(x -> Dict(isodd(x) => 1), mergewith!(+), 1:10; init = OnInit(Dict{Bool,Int}))
7 |
8 | @assert ys1 == 1:10
9 | @assert ys2 == Set(1:10)
10 | @assert ys3 == Dict(x => x^2 for x in 1:10)
11 | @assert ys4 == Dict(false => 5, true => 5)
12 |
--------------------------------------------------------------------------------
/src/_assets/tutorials/mutations/combining_containers_2.jl:
--------------------------------------------------------------------------------
1 | using Folds
2 | using MicroCollections
3 | using BangBang
4 | ys1 = Folds.mapreduce(x -> SingletonVector((x,)), append!!, 1:10)
5 | ys2 = Folds.mapreduce(x -> SingletonSet((x,)), union!!, 1:10)
6 | ys3 = Folds.mapreduce(x -> SingletonDict(x => x^2), merge!!, 1:10)
7 | ys4 = Folds.mapreduce(x -> SingletonDict(isodd(x) => 1), mergewith!!(+), 1:10)
8 |
--------------------------------------------------------------------------------
/src/_assets/tutorials/mutations/combining_containers_3.jl:
--------------------------------------------------------------------------------
1 | using Folds
2 | ys1 = Folds.collect(1:10)
3 | ys2 = Folds.set(1:10)
4 | ys3 = Folds.dict(x => x^2 for x in 1:10)
5 |
--------------------------------------------------------------------------------
/src/_assets/tutorials/mutations/example_1.jl:
--------------------------------------------------------------------------------
1 | N = 40
2 | M = 1000
3 | As = [randn(N, N) for _ in 1:M]
4 | Bs = [randn(N, N) for _ in 1:M]
5 | sum(A * B for (A, B) in zip(As, Bs))
6 |
--------------------------------------------------------------------------------
/src/_assets/tutorials/mutations/example_2.jl:
--------------------------------------------------------------------------------
1 | using Folds
2 | Folds.sum(A * B for (A, B) in zip(As, Bs))
3 |
--------------------------------------------------------------------------------
/src/_assets/tutorials/mutations/example_3.jl:
--------------------------------------------------------------------------------
1 | using FLoops
2 |
3 | @floop for (A, B) in zip(As, Bs)
4 | C = A * B # allocation for each iteration
5 | @reduce() do (S = zero(C); C)
6 | S = S + C # allocation for each iteration
7 | end
8 | end
9 |
--------------------------------------------------------------------------------
/src/_assets/tutorials/mutations/example_4.jl:
--------------------------------------------------------------------------------
1 | using LinearAlgebra: mul!
2 |
3 | @floop for (A, B) in zip(As, Bs)
4 | @init C = similar(A)
5 | mul!(C, A, B)
6 | @reduce() do (S = zero(C); C)
7 | S .+= C
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/src/_assets/tutorials/mutations/example_5.jl:
--------------------------------------------------------------------------------
1 | using FLoops
2 | using LinearAlgebra: mul!
3 |
4 | @floop for (A, B) in zip(As, Bs)
5 | C = (A, B)
6 | @reduce() do (S = zero(A); C)
7 | if C isa Tuple # base case
8 | mul!(S, C[1], C[2], 1, 1)
9 | else # combining base cases
10 | S .+= C
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/src/_assets/tutorials/mutations/filling_1.jl:
--------------------------------------------------------------------------------
1 | using FLoops
2 |
3 | xs = 1:2:100
4 | ys = similar(xs) # output
5 | @floop ThreadedEx() for (i, x) in pairs(xs)
6 | @inbounds ys[i] = gcd(42, x)
7 | end
8 |
--------------------------------------------------------------------------------
/src/_assets/tutorials/mutations/filling_2.jl:
--------------------------------------------------------------------------------
1 | xs = 1:2:100
2 | ys = similar(xs) # output
3 | Threads.@threads for i in eachindex(xs, ys)
4 | @inbounds ys[i] = gcd(42, xs[i])
5 | end
6 |
--------------------------------------------------------------------------------
/src/_assets/tutorials/mutations/filling_3.jl:
--------------------------------------------------------------------------------
1 | using Folds
2 |
3 | xs = 1:2:100
4 | ys = similar(xs) # output
5 | Folds.map!(x -> gcd(42, x), ys, xs)
6 |
--------------------------------------------------------------------------------
/src/_assets/tutorials/mutations/floops_init_1.jl:
--------------------------------------------------------------------------------
1 | using FLoops
2 |
3 | @floop for x in 1:10
4 | @init xs = Vector{Int}(undef, 3)
5 | xs .= (x, 2x, 3x)
6 | @reduce() do (ys = zeros(Int, 3); xs)
7 | ys .+= xs
8 | end
9 | end
10 |
11 | @assert ys == mapreduce(x -> [x, 2x, 3x], .+, 1:10)
12 |
--------------------------------------------------------------------------------
/src/_assets/tutorials/mutations/floops_reduce_1.jl:
--------------------------------------------------------------------------------
1 | using FLoops
2 |
3 | @floop for x in 1:10
4 | if isodd(x)
5 | @reduce(odds = append!(Int[], (x,)))
6 | else
7 | @reduce(evens = append!(Int[], (x,)))
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/src/_assets/tutorials/mutations/floops_reduce_2.jl:
--------------------------------------------------------------------------------
1 | function basecase(chunk)
2 | odds = Int[] # init
3 | evens = Int[] # init
4 | for x in chunk
5 | if isodd(x)
6 | odds = append!(odds, (x,))
7 | else
8 | evens = append!(evens, (x,))
9 | end
10 | end
11 | return (odds, evens)
12 | end
13 |
--------------------------------------------------------------------------------
/src/_assets/tutorials/mutations/floops_reduce_3.jl:
--------------------------------------------------------------------------------
1 | chunk_left = 1:5
2 | chunk_right = 6:10
3 | @assert vcat(chunk_left, chunk_right) == 1:10 # original input
4 |
--------------------------------------------------------------------------------
/src/_assets/tutorials/mutations/floops_reduce_4.jl:
--------------------------------------------------------------------------------
1 | odds_left, evens_left = basecase(chunk_left)
2 | odds_right, evens_right = basecase(chunk_right)
3 |
4 | @assert odds_left == 1:2:5
5 | @assert evens_left == 2:2:5
6 | @assert odds_right == 7:2:10
7 | @assert evens_right == 6:2:10
8 |
--------------------------------------------------------------------------------
/src/_assets/tutorials/mutations/floops_reduce_5.jl:
--------------------------------------------------------------------------------
1 | odds = append!(odds_left, odds_right)
2 | evens = append!(evens_left, evens_right)
3 |
4 | @assert odds == 1:2:10
5 | @assert evens == 2:2:10
6 |
--------------------------------------------------------------------------------
/src/_assets/tutorials/mutations/floops_reduce_6_vcat.jl:
--------------------------------------------------------------------------------
1 | using FLoops
2 |
3 | @floop for x in 1:10
4 | if isodd(x)
5 | @reduce(odds = vcat(Int[], [x]))
6 | else
7 | @reduce(evens = vcat(Int[], [x]))
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/src/_assets/tutorials/mutations/floops_reduce_do_1.jl:
--------------------------------------------------------------------------------
1 | @floop for n in 1:10
2 | xs = [n, 2n, 3n]
3 | @reduce() do (ys = zeros(Int, 3); xs)
4 | # ~~~~~~~~~~~~~~~~~~
5 | # initializer
6 | ys .+= xs
7 | # ~~~~~~~~~
8 | # reduce body
9 | end
10 | end
11 |
12 | @assert ys == mapreduce(n -> [n, 2n, 3n], .+, 1:10)
13 |
--------------------------------------------------------------------------------
/src/_assets/tutorials/mutations/floops_reduce_do_2.jl:
--------------------------------------------------------------------------------
1 | function basecase(chunk)
2 | ys = zeros(Int, 3) # initializer
3 | for n in chunk
4 | xs = [n, 2n, 3n]
5 | ys .+= xs # reduce body
6 | end
7 | return ys
8 | end
9 |
--------------------------------------------------------------------------------
/src/_assets/tutorials/mutations/floops_reduce_do_3.jl:
--------------------------------------------------------------------------------
1 | chunk_left = 1:5
2 | chunk_right = 6:10
3 |
4 | ys_left = basecase(chunk_left)
5 | ys_right = basecase(chunk_right)
6 |
--------------------------------------------------------------------------------
/src/_assets/tutorials/mutations/floops_reduce_do_4.jl:
--------------------------------------------------------------------------------
1 | ys_left .+= ys_right
2 | ys = ys_left
3 |
4 | @assert ys == mapreduce(n -> [n, 2n, 3n], .+, 1:10)
5 |
--------------------------------------------------------------------------------
/src/_assets/tutorials/mutations/floops_reduce_do_correct.jl:
--------------------------------------------------------------------------------
1 | @floop for n in 1:10
2 | xs = [n, 2n, 3n]
3 | zs = 2 .* xs # CORRECT
4 | @reduce() do (ys = zeros(Int, 3); zs)
5 | ys .+= zs
6 | end
7 | end
8 |
9 | @assert ys == mapreduce(n -> 2 .* [n, 2n, 3n], .+, 1:10)
10 |
--------------------------------------------------------------------------------
/src/_assets/tutorials/mutations/floops_reduce_do_incorrect.jl:
--------------------------------------------------------------------------------
1 | @floop for n in 1:10
2 | xs = [n, 2n, 3n]
3 | @reduce() do (ys = zeros(Int, 3); xs)
4 | ys .+= 2 .* xs # INCORRECT
5 | end
6 | end
7 |
--------------------------------------------------------------------------------
/src/_assets/tutorials/mutations/floops_reduce_do_os_1.jl:
--------------------------------------------------------------------------------
1 | using FLoops
2 | using OnlineStats
3 |
4 | @floop for x in 1:10
5 | y = 2x
6 | m = fit!(Mean(), y)
7 | @reduce() do (acc = Mean(); m)
8 | merge!(acc, m)
9 | end
10 | end
11 |
12 | @assert acc == fit!(Mean(), 2:2:20)
13 |
--------------------------------------------------------------------------------
/src/_assets/tutorials/mutations/floops_reduce_do_os_2.jl:
--------------------------------------------------------------------------------
1 | @floop for x in 1:10
2 | y = 2x
3 | @reduce() do (acc = Mean(); y)
4 | if y isa OnlineStat
5 | merge!(acc, y)
6 | else
7 | fit!(acc, y)
8 | end
9 | end
10 | end
11 |
12 | @assert acc == fit!(Mean(), 2:2:20)
13 |
--------------------------------------------------------------------------------
/src/_assets/tutorials/mutations/ownership_passing_style_second_1_intro.jl:
--------------------------------------------------------------------------------
1 | vectors = Any[[n n^2; n^3 n^4] for n in 1:100]
2 | sum(vectors)
3 |
--------------------------------------------------------------------------------
/src/_assets/tutorials/mutations/ownership_passing_style_second_2_wrong.jl:
--------------------------------------------------------------------------------
1 | vectors = Any[[n n^2; n^3 n^4] for n in 1:100]
2 | @floop for xs in vectors
3 | @reduce() do (ys = nothing; xs)
4 | if ys === nothing
5 | ys = xs # ❌ WRONG
6 | else
7 | ys .+= xs
8 | end
9 | end
10 | end
11 |
12 | @assert ys === vectors[1] # above loop mutated the input
13 |
--------------------------------------------------------------------------------
/src/_assets/tutorials/mutations/ownership_passing_style_second_3_correct.jl:
--------------------------------------------------------------------------------
1 | vectors = Any[[n n^2; n^3 n^4] for n in 1:100]
2 | @floop for xs in vectors
3 | @reduce() do (ys = nothing; xs)
4 | if ys === nothing
5 | ys = copy(xs) # ✅ CORRECT
6 | else
7 | ys .+= xs
8 | end
9 | end
10 | end
11 |
12 | @assert ys !== vectors[1] # input not mutated
13 |
--------------------------------------------------------------------------------
/src/_assets/tutorials/mutations/test_mutations.jl:
--------------------------------------------------------------------------------
1 | module TestMutations
2 |
3 | using Test
4 |
5 | module Example
6 | S1 = include("example_1.jl")
7 | S2 = include("example_2.jl")
8 | include("example_3.jl")
9 | S3 = S
10 | S = nothing
11 | include("example_4.jl")
12 | S4 = S
13 | S = nothing
14 | include("example_5.jl")
15 | S5 = S
16 | end # module Example
17 |
18 | @testset "Example" begin
19 | @test Example.S2 ≈ Example.S1
20 | @test Example.S3 ≈ Example.S1
21 | @test Example.S4 ≈ Example.S1
22 | @test Example.S5 ≈ Example.S1
23 | end
24 |
25 | module Filling
26 | include("filling_1.jl")
27 | ys1 = ys
28 | ys = nothing
29 | include("filling_2.jl")
30 | ys2 = ys
31 | ys = nothing
32 | ys3 = include("filling_3.jl")
33 | end
34 |
35 | @testset "Filling" begin
36 | @test Filling.ys1 == gcd.(42, 1:2:100)
37 | @test Filling.ys2 == gcd.(42, 1:2:100)
38 | @test Filling.ys3 == gcd.(42, 1:2:100)
39 | end
40 |
41 | module FLoopsReduce
42 | using Test: @test as @assert
43 | include("floops_reduce_1.jl")
44 | include("floops_reduce_2.jl")
45 | include("floops_reduce_3.jl")
46 | include("floops_reduce_4.jl")
47 | include("floops_reduce_5.jl")
48 | end
49 |
50 | module FLoopsReduce6
51 | include("floops_reduce_6_vcat.jl")
52 | end
53 |
54 | @testset "floops_reduce_6_vcat.jl" begin
55 | @test FLoopsReduce6.odds == 1:2:10
56 | @test FLoopsReduce6.evens == 2:2:10
57 | end
58 |
59 | module FLoopsReduceDo
60 | using Test: @test as @assert
61 | using FLoops
62 | include("floops_reduce_do_1.jl")
63 | include("floops_reduce_do_2.jl")
64 | include("floops_reduce_do_3.jl")
65 | include("floops_reduce_do_4.jl")
66 | end
67 |
68 | module FLoopsReduceDoOnlineStats
69 | using Test: @test as @assert
70 | include("floops_reduce_do_os_1.jl")
71 | acc = nothing
72 | include("floops_reduce_do_os_2.jl")
73 | end
74 |
75 | module FLoopsReduceDo2
76 | using Test: @test as @assert
77 | using FLoops
78 | include("floops_reduce_do_incorrect.jl")
79 | include("floops_reduce_do_correct.jl")
80 | end
81 |
82 | module OwnershipPassingStyleSecond1
83 | include("ownership_passing_style_second_1_intro.jl")
84 | end
85 |
86 | module OwnershipPassingStyleSecond2
87 | using Test: @test as @assert
88 | using FLoops
89 | include("ownership_passing_style_second_2_wrong.jl")
90 | end
91 |
92 | module OwnershipPassingStyleSecond3
93 | using Test: @test as @assert
94 | using FLoops
95 | include("ownership_passing_style_second_3_correct.jl")
96 | end
97 |
98 | module TransducersOnInit
99 | using Test: @test as @assert
100 | include("transducers_oninit.jl")
101 | end
102 |
103 | module CombiningContainers1
104 | using Test: @test as @assert
105 | include("combining_containers_1.jl")
106 | end
107 |
108 | module CombiningContainers2
109 | include("combining_containers_2.jl")
110 | end
111 |
112 | module CombiningContainers3
113 | include("combining_containers_3.jl")
114 | end
115 |
116 | @testset "CombiningContainers" begin
117 | @test CombiningContainers2.ys1 == CombiningContainers1.ys1
118 | @test CombiningContainers2.ys2 == CombiningContainers1.ys2
119 | @test CombiningContainers2.ys3 == CombiningContainers1.ys3
120 | @test CombiningContainers2.ys4 == CombiningContainers1.ys4
121 | @test CombiningContainers3.ys1 == CombiningContainers1.ys1
122 | @test CombiningContainers3.ys2 == CombiningContainers1.ys2
123 | @test CombiningContainers3.ys3 == CombiningContainers1.ys3
124 | end
125 |
126 | module AccidentalMutations
127 | include("accidental_mutations_1_threads.jl")
128 | include("accidental_mutations_2_seq.jl")
129 | include("accidental_mutations_3_threads_fixed.jl")
130 | include("accidental_mutations_4_floop.jl")
131 | include("accidental_mutations_5_floop_fixed.jl")
132 | end
133 |
134 | @testset "AccidentalMutations" begin
135 | @test AccidentalMutations.f() isa Any
136 | @test AccidentalMutations.f_fixed() == AccidentalMutations.f_seq()
137 | @test AccidentalMutations.f_floop_fixed() == AccidentalMutations.f_seq()
138 |
139 | local err
140 | @test (err = try
141 | AccidentalMutations.f_floop()
142 | nothing
143 | catch err_
144 | err_
145 | end) isa Exception
146 | @test occursin("has 1 boxed variable", sprint(showerror, err))
147 | end
148 |
149 | module FLoopsInit
150 | using Test: @test as @assert
151 | include("floops_init_1.jl")
152 | end
153 |
154 | module AdjoiningTrick
155 | using Test: @test as @assert
156 | include("adjoining_trick_1.jl")
157 | include("adjoining_trick_2.jl")
158 | end
159 |
160 | end # module
161 |
--------------------------------------------------------------------------------
/src/_assets/tutorials/mutations/transducers_oninit.jl:
--------------------------------------------------------------------------------
1 | using Folds
2 | using Transducers: OnInit
3 | ys = Folds.mapreduce(x -> (x, 2x, 3x), .+, 1:10; init = OnInit(() -> [0, 0, 0]))
4 |
5 | @assert ys == [sum(1:10), 2sum(1:10), 3sum(1:10)]
6 |
--------------------------------------------------------------------------------
/src/_css/basic.css:
--------------------------------------------------------------------------------
1 | /* ==================================================================
2 | Header and Nav
3 | ================================================================== */
4 |
5 | nav {
6 | width: 64%;
7 | display: inline-block; }
8 |
9 | nav ul {
10 | padding-left: 0;
11 | margin-top: 0;
12 | margin-bottom: 0; }
13 |
14 | nav li {
15 | display: inline-block; }
16 |
17 | nav li a {
18 | color: #004de6;
19 | text-decoration: none;
20 | font-size: 18px;
21 | font-weight: bold;
22 | display: inline-block;
23 | float: center;
24 | padding-top: 10px;
25 | padding-right:2px;
26 | padding-left:2px;
27 | padding-bottom:5px;
28 | margin-left:7px;
29 | margin-right:7px;
30 | border-bottom: 2px solid #4C9CF1;
31 | transition: color 0.3s ease; }
32 |
33 | header {
34 | text-align: right;
35 | padding-left: 30px;
36 | padding-right: 30px;
37 | margin-top: 50px;
38 | margin-bottom: 50px;
39 | display: flex;
40 | align-items: center; }
41 |
42 | header .blog-name {
43 | width: 35%;
44 | display: inline-block;
45 | text-align: left;
46 | font-size: 18px;
47 | font-family: "Lucida Console", Monaco, monospace;
48 | padding-top: 10px;}
49 |
50 | header .blog-name a {
51 | color: #a6a2a0;
52 | text-decoration: none; }
53 |
54 | header li a:hover {
55 | color: black;
56 | border-bottom: 2px solid black; }
57 |
58 |
59 | #menu-icon {
60 | display: none; }
61 |
62 | .github-edit-small {
63 | display: none; }
64 |
65 | @media (max-width: 480px) {
66 | header {
67 | padding-left: 6%;
68 | padding-right:6%;
69 | }
70 | }
71 |
72 | /* wide display: enforce maximum width of header to match content */
73 | @media (min-width: 940px) {
74 | header {
75 | margin-left: auto;
76 | margin-right: auto; }
77 | }
78 |
79 | /*
80 | medium display: nav goes under name
81 | */
82 | @media (max-width: 760px) {
83 | header { display: block; }
84 | header .blog-name {
85 | display: block;
86 | width: 100%;
87 | padding-bottom: 10px; }
88 | nav {
89 | width: 100%; }
90 | }
91 |
92 | /*
93 | narrow display: collapse the header (don't show the menu items)
94 | instead, display a burger menu.
95 | */
96 | @media (max-width: 500px) {
97 | header {
98 | height: 35px;
99 | display: flex;
100 | align-items: center; }
101 | header .blog-name {
102 | display: inline-block;
103 | width: 70%; }
104 | nav {
105 | display: inline-block;
106 | width: 27%; }
107 | nav ul, nav:active ul {
108 | display: none;
109 | position: absolute;
110 | /* padding: 20px; */
111 | background: #fff;
112 | border: 1px solid #444;
113 | right: 50px;
114 | top: 60px;
115 | width: 30%;
116 | border-radius: 4px 0 4px 4px;
117 | z-index: 1;}
118 | nav li {
119 | text-align: left;
120 | display: block;
121 | padding: 0;
122 | margin: 0; }
123 | header li a { border-bottom: none; }
124 | header li a:hover { border-bottom: none; }
125 | nav:hover ul{
126 | display: block; }
127 | #menu-icon {
128 | display: inline-block;
129 | margin-right: 10px;
130 | margin-top: 5px; }
131 | .github-edit-big {
132 | display: none; }
133 | .github-edit-small {
134 | display: inline-block; }
135 | }
136 |
137 | table {
138 | line-height:1em;
139 | margin-left:auto;
140 | margin-right:auto;
141 | border-collapse:collapse;
142 | text-align:center;
143 | margin-bottom:1.5em
144 | }
145 | tr:first-of-type {
146 | background:#eae9f4
147 | }
148 | tr:first-of-type>th {
149 | text-align:center
150 | }
151 | tr,
152 | th,
153 | td {
154 | padding:10px;
155 | border:1px solid lightgray
156 | }
157 | table tbody tr td {
158 | border:1px solid lightgray
159 | }
160 |
--------------------------------------------------------------------------------
/src/_css/franklin.css:
--------------------------------------------------------------------------------
1 | /* ==================================================================
2 | DEFAULT FONT AND LAYOUT
3 | ================================================================== */
4 |
5 | html {
6 | font-family: Helvetica, Arial, sans-serif;
7 | font-size : 17px;
8 | color: #1c1c1c;
9 | }
10 |
11 | /* ==================================================================
12 | BASIC GRID FOR PROFILE PIC
13 | ================================================================== */
14 |
15 | .franklin-content .row {
16 | display: block; }
17 |
18 | .franklin-content .left {
19 | float: left;
20 | margin-right: 15px; }
21 |
22 | .franklin-content .right {
23 | float: right; }
24 |
25 | .franklin-content .container img {
26 | width: auto;
27 | padding-left: 0;
28 | border-radius: 10px; }
29 |
30 | .franklin-content .footnote {
31 | position: relative;
32 | top: -0.5em;
33 | font-size: 70%;
34 | }
35 |
36 | /* ==================================================================
37 | FOOT / COPYRIGHT
38 | ================================================================== */
39 |
40 | .franklin-content .page-foot a {
41 | text-decoration: none;
42 | color: #a6a2a0;
43 | text-decoration: underline; }
44 |
45 | .page-foot {
46 | font-size: 80%;
47 | font-family: Arial, serif;
48 | color: #a6a2a0;
49 | text-align: center;
50 | margin-top: 6em;
51 | border-top: 1px solid lightgrey;
52 | padding-top: 2em;
53 | margin-bottom: 4em; }
54 |
55 | /* ==================================================================
56 | TEXT GEOMETRY
57 | ================================================================== */
58 |
59 | .franklin-content {
60 | position: relative;
61 | padding-left: 30px;
62 | padding-right: 30px;
63 | line-height: 1.35em; }
64 |
65 | /* on wide screens, fix content width to a max value */
66 | @media (min-width: 940px) {
67 | .franklin-content {
68 | margin-left: auto;
69 | margin-right: auto; }
70 | }
71 |
72 | /* on narrow device, reduce margins */
73 | @media (max-width: 480px) {
74 | .franklin-content {
75 | padding-left: 6%;
76 | padding-right: 6%; }
77 | }
78 |
79 | /* ==================================================================
80 | TITLES
81 | ================================================================== */
82 |
83 | .franklin-content h1 { font-size: 24px; }
84 | .franklin-content h2 { font-size: 22px; }
85 | .franklin-content h3 { font-size: 20px; }
86 |
87 | .franklin-content h1, h2, h3, h4, h5, h6 {
88 | text-align: left; }
89 |
90 | .franklin-content h1 {
91 | padding-bottom: 0.5em;
92 | border-bottom: 3px double lightgrey;
93 | margin-top: 1.5em;
94 | margin-bottom: 1em; }
95 |
96 | .franklin-content h2 {
97 | padding-bottom: 0.3em;
98 | border-bottom: 1px solid lightgrey;
99 | margin-top: 2em;
100 | margin-bottom: 1em; }
101 |
102 | .franklin-content h1 a { color: inherit; }
103 | .franklin-content h1 a:hover {text-decoration: none;}
104 | .franklin-content h2 a { color: inherit; }
105 | .franklin-content h2 a:hover {text-decoration: none;}
106 | .franklin-content h3 a { color: inherit; }
107 | .franklin-content h3 a:hover {text-decoration: none;}
108 | .franklin-content h4 a { color: inherit; }
109 | .franklin-content h4 a:hover {text-decoration: none;}
110 | .franklin-content h5 a { color: inherit; }
111 | .franklin-content h5 a:hover {text-decoration: none;}
112 | .franklin-content h6 a { color: inherit; }
113 | .franklin-content h6 a:hover {text-decoration: none;}
114 |
115 | .franklin-content table {
116 | margin-left: auto;
117 | margin-right: auto;
118 | border-collapse: collapse;
119 | text-align: center;}
120 | .franklin-content th, td{
121 | padding: 10px;
122 | border: 1px solid black;}
123 |
124 | .franklin-content blockquote {
125 | background: #eeeeee;
126 | border-left: 7px solid #a8a8a8;
127 | margin: 1.5em 10px;
128 | padding: 0.5em 10px;
129 | font-style: italic;}
130 |
131 | .franklin-content blockquote p {
132 | display: inline; }
133 |
134 | /* ==================================================================
135 | GENERAL FORMATTING
136 | ================================================================== */
137 |
138 | /* spacing between bullet points */
139 | .franklin-content li p {
140 | margin: 10px 0; }
141 |
142 | .franklin-content a {
143 | color: #004de6;
144 | text-decoration: none; }
145 |
146 | .franklin-content a:hover {
147 | text-decoration: underline; }
148 |
149 | /* ==================================================================
150 | HYPERREFS AND FOOTNOTES
151 | ================================================================== */
152 |
153 | .franklin-content .eqref a { color: green; }
154 | .franklin-content .bibref a { color: green; }
155 |
156 | .franklin-content sup {
157 | font-size: 70%;
158 | vertical-align: super;
159 | line-height: 0; }
160 |
161 | .franklin-content table.fndef {
162 | margin: 0;
163 | margin-bottom: 10px;}
164 | .franklin-content .fndef tr, td {
165 | padding: 0;
166 | border: 0;
167 | text-align: left;}
168 | .franklin-content .fndef tr {
169 | border-left: 2px solid lightgray;
170 | }
171 | .franklin-content .fndef td.fndef-backref {
172 | vertical-align: top;
173 | font-size: 70%;
174 | padding-left: 5px;}
175 | .franklin-content .fndef td.fndef-content {
176 | font-size: 80%;
177 | padding-left: 10px;}
178 |
179 | /* ==================================================================
180 | IMAGES in CONTENT
181 | ================================================================== */
182 |
183 | .franklin-content img {
184 | max-width: 70%;
185 | text-align: center;
186 | padding-left: 10%; }
187 |
188 | .franklin-content .img-small img {
189 | max-width: 50%;
190 | text-align: center;
191 | padding-left: 20%; }
192 |
193 | /* ==================================================================
194 | IMAGES in TABLE
195 | ================================================================== */
196 |
197 | .franklin-content table img {
198 | max-width: 100%;
199 | text-align: left;
200 | padding-left: 0; }
201 |
202 | /* ==================================================================
203 | KATEX
204 | ================================================================== */
205 |
206 | /* Franklin-specify setups are removed. */
207 |
208 | /* ==================================================================
209 | CODE & HIGHLIGHT.JS
210 | ================================================================== */
211 |
212 | code {
213 | background-color: rgba(27,31,35,0.05);
214 | padding: 0.1em 0.2em;
215 | border-radius: 2px;
216 | font-size: 90%;
217 | }
218 |
219 | /* .franklin-content code { */
220 | /* background-color: rgba(27,31,35,0.05); */
221 | /* padding: 0.1em 0.2em; */
222 | /* border-radius: 2px; */
223 | /* font-size: 90%; } */
224 |
225 | .hljs {
226 | font-size: 90%;
227 | line-height: 1.35em;
228 | border-radius: 10px; }
229 |
230 | .hljs-meta, .hljs-metas, .hljs-metap { font-weight: bold; }
231 |
232 | .hljs-meta { color: rgb(25, 179, 51); }
233 |
234 | .hljs-metas { color: red; }
235 |
236 | .hljs-metap { color: rgb(51, 131, 231); }
237 |
238 | /* ==================================================================
239 | BOXES
240 | ================================================================== */
241 |
242 | .franklin-content .colbox-blue {
243 | background-color: #EEF3F5;
244 | padding-top: 5px;
245 | padding-right: 10px;
246 | padding-left: 10px;
247 | padding-bottom: 5px;
248 | margin-left: 5px;
249 | margin-top: 5px;
250 | margin-bottom: 5px;
251 | border-radius: 0 10px 10px 0;
252 | border-left: 5px solid #4C9CF1; }
253 |
254 | /* ==================================================================
255 | ADMONITION
256 | ================================================================== */
257 |
258 | /* Taken from:
259 | https://github.com/tlienart/Franklin.jl/blob/v0.10.3/docs/_css/jtd.css#L674-L696
260 | */
261 |
262 | .note, .warn {
263 | margin-top: 1.5em;
264 | width: 95%;
265 | margin-left:auto;
266 | margin-right:auto;
267 | border-bottom-left-radius: 5px;
268 | border-bottom-right-radius: 5px;
269 | margin-bottom: 1em; }
270 | .note .content,
271 | .warn .content {
272 | padding: 10px;
273 | padding-left: 12px; }
274 | .note .title,
275 | .warn .title {
276 | font-size: 105%;
277 | border-top-left-radius: 5px;
278 | border-top-right-radius: 5px;
279 | padding-left: 7px;
280 | padding-top: 2px; }
281 |
282 | .note {
283 | background-color: aliceblue; }
284 | .note .title {
285 | color: white;
286 | background: cornflowerblue; }
287 |
288 | .warn {
289 | background-color: #f7e2e2; }
290 | .warn .title {
291 | color: white;
292 | background: #af4545; }
293 |
294 | /* ==================================================================
295 | TESTING
296 | ================================================================== */
297 |
298 | .test {
299 | display: none; }
300 |
301 | .test_code > pre,
302 | .test_ok > pre,
303 | .test_failure > pre {
304 | margin-top: 0px; }
305 |
306 | .test_code > pre {
307 | margin-bottom: 5px; }
308 |
309 | .test_code > .title,
310 | .test_ok > .title,
311 | .test_failure > .title {
312 | font-family: monospace;
313 | font-size: 14px;
314 | font-weight: bold; }
315 |
316 | .test_ok > .title {
317 | color: green; }
318 |
319 | .test_failure > .title {
320 | color: red; }
321 |
322 | .test_code .hljs {
323 | background-color: lemonchiffon; }
324 |
325 | .test_ok .hljs {
326 | background-color: palegreen; }
327 |
328 | .test_failure .hljs {
329 | background-color: salmon; }
330 |
331 | /* ==================================================================
332 | MISC
333 | ================================================================== */
334 |
335 | kbd {
336 | display: inline-block;
337 | padding: 3px 5px;
338 | font: 15px SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;
339 | line-height: 16px;
340 | color: #444d56;
341 | vertical-align: middle;
342 | background-color: #fafbfc;
343 | border: 1px solid #d1d5da;
344 | border-radius: 6px;
345 | box-shadow: inset 0 -1px 0 #d1d5da; }
346 |
--------------------------------------------------------------------------------
/src/_layout/foot.html:
--------------------------------------------------------------------------------
1 |
2 | {{ if hasmath }}
3 | {{ insert foot_katex.html }}
4 | {{ end }}
5 | {{ if hascode }}
6 | {{ insert foot_highlight.html }}
7 | {{ end }}
8 |
18 |