35

The Julia Challenge Who Will Win?

 6 years ago
source link: https://www.tuicool.com/articles/hit/vu6NvqJ
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

{"~:article":{"~:root":"88abac49-0430-44de-b93d-3b075faadba5","~:nodes":{"05f5e5f2-3afb-48e9-a6e1-d9becac5be65":{"~:id":"05f5e5f2-3afb-48e9-a6e1-d9becac5be65","~:kind":"text","~:content":"

What makes it difficult to implement is that you can really throw anything at it! It works for n-arguments, n-dimensional arrays with arbitrary user types and functions - and pretty much needs to have optimal performance in all cases.

"},"528cdae1-335b-4f9c-afb7-c3506c029eb8":{"~:content":"using BenchmarkTools\n\na = rand(Point3, 10^6)\nb = rand(Point3, 10^6)\nout = fill(0f0, 10^6)\n\n@btime $out .= super_custom_func.($a, $b)\nbr = @broadcast super_custom_func(a, b)\n@btime materialize!($out, $br)\nnothing","~:refs":{"~#list":[]},"~:output-log-lines":{"~:stdout":2},"~:language":"julia","~:id":"528cdae1-335b-4f9c-afb7-c3506c029eb8","~:compute-ref":"~ue25cd8b0-b673-11e8-84f6-feb93b1b86dd","~:runtime":["~:runtime","7437c28d-5ffa-4b39-92a7-04864a9ae2b3"],"~:kind":"code","~:outputs":{},"~:error":null,"~:exec-duration":27059,"~:bucket":null},"40b24e11-8492-4713-89a2-5a0688fbe050":{"~:id":"40b24e11-8492-4713-89a2-5a0688fbe050","~:kind":"section","~:title":"Winning Criteria For Implementation","~:content":{"~#list":[]},"~:sections":{"~#list":["df68b751-1ad5-497c-80ad-4263580ab101","32d08f8d-d898-4e2f-aa1a-7d496d85318c","90480d64-f52f-4ce1-b17f-8c8e57ad3ac5","3e611adf-b54d-4671-927d-ae4fd721072a"]}},"df68b751-1ad5-497c-80ad-4263580ab101":{"~:id":"df68b751-1ad5-497c-80ad-4263580ab101","~:kind":"section","~:title":"Solution","~:content":{"~#list":["835904a2-26a1-43dc-a0fa-f03ff58ce7c0"]}},"d81d8319-9278-4cbf-b86f-3e869807564c":{"~:content":"x = fill(0.0, (4, 4)) # 4x4 array of zeros\ny = [1, 2, 3, 4] # 4 element vector\nz = 2 # a scalar\n# a user defined function, let's not make it complicated\nuser_defined(x) = x + 1 \n# y's 1st dimension gets repeated for the 2nd dimension in x\n# and the scalar z get's repeated for all dimensions\n# the below is equal to \n# `broadcast(+, broadcast(+, broadcast(user_defined, x), y), z)`\nprintln(user_defined.(x) .+ y .+ z)\n# or try column vector with a row vector\nA = 1:4\nb = A'\nprintln(A .+ b) # gives a 4x4 matrix\n# Or it can be used quite general purpose:\nx = ComplexF64[1im, 2im, 3im]\nprintln(getfield.(x, :im)) # or just use imag.(x)","~:refs":{"~#list":[]},"~:output-log-lines":{"~:stdout":3},"~:language":"julia","~:id":"d81d8319-9278-4cbf-b86f-3e869807564c","~:compute-ref":"~ucb7ea8d0-b673-11e8-84f6-feb93b1b86dd","~:runtime":["~:runtime","7437c28d-5ffa-4b39-92a7-04864a9ae2b3"],"~:kind":"code","~:outputs":{},"~:error":null,"~:exec-duration":1445,"~:bucket":null},"4ce9036b-5053-4743-9a45-0b6ced833714":{"~:id":"4ce9036b-5053-4743-9a45-0b6ced833714","~:kind":"section","~:title":"Rules","~:content":{"~#list":["1b3cc5a7-578c-4700-a48b-357a7f2d5ef9"]},"~:sections":{"~#list":[]}},"f6d1c737-66ce-4639-bedf-9ffeb49aa1a7":{"~:id":"f6d1c737-66ce-4639-bedf-9ffeb49aa1a7","~:kind":"section","~:title":"The Problem: Broadcasting","~:content":["94014d5e-1304-47ef-980b-47e891069470","d06d2d0c-3895-4ba6-b85f-3ac2dbfa69e6","ce4f1cc3-92d1-47b8-ae99-5f5374922dc3","fa11bfbb-27bb-440f-96b3-400b6e1388d3","d81d8319-9278-4cbf-b86f-3e869807564c","05f5e5f2-3afb-48e9-a6e1-d9becac5be65","9a5af79b-3113-4257-b4b1-4791b158d873","42f56b48-775f-40cc-8c1b-d65efb56aae9","af09953d-be96-4999-b34a-3afefb521bfb"]},"3e611adf-b54d-4671-927d-ae4fd721072a":{"~:id":"3e611adf-b54d-4671-927d-ae4fd721072a","~:kind":"section","~:title":"Feed Our Type Into Other Functions","~:content":{"~#list":["a3b1ba3d-701a-41f3-bb9e-2534a73cab03","c32ec18f-cc95-4209-814d-8fb3322b30e7","ecedea8c-1963-423d-ad8d-c3f1232db2bb","90470514-8a81-4db3-805d-4ea95168f42d","d1126bbb-8f40-491f-855f-42d50dcf13d3","76dff326-6646-4ce1-935e-3e0531a586f6","6b1f7040-6139-48fb-b93d-d80220d35a55"]},"~:sections":{"~#list":[]}},"3c5792cc-756e-48ab-bb48-2620306c00d2":{"~:id":"3c5792cc-756e-48ab-bb48-2620306c00d2","~:kind":"text","~:content":"

To win the performance part, your implementation needs to be at least as fast as Julia's base broadcast implementation for arbitrary dimensions and argument combinations. That means it needs to use SIMD acceleration and compile away all those high level, lazy indexing constructs. It also needs to work with user defined types and functions that are out of the control of your implementation!

"},"fa11bfbb-27bb-440f-96b3-400b6e1388d3":{"~:id":"fa11bfbb-27bb-440f-96b3-400b6e1388d3","~:kind":"text","~:content":"

It's such a basic operation that Julia has its own syntax for it. You can apply any function element-wise by just putting a dot in front of it:

"},"d06d2d0c-3895-4ba6-b85f-3ac2dbfa69e6":{"~:id":"d06d2d0c-3895-4ba6-b85f-3ac2dbfa69e6","~:kind":"code-listing","~:content":"In many programming languages, map is the name of a higher-order function that applies a given function to each element of a list, returning a list of results in the same order. It is often called apply-to-all when considered in functional form. ","~:language":"julia"},"0320827e-e0ce-473d-b14d-2cbcd1167640":{"~:id":"0320827e-e0ce-473d-b14d-2cbcd1167640","~:kind":"section","~:title":"Reference Implementation","~:content":{"~#list":["7f9a5ad0-2127-4a5e-b54e-bfe2e51fe786","c836bb7e-34a6-4863-9fdd-91cdfc0b6494","fb1189a9-2ea8-447f-9303-7e93fb3f2508"]},"~:sections":{"~#list":[]}},"509674d5-49ba-4189-abcd-cc1a7e43e071":{"~:id":"509674d5-49ba-4189-abcd-cc1a7e43e071","~:kind":"section","~:title":"Submission","~:content":["934b26ff-da7e-42c1-8c72-895432518159","a24a8d68-e39b-47af-a909-988f7cd6d7eb","1fcdccf3-18d9-43d3-b6c7-2a5a8c1ebc76","55de1b68-0bb4-4b12-9fde-4e23bdad4f09"]},"9a5af79b-3113-4257-b4b1-4791b158d873":{"~:id":"9a5af79b-3113-4257-b4b1-4791b158d873","~:kind":"text","~:content":"

What I haven't even mentioned yet is that Julia takes multiple broadcast calls, e.g. user_defined.(x) .+ y .+ z , and fuses them into one loop without any temporary allocations. This mechanism is customizable, and one can influence how to fuse the call tree to allow domain specific optimizations . This is possible because Julia's implementation allows you to overload it, and passes you a lazy representation of the broadcast expression which you're free to modify appropriately.

"},"90470514-8a81-4db3-805d-4ea95168f42d":{"~:id":"90470514-8a81-4db3-805d-4ea95168f42d","~:kind":"text","~:content":"

With that, we can overload Julia'siteration protocol:

"},"94014d5e-1304-47ef-980b-47e891069470":{"~:id":"94014d5e-1304-47ef-980b-47e891069470","~:kind":"text","~:content":"

A common problem is that you have a bunch of arrays and scalars, and want to apply a function element-wise over the arrays. This is an extension of the regularmapfunction:

"},"9e9465fd-58be-4898-96f9-71b7cf3ab9fd":{"~:id":"9e9465fd-58be-4898-96f9-71b7cf3ab9fd","~:kind":"text","~:content":"

But how close to the truth am I, really? How big is the difference? And how fast and composable can an implementation become in a modern C++?

Obviously, I can't just sit down and learn how to write the best and most elegant code in another language - it would take years until I reached the level of my current Julia skills. This is where the Julia challenge comes into play!

I put together a reference implementation for a problem that nicely illustrates the fundamental principles which make Julia so productive and scalable for numeric libraries. It's the foundation that allows one to freely combine packages and still get optimal performance. (If you're curious about such packages, have a look at this article: Some State of the Art Packages in Julia 1.0 .)

I can't really imagine writing those in any other language, so I dare you to teach me! Use Python + Numba, Python + C, Swift, Cython, Go, Lua-Jit, Rust, D - surprise us! If all works out, this can be a great learning experience for everyone following the challenge! :)

"},"fe80ecba-27aa-4501-9d4e-7c3b94ce24a2":{"~:id":"fe80ecba-27aa-4501-9d4e-7c3b94ce24a2","~:kind":"text","~:content":"

Because of that, I get into a lot of discussions that go, "Why Julia? We always just use a scripting language and write the fast part in C/C++," and all different combinations of that argument. I usually quickly reply that this approach ends up in a lot of effort and it doesn't actually compose very well to mix a low-level language with a scripting language! One ends up with a difficult to maintain C/C++ library, and a scripting language that can't be used for all tasks, since the slow speed becomes a bottleneck. Or the C/C++ library doesn't work with the high-level constructs that you learned to love. Even Tensorflow and Google have acknowledged this , and are trying to rewrite Tensorflow in one language, namely, Swift!

"},"6584a98c-e636-4ef8-a9be-f4626424e7b4":{"~:id":"6584a98c-e636-4ef8-a9be-f4626424e7b4","~:kind":"text","~:content":"

I've been using the Julia language for around 5 years now, and I've grown rather fond of it. By now, I can't really imagine how to do the things I'm doing in any other language. This could, of course, be a misconception and the result of not knowing any other language well enough - a form of theDunning–Kruger effect. But my impression is that Julia is the best language to write high performance numeric libraries for machine learning, data science, solvers, optimizations, etc....

"},"c32ec18f-cc95-4209-814d-8fb3322b30e7":{"~:id":"c32ec18f-cc95-4209-814d-8fb3322b30e7","~:kind":"text","~:content":"

Implementing the iterator protocol for our lazy broadcast is a bit more challenging, since now we actually need to figure out its shape so we can iterate over it (we got around that before by using an output array).

"},"88abac49-0430-44de-b93d-3b075faadba5":{"~:id":"88abac49-0430-44de-b93d-3b075faadba5","~:title":"The Julia Language Challenge","~:kind":"section","~:version":2,"~:content":{"~#list":["6584a98c-e636-4ef8-a9be-f4626424e7b4","fe80ecba-27aa-4501-9d4e-7c3b94ce24a2","9e9465fd-58be-4898-96f9-71b7cf3ab9fd"]},"~:sections":{"~#list":["4ce9036b-5053-4743-9a45-0b6ced833714","f6d1c737-66ce-4639-bedf-9ffeb49aa1a7","0320827e-e0ce-473d-b14d-2cbcd1167640","40b24e11-8492-4713-89a2-5a0688fbe050","e6acd05d-4c99-4246-a645-70293f2bf925","509674d5-49ba-4189-abcd-cc1a7e43e071"]}},"1fcdccf3-18d9-43d3-b6c7-2a5a8c1ebc76":{"~:id":"1fcdccf3-18d9-43d3-b6c7-2a5a8c1ebc76","~:kind":"text","~:content":"

Good luck and happy coding!

"},"af09953d-be96-4999-b34a-3afefb521bfb":{"~:id":"af09953d-be96-4999-b34a-3afefb521bfb","~:kind":"text","~:content":"

  • Needs to work for n arguments
  • Needs to work for any combinations of scalars and n-dimensional arrays
  • Inside the implementation, one needs to have access to the whole call tree and it should be possible to rewrite that tree or flatten it into one loop
  • User defined functions and types need to be inlined and SIMD acceleration should be used whenever possible

"},"a24a8d68-e39b-47af-a909-988f7cd6d7eb":{"~:id":"a24a8d68-e39b-47af-a909-988f7cd6d7eb","~:kind":"text","~:content":"

Results are accepted as a pull request (PR) to: https://github.com/SimonDanisch/julia-challenge . The Github repository can also be used to turn this into a community effort. Just make a PR with an incomplete solution and continue polishing it with others.

"},"d1126bbb-8f40-491f-855f-42d50dcf13d3":{"~:content":"iterate(br::LazyBroadcast) = iterate(br, (eachindex(br),))\n@propagate_inbounds function iterate(bc::LazyBroadcast, s)\n istate = iterate(s...) # iterate indices\n istate === nothing && return nothing # if done\n i, newstate = istate\n return (bc[i], (s[1], newstate))\nend","~:refs":{"~#list":[]},"~:output-log-lines":{},"~:language":"julia","~:id":"d1126bbb-8f40-491f-855f-42d50dcf13d3","~:compute-ref":"~uf31edae0-b673-11e8-84f6-feb93b1b86dd","~:runtime":["~:runtime","7437c28d-5ffa-4b39-92a7-04864a9ae2b3"],"~:kind":"code","~:outputs":{"~_":{"~:kind":"html","~:name":null,"~:coder":"display","~:blob":{"~:id":"QmY2pbAxagunvkKRCw7zFsrBEhvh7EnWWmApCcBgKfRoAo","~:size":58,"~:content-type":"text/html"}}},"~:error":null,"~:exec-duration":661,"~:bucket":null},"6b1f7040-6139-48fb-b93d-d80220d35a55":{"~:content":"@btime sum($br) \n@btime sum($out .= super_custom_func.($a, $b))\n\n@assert sum(br) ≈ sum(super_custom_func.(a, b))\n\nnothing","~:refs":{"~#list":[]},"~:active-requests":{"~#list":[]},"~:output-log-lines":{"~:stdout":2},"~:language":"julia","~:id":"6b1f7040-6139-48fb-b93d-d80220d35a55","~:compute-ref":"~uf386eb80-b673-11e8-84f6-feb93b1b86dd","~:runtime":["~:runtime","7437c28d-5ffa-4b39-92a7-04864a9ae2b3"],"~:kind":"code","~:outputs":{},"~:error":null,"~:exec-duration":23684,"~:bucket":null,"~:stdout":null},"04d5fa59-4a72-4fa4-b5ef-92ad6490b917":{"~:id":"04d5fa59-4a72-4fa4-b5ef-92ad6490b917","~:kind":"text","~:content":"

Next bonus point could be to write a simple GPU-accelerated materialize! To not further bloat this article, I'll leave that for another challenge.

"},"42f56b48-775f-40cc-8c1b-d65efb56aae9":{"~:id":"42f56b48-775f-40cc-8c1b-d65efb56aae9","~:kind":"text","~:content":"

So, the spec for our little toy broadcast implementation is the following:

"},"32d08f8d-d898-4e2f-aa1a-7d496d85318c":{"~:id":"32d08f8d-d898-4e2f-aa1a-7d496d85318c","~:kind":"section","~:title":"Performance","~:content":{"~#list":["3c5792cc-756e-48ab-bb48-2620306c00d2","477176dd-d6f3-4f60-89b7-a6fe32106b25"]},"~:sections":{"~#list":[]}},"ce4f1cc3-92d1-47b8-ae99-5f5374922dc3":{"~:id":"ce4f1cc3-92d1-47b8-ae99-5f5374922dc3","~:kind":"text","~:content":"

It's a fundamental operation for any array library, and is used heavily in machine learning and any other area in computer science. If you were in the situation of needing to implement a Numpy-like library, this would be one of your first challenges.

"},"c836bb7e-34a6-4863-9fdd-91cdfc0b6494":{"~:content":"import Base: getindex, iterate, axes, eachindex, tail, @propagate_inbounds\nstruct LazyBroadcast{F, Args}\n f::F\n args::Args\nend\nbr_getindex(scalar, I) = scalar # Scalars no need to index them\n@propagate_inbounds function br_getindex(A::AbstractArray, I)\n idx = ntuple(i-> ifelse(size(A, i) === 1, 1, I[i]), Val(ndims(A)))\n return A[CartesianIndex(idx)]\nend\n@propagate_inbounds function br_getindex(x::LazyBroadcast, I)\n # this could be a map, but the current map in 1.0 has a perf problem\n return x.f(getindex_arg(x.args, I)...) \nend\ngetindex_arg(args::Tuple{}, I) = () # recursion ancor\n@propagate_inbounds function getindex_arg(args::NTuple{N, Any}, I) where N\n return (br_getindex(args[1], I), getindex_arg(tail(args), I)...)\nend\n@propagate_inbounds getindex(x::LazyBroadcast, I) = br_getindex(x, Tuple(I))\nfunction materialize!(out::AbstractArray, call_tree::LazyBroadcast)\n # an n-dimensional simd accelerated loop\n @simd for i in CartesianIndices(axes(out)) \n @inbounds out[i] = call_tree[i]\n end\n return out\nend\nbr_construct(x) = x\nfunction br_construct(x::Expr)\n x.args .= br_construct.(x.args) # apply recursively\n if Meta.isexpr(x, :call) # replace calls to construct LazyBroadcasts\n x = :(LazyBroadcast($(x.args[1]), ($(x.args[2:end]...),)))\n end\n x\nend\n# macro to enable the syntax @broadcast a + b - sin(c) to construct our type\nmacro broadcast(call_expr) \n esc(br_construct(call_expr))\nend","~:refs":{"~#list":[]},"~:output-log-lines":{},"~:language":"julia","~:id":"c836bb7e-34a6-4863-9fdd-91cdfc0b6494","~:compute-ref":"~ucc5fe110-b673-11e8-84f6-feb93b1b86dd","~:runtime":["~:runtime","7437c28d-5ffa-4b39-92a7-04864a9ae2b3"],"~:kind":"code","~:outputs":{"~_":{"~:kind":"html","~:name":null,"~:coder":"display","~:blob":{"~:id":"QmbdcJVHWDC4DjUecoaaZE47396ExxAMM3fggfYwTo1d3T","~:size":47,"~:content-type":"text/html"}}},"~:error":null,"~:exec-duration":1132,"~:bucket":null},"7f9a5ad0-2127-4a5e-b54e-bfe2e51fe786":{"~:id":"7f9a5ad0-2127-4a5e-b54e-bfe2e51fe786","~:kind":"text","~:content":"

The below snippet implements a (quite) fully featured lazy, n-dimensional, n-argument broadcast. You can actually play around with the implementation by creating aNextjournal account with signup code: juliacon , and then clicking edit in the right corner of the article!

"},"90480d64-f52f-4ce1-b17f-8c8e57ad3ac5":{"~:id":"90480d64-f52f-4ce1-b17f-8c8e57ad3ac5","~:kind":"section","~:content":{"~#list":["411b61d8-8142-46ee-99dd-34276364048c","9afff5df-8f72-4b1e-81cd-914ad43519a3","528cdae1-335b-4f9c-afb7-c3506c029eb8"]},"~:sections":{"~#list":[]},"~:title":"Types & Functions From Different Packages"},"32cda6ce-6dc2-4ed5-a16a-6d0a0d8fd499":{"~:id":"32cda6ce-6dc2-4ed5-a16a-6d0a0d8fd499","~:kind":"section","~:title":"Multithreading","~:content":{"~#list":["53d04e1b-ee80-4125-90e2-cb4f016030f9","f484777f-854c-4573-b999-b409627323c9","04d5fa59-4a72-4fa4-b5ef-92ad6490b917"]},"~:sections":{"~#list":[]}},"f484777f-854c-4573-b999-b409627323c9":{"~:content":"using Statistics\nfunction threaded_materialize!(out::AbstractArray, x::LazyBroadcast)\n Threads.@threads for i in CartesianIndices(axes(out))\n @inbounds out[i] = x[i]\n end\n return out\nend\na = @benchmark threaded_materialize!($out, $br)\nb = @benchmark materialize!($out, $br)\nhyper_threads = Sys.CPU_THREADS\n# intels hyper threads don't really count in such intense calculations\ncores = hyper_threads / 2\n# scaling\nprintln(\"best scaling \", minimum(b).time / minimum(a).time / cores)\nprintln(\"average scaling \", mean(b).time / mean(a).time / cores)\nnothing","~:refs":{"~#list":[]},"~:output-log-lines":{"~:stdout":2},"~:language":"julia","~:id":"f484777f-854c-4573-b999-b409627323c9","~:compute-ref":"~u021d5b20-b674-11e8-84f6-feb93b1b86dd","~:runtime":["~:runtime","7437c28d-5ffa-4b39-92a7-04864a9ae2b3"],"~:kind":"code","~:outputs":{},"~:error":null,"~:exec-duration":23426,"~:bucket":null},"9afff5df-8f72-4b1e-81cd-914ad43519a3":{"~:content":"# Library A - I chose GeometryTypes. \npkg\"add GeometryTypes\" # use package manager to install the package\n# Any library with NVectors specializing to the length will do!\nusing GeometryTypes\n# You need to take my word for it, that this is just a normal Julia struct\n# Or you can inspect it with dump(Point3)\nconst Point3 = Point{3, Float32} \n\n# function needs to come from different library\nmodule LibraryB\n # no odd stuff, no functors, no special lambda expression! \n # this function needs to be a normal language function\n # as can be found in the wild\n\tsuper_custom_func(a, b) = sqrt(sum(a .* b))\nend\n# emulate that the function comes from a different library\nusing .LibraryB: super_custom_func ","~:refs":{"~#list":[]},"~:output-log-lines":{"~:stdout":10},"~:language":"julia","~:id":"9afff5df-8f72-4b1e-81cd-914ad43519a3","~:compute-ref":"~udba52e50-b673-11e8-84f6-feb93b1b86dd","~:runtime":["~:runtime","7437c28d-5ffa-4b39-92a7-04864a9ae2b3"],"~:kind":"code","~:outputs":{},"~:error":null,"~:exec-duration":11247,"~:bucket":null},"55de1b68-0bb4-4b12-9fde-4e23bdad4f09":{"~:id":"55de1b68-0bb4-4b12-9fde-4e23bdad4f09","~:kind":"text","~:content":"

"},"411b61d8-8142-46ee-99dd-34276364048c":{"~:id":"411b61d8-8142-46ee-99dd-34276364048c","~:kind":"text","~:content":"

It's mandatory to use an existing package without changing it. This can also be emulated by putting it in an isolated module not using any constructs defined in our implementation. This is necessary in order to make Julia's packages talk to each other and combine already existing code just like Lego. So it's important to use a normal, user defined type/object/class in the language that you target!

"},"7437c28d-5ffa-4b39-92a7-04864a9ae2b3":{"~:runtime/inherited-environment-variables":{"~#list":[{"~:name":"SERVICE_9998_NAME","~:value":"runtime-b6125ea6-4202-4f47-a9ab-2bf7d365a145"},{"~:name":"SERVICE_9998_CHECK_TCP","~:value":"true"},{"~:name":"SERVICE_TAGS=urlprefix-/runner/17592186856755/runtime/b6125ea6-4202-4f47-a9ab-2bf7d365a145 strip","~:value":"/runner/17592186856755/runtime/b6125ea6-4202-4f47-a9ab-2bf7d365a145"},{"~:name":"NVIDIA_VISIBLE_DEVICES","~:value":"void"},{"~:name":"NVIDIA_DRIVER_CAPABILITIES","~:value":"all"},{"~:name":"PATH","~:value":"/usr/local/julia/bin:/usr/local/nvidia/bin:/usr/local/cuda/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"},{"~:name":"JULIA_PATH","~:value":"/usr/local/julia"},{"~:name":"JULIA_GPG","~:value":"3673DF529D9049477F76B37566E3C7DC03D6E495"},{"~:name":"JULIA_VERSION","~:value":"1.0.0"},{"~:name":"LC_ALL","~:value":"en_US.UTF-8"},{"~:name":"LANGUAGE","~:value":"en_US.en"},{"~:name":"LANG","~:value":"en_US.UTF-8"},{"~:name":"DEBIAN_FRONTEND","~:value":"noninteractive"},{"~:name":"BASH_ENV","~:value":"/.bash_profile"}]},"~:type":"~:nextjournal","~:language":"julia","~:id":"7437c28d-5ffa-4b39-92a7-04864a9ae2b3","~:kind":"runtime","~:error":null,"~:environment":["~:environment",{"~:article/nextjournal.id":"~u028d35a1-c683-4eee-b4a2-3da8349e4116","~:change/nextjournal.id":"~u5b9562b0-4740-454e-a82c-4a083930236d","~:node/id":"b6125ea6-4202-4f47-a9ab-2bf7d365a145"}],"~:runtime/environment-variables":[{"~:name":"JULIA_NUM_THREADS","~:value":"8"}],"~:resources":"~:shared-10gb"},"a3b1ba3d-701a-41f3-bb9e-2534a73cab03":{"~:id":"a3b1ba3d-701a-41f3-bb9e-2534a73cab03","~:kind":"text","~:content":"

The inverse of the above: now it's time for our type to be consumed by other functions. For simplicity, we just use Julia's Base sum function, which should already get us all the SIMD accelerated speed we wish for. For it to work, we just need to implement the iterator protocol.

"},"53d04e1b-ee80-4125-90e2-cb4f016030f9":{"~:id":"53d04e1b-ee80-4125-90e2-cb4f016030f9","~:kind":"text","~:content":"

Now let's write a simple multi-threaded materialize! , just because we can :)

"},"934b26ff-da7e-42c1-8c72-895432518159":{"~:id":"934b26ff-da7e-42c1-8c72-895432518159","~:kind":"text","~:content":"

This is it - if you get through this just fine with your language, I'd be delighted to see your code! If you don't, but have some interesting fragments going, please publish them as well.

"},"76dff326-6646-4ce1-935e-3e0531a586f6":{"~:id":"76dff326-6646-4ce1-935e-3e0531a586f6","~:kind":"text","~:content":"

Which now enables us to feed LazyBroadcast into any function that accepts iterators! Important to win the challenge: the function we use needs to be out of our control and must be part of some standard library:

"},"1b3cc5a7-578c-4700-a48b-357a7f2d5ef9":{"~:id":"1b3cc5a7-578c-4700-a48b-357a7f2d5ef9","~:kind":"text","~:content":"

  • There are 3 categories: developer effort, performance and extensibility.
  • A successful implementations needs to shine in all 3 categories.
  • You shouldn't need to write a large helper library to produce concise code. This can be unfair since Julia has lots of functionality for array algorithms built in, but also illustrates the point. To not exclude any new languages, I'm open to discussing the details of this rule.

"},"e6acd05d-4c99-4246-a645-70293f2bf925":{"~:id":"e6acd05d-4c99-4246-a645-70293f2bf925","~:kind":"section","~:title":"Bonus Points","~:content":{"~#list":[]},"~:sections":{"~#list":["32cda6ce-6dc2-4ed5-a16a-6d0a0d8fd499"]}},"477176dd-d6f3-4f60-89b7-a6fe32106b25":{"~:content":"using BenchmarkTools\n\nreference(out, a, b, c) = (out .= a .+ b .- sin.(c))\n\na = rand(1000, 1000);\nb = rand(1000);\nc = 1.0\nout1 = similar(a);\nout2 = similar(a);\nbr = @broadcast a + b - sin(c)\n\n@btime materialize!($out1, $br)\n@btime reference($out2, $a, $b, $c)\n@assert out1 == out2\nnothing","~:refs":{"~#list":[]},"~:output-log-lines":{"~:stdout":2},"~:result-selected?":null,"~:language":"julia","~:id":"477176dd-d6f3-4f60-89b7-a6fe32106b25","~:compute-ref":"~ucd106c60-b673-11e8-84f6-feb93b1b86dd","~:runtime":["~:runtime","7437c28d-5ffa-4b39-92a7-04864a9ae2b3"],"~:kind":"code","~:outputs":{},"~:error":null,"~:exec-duration":24439,"~:bucket":null},"835904a2-26a1-43dc-a0fa-f03ff58ce7c0":{"~:id":"835904a2-26a1-43dc-a0fa-f03ff58ce7c0","~:kind":"text","~:content":"

materialize!
@broadcast a + b - sin(c)

"},"fb1189a9-2ea8-447f-9303-7e93fb3f2508":{"~:id":"fb1189a9-2ea8-447f-9303-7e93fb3f2508","~:kind":"text","~:content":"

A first prototype took me half an hour to write, and a further 1.5 hours to optimize all cases to match Julia's SIMD optimized broadcast implementation in terms of performance. This should be the baseline for truly winning this challenge.

"},"ecedea8c-1963-423d-ad8d-c3f1232db2bb":{"~:content":"# Simplified implementation to take the axes of the array with the largest \n# dimensionality (axes -> the index range an array iterates over)\nbiggest(a, b, c, rest...) = biggest(biggest(a, b), biggest(c, rest...))\nbiggest(a::NTuple{N1, Any}, b::NTuple{N2, Any}) where {N1, N2} = \n\tifelse(N1 > N2, a, b)\nbiggest(a) = a\nflatten_args(t::LazyBroadcast, rest...) = \n\t(flatten_args(t.args...)..., flatten_args(rest...)...)\nflatten_args(t::Any, rest...) = (t, flatten_args(rest...)...)\nflatten_args() = ()\n# the indexing axes of our array\naxes(br::LazyBroadcast) = biggest(map(axes, flatten_args(br))...)\n# lazy view that can be used to index all elements in br\neachindex(br::LazyBroadcast) = CartesianIndices(axes(br)) ","~:refs":{"~#list":[]},"~:output-log-lines":{},"~:language":"julia","~:id":"ecedea8c-1963-423d-ad8d-c3f1232db2bb","~:compute-ref":"~uf2818970-b673-11e8-84f6-feb93b1b86dd","~:runtime":["~:runtime","7437c28d-5ffa-4b39-92a7-04864a9ae2b3"],"~:kind":"code","~:outputs":{"~_":{"~:kind":"html","~:name":null,"~:coder":"display","~:blob":{"~:id":"QmPL3ADLWSWATaFBhMNWAvwSrztW5yj5BMZJutSDkp6pQZ","~:size":59,"~:content-type":"text/html"}}},"~:error":null,"~:exec-duration":1010,"~:bucket":null}},"~:transclusions":{"~#cmap":[{"~:node/id":"9571eacc-e781-4fea-80de-1d9145e7aa12","~:article/nextjournal.id":"~u5b34cf3c-172d-4c84-a965-12ff182d12cf","~:change/nextjournal.id":"~u5b3a3b44-4259-430c-b1ee-98a0a2928dd6"},{"~:runtime/inherited-environment-variables":{"~#list":[{"~:name":"PATH","~:value":"/usr/local/nvidia/bin:/usr/local/cuda/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"},{"~:name":"LC_ALL","~:value":"en_US.UTF-8"},{"~:name":"LANGUAGE","~:value":"en_US.en"},{"~:name":"LANG","~:value":"en_US.UTF-8"},{"~:name":"DEBIAN_FRONTEND","~:value":"noninteractive"},{"~:name":"BASH_ENV","~:value":"/.bash_profile"}]},"~:change/inserted-at":"~t2018-07-02T14:48:36.790Z","~:transclusion":{"~:node/id":"9571eacc-e781-4fea-80de-1d9145e7aa12","~:article/nextjournal.id":"~u5b34cf3c-172d-4c84-a965-12ff182d12cf","~:change/nextjournal.id":"~u5b3a3b44-4259-430c-b1ee-98a0a2928dd6"},"~:name":"Jupyter IRkernel","~:docker/environment-image":"eu.gcr.io/nextjournal-com/environment@sha256:beba038e977850f54e002fb33ec5101bbf17a9d5a6ce19468165d3cd2d4b6dea","~:type":"~:nextjournal","~:environment?":true,"~:language":"r","~:id":"9571eacc-e781-4fea-80de-1d9145e7aa12","~:kind":"runtime","~:changed?":false,"~:error":null,"~:environment":["~:environment",{"~:node/id":"10bc64db-a75e-4f08-8981-c5c6f4e614b9","~:article/nextjournal.id":"~u5accb601-b16a-4637-ae55-5fd73544a52f","~:change/nextjournal.id":"~u5b3027a6-c456-478c-8c37-d106caef56bb"}],"~:diff":""},{"~:node/id":"19e3027e-20bc-47c9-8f60-edcdbf613a23","~:article/nextjournal.id":"~u5b45eb52-bad4-413d-9d7f-b2b573a25322","~:change/nextjournal.id":"~u5b45f778-3e8c-4dc6-807a-f83956c63dca"},{"~:runtime/inherited-environment-variables":{"~#list":[{"~:name":"PATH","~:value":"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"}]},"~:change/inserted-at":"~t2018-07-11T12:26:32.700Z","~:transclusion":{"~:node/id":"19e3027e-20bc-47c9-8f60-edcdbf613a23","~:article/nextjournal.id":"~u5b45eb52-bad4-413d-9d7f-b2b573a25322","~:change/nextjournal.id":"~u5b45f778-3e8c-4dc6-807a-f83956c63dca"},"~:name":"Clojure Default","~:docker/environment-image":"eu.gcr.io/nextjournal-com/environment@sha256:221ca04cb534971a75c62c286dc9be15bb5d017e2851cdb04b311c3f48ac71ce","~:type":"~:nextjournal","~:environment?":true,"~:language":"bash","~:id":"19e3027e-20bc-47c9-8f60-edcdbf613a23","~:kind":"runtime","~:changed?":false,"~:error":null,"~:environment":["~:environment","46dc43bc-c049-4cfd-87aa-3463ca22df93"],"~:runtime/environment-variables":[{"~:name":"CLOJURE_VERSION","~:value":"1.9.0.341"},{"~:name":"DEBIAN_FRONTEND","~:value":"noninteractive"},{"~:name":"BASH_ENV","~:value":"/.bash_profile"},{"~:name":"LANG","~:value":"en_US.UTF-8"},{"~:name":"LANGUAGE","~:value":"en_US.en"},{"~:name":"LC_ALL","~:value":"en_US.UTF-8"}],"~:diff":""},{"~:article/nextjournal.id":"~u028acb25-56e5-4ede-bf23-552785783878","~:change/nextjournal.id":"~u5b7d73b5-7b33-4d04-ac5e-9c0b8ec6128d","~:node/id":"90436991-2923-44bb-b0d1-f235aec846ef"},{"~:runtime/inherited-environment-variables":{"~#list":[{"~:name":"PATH","~:value":"/usr/local/julia/bin:/usr/local/nvidia/bin:/usr/local/cuda/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"},{"~:name":"JULIA_PATH","~:value":"/usr/local/julia"},{"~:name":"JULIA_GPG","~:value":"3673DF529D9049477F76B37566E3C7DC03D6E495"},{"~:name":"JULIA_VERSION","~:value":"0.7.0"},{"~:name":"NVIDIA_VISIBLE_DEVICES","~:value":"void"},{"~:name":"NVIDIA_DRIVER_CAPABILITIES","~:value":"compute,utility"},{"~:name":"LC_ALL","~:value":"en_US.UTF-8"},{"~:name":"LANGUAGE","~:value":"en_US.en"},{"~:name":"LANG","~:value":"en_US.UTF-8"},{"~:name":"BASH_ENV","~:value":"/.bash_profile"},{"~:name":"NEXTJOURNAL_MOUNT_CUDA","~:value":"9.2-cudnn7-devel-ubuntu18.04"}]},"~:change/inserted-at":"~t2018-08-22T14:31:17.852Z","~:transclusion":{"~:article/nextjournal.id":"~u028acb25-56e5-4ede-bf23-552785783878","~:change/nextjournal.id":"~u5b7d73b5-7b33-4d04-ac5e-9c0b8ec6128d","~:node/id":"90436991-2923-44bb-b0d1-f235aec846ef"},"~:name":"IJulia","~:docker/environment-image":"eu.gcr.io/nextjournal-com/environment@sha256:ee53995c089dac09a01dd656f71382b81169eb3203b7aecf1804811210c420c6","~:type":"~:nextjournal","~:environment?":true,"~:language":"julia","~:id":"90436991-2923-44bb-b0d1-f235aec846ef","~:kind":"runtime","~:changed?":false,"~:error":null,"~:environment":["~:environment",{"~:article/nextjournal.id":"~u5b18ea0a-61b4-42dd-8743-7a4c62479773","~:change/nextjournal.id":"~u5b79b7be-159b-4ac9-8b16-844c4f06f6a7","~:node/id":"e8f2328c-62cf-4dca-849f-73746f78304c"}],"~:runtime/environment-variables":[{"~:name":"IJULIA_DEBUG","~:value":"true"}],"~:diff":""},{"~:article/nextjournal.id":"~u5b18ea0a-61b4-42dd-8743-7a4c62479773","~:change/nextjournal.id":"~u5b79b7be-159b-4ac9-8b16-844c4f06f6a7","~:node/id":"e8f2328c-62cf-4dca-849f-73746f78304c"},{"~:runtime/inherited-environment-variables":{"~#list":[{"~:name":"PATH","~:value":"/usr/local/nvidia/bin:/usr/local/cuda/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"},{"~:name":"LC_ALL","~:value":"en_US.UTF-8"},{"~:name":"LANGUAGE","~:value":"en_US.en"},{"~:name":"LANG","~:value":"en_US.UTF-8"},{"~:name":"BASH_ENV","~:value":"/.bash_profile"},{"~:name":"NEXTJOURNAL_MOUNT_CUDA","~:value":"9.2-cudnn7-devel-ubuntu18.04"},{"~:name":"NVIDIA_VISIBLE_DEVICES","~:value":"void"},{"~:name":"NVIDIA_DRIVER_CAPABILITIES","~:value":"compute,utility"}]},"~:change/inserted-at":"~t2018-08-19T18:32:30.713Z","~:transclusion":{"~:article/nextjournal.id":"~u5b18ea0a-61b4-42dd-8743-7a4c62479773","~:change/nextjournal.id":"~u5b79b7be-159b-4ac9-8b16-844c4f06f6a7","~:node/id":"e8f2328c-62cf-4dca-849f-73746f78304c"},"~:name":"Julia 0.7","~:docker/environment-image":"eu.gcr.io/nextjournal-com/environment@sha256:32809b45bfea10bc84c6354348aba368fe881946de745e0147b1a02d8d5efd71","~:type":"~:nextjournal","~:environment?":true,"~:language":"bash","~:id":"e8f2328c-62cf-4dca-849f-73746f78304c","~:kind":"runtime","~:changed?":false,"~:error":null,"~:environment":["~:environment",{"~:article/nextjournal.id":"~u5b45dad0-dfdf-4576-9b8c-f90892e74c94","~:change/nextjournal.id":"~u5b750a51-bd70-424c-9e46-864bc3dc85f5","~:node/id":"01204d2e-3c51-4802-88f3-a128e4d85a3e"}],"~:runtime/environment-variables":[{"~:name":"PATH","~:value":"/usr/local/julia/bin:/usr/local/nvidia/bin:/usr/local/cuda/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"},{"~:name":"JULIA_PATH","~:value":"/usr/local/julia"},{"~:name":"JULIA_GPG","~:value":"3673DF529D9049477F76B37566E3C7DC03D6E495"},{"~:name":"JULIA_VERSION","~:value":"0.7.0"}],"~:diff":""},{"~:article/nextjournal.id":"~u028d35a1-c683-4eee-b4a2-3da8349e4116","~:change/nextjournal.id":"~u5b9562b0-4740-454e-a82c-4a083930236d","~:node/id":"b6125ea6-4202-4f47-a9ab-2bf7d365a145"},{"~:runtime/inherited-environment-variables":{"~#list":[{"~:name":"PATH","~:value":"/usr/local/julia/bin:/usr/local/nvidia/bin:/usr/local/cuda/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"},{"~:name":"JULIA_PATH","~:value":"/usr/local/julia"},{"~:name":"JULIA_GPG","~:value":"3673DF529D9049477F76B37566E3C7DC03D6E495"},{"~:name":"JULIA_VERSION","~:value":"1.0.0"},{"~:name":"LC_ALL","~:value":"en_US.UTF-8"},{"~:name":"LANGUAGE","~:value":"en_US.en"},{"~:name":"LANG","~:value":"en_US.UTF-8"},{"~:name":"DEBIAN_FRONTEND","~:value":"noninteractive"},{"~:name":"BASH_ENV","~:value":"/.bash_profile"}]},"~:change/inserted-at":"~t2018-09-09T18:13:04.730Z","~:transclusion":{"~:article/nextjournal.id":"~u028d35a1-c683-4eee-b4a2-3da8349e4116","~:change/nextjournal.id":"~u5b9562b0-4740-454e-a82c-4a083930236d","~:node/id":"b6125ea6-4202-4f47-a9ab-2bf7d365a145"},"~:refs":{"~#list":[]},"~:docker/environment-image":"eu.gcr.io/nextjournal-com/environment@sha256:6072f7b013639af2be22973b23cadecb528879818cb7d6848ad612a4cf8d57ce","~:type":"~:nextjournal","~:environment?":true,"~:language":"julia","~:id":"b6125ea6-4202-4f47-a9ab-2bf7d365a145","~:kind":"runtime","~:changed?":false,"~:error":null,"~:environment":["~:environment",{"~:article/nextjournal.id":"~u0289217e-a1bb-440d-9224-f32b960a271a","~:change/nextjournal.id":"~u5b7430ce-e0bf-4c81-b2c1-aee21676992b","~:node/id":"e8f2328c-62cf-4dca-849f-73746f78304c"}],"~:diff":""},{"~:article/nextjournal.id":"~u5b45e08b-5b96-413e-84ed-f03b5b65bd66","~:change/nextjournal.id":"~u5b75173e-3d02-475e-a514-fbfbe28d4543","~:node/id":"0149f12a-08de-4f3d-9fd3-4b7a665e8624"},{"~:runtime/inherited-environment-variables":{"~#list":[{"~:name":"PATH","~:value":"/opt/conda/bin:/usr/local/nvidia/bin:/usr/local/cuda/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"},{"~:name":"NVIDIA_VISIBLE_DEVICES","~:value":"void"},{"~:name":"NVIDIA_DRIVER_CAPABILITIES","~:value":"compute,utility"},{"~:name":"LC_ALL","~:value":"en_US.UTF-8"},{"~:name":"LANGUAGE","~:value":"en_US.en"},{"~:name":"LANG","~:value":"en_US.UTF-8"},{"~:name":"BASH_ENV","~:value":"/.bash_profile"},{"~:name":"NEXTJOURNAL_MOUNT_CUDA","~:value":"9.2-cudnn7-devel-ubuntu18.04"}]},"~:change/inserted-at":"~t2018-08-16T06:18:38.978Z","~:transclusion":{"~:article/nextjournal.id":"~u5b45e08b-5b96-413e-84ed-f03b5b65bd66","~:change/nextjournal.id":"~u5b75173e-3d02-475e-a514-fbfbe28d4543","~:node/id":"0149f12a-08de-4f3d-9fd3-4b7a665e8624"},"~:name":"Python 3","~:docker/environment-image":"eu.gcr.io/nextjournal-com/environment@sha256:4a98ce5bded867f83a6d64d4578a06a67241c7448fb89c7f2c1eae2ae2897afe","~:type":"~:nextjournal","~:environment?":true,"~:language":"bash","~:id":"0149f12a-08de-4f3d-9fd3-4b7a665e8624","~:kind":"runtime","~:changed?":false,"~:error":null,"~:environment":["~:environment","0ac0b628-8b41-44d3-9023-3a688335af14"],"~:runtime/environment-variables":[{"~:name":"MPLBACKEND","~:value":"svg"}],"~:diff":""},{"~:article/nextjournal.id":"~u5b45e6f7-fe51-488a-b89b-1c8f74dfb387","~:change/nextjournal.id":"~u5b751e08-cc0f-4e29-8944-bd8f3056cdc6","~:node/id":"e8cb826f-2c7d-4cdd-b45b-0ad4282c5394"},{"~:runtime/inherited-environment-variables":{"~#list":[{"~:name":"PATH","~:value":"/usr/local/nvidia/bin:/usr/local/cuda/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"},{"~:name":"LC_ALL","~:value":"en_US.UTF-8"},{"~:name":"LANGUAGE","~:value":"en_US.en"},{"~:name":"LANG","~:value":"en_US.UTF-8"},{"~:name":"BASH_ENV","~:value":"/.bash_profile"},{"~:name":"NEXTJOURNAL_MOUNT_CUDA","~:value":"9.2-cudnn7-devel-ubuntu18.04"},{"~:name":"NVIDIA_VISIBLE_DEVICES","~:value":"void"},{"~:name":"NVIDIA_DRIVER_CAPABILITIES","~:value":"compute,utility"}]},"~:change/inserted-at":"~t2018-08-16T06:47:36.613Z","~:transclusion":{"~:article/nextjournal.id":"~u5b45e6f7-fe51-488a-b89b-1c8f74dfb387","~:change/nextjournal.id":"~u5b751e08-cc0f-4e29-8944-bd8f3056cdc6","~:node/id":"e8cb826f-2c7d-4cdd-b45b-0ad4282c5394"},"~:name":"R Default","~:docker/environment-image":"eu.gcr.io/nextjournal-com/environment@sha256:412d34ee5072c6a95d73031cd97171914b430eeb6f7366b00d6db8051e7034d2","~:type":"~:nextjournal","~:environment?":true,"~:language":"bash","~:id":"e8cb826f-2c7d-4cdd-b45b-0ad4282c5394","~:kind":"runtime","~:changed?":false,"~:error":null,"~:environment":["~:environment",{"~:article/nextjournal.id":"~u5b45dad0-dfdf-4576-9b8c-f90892e74c94","~:change/nextjournal.id":"~u5b750a51-bd70-424c-9e46-864bc3dc85f5","~:node/id":"01204d2e-3c51-4802-88f3-a128e4d85a3e"}],"~:diff":""},{"~:article/nextjournal.id":"~u0289217e-a1bb-440d-9224-f32b960a271a","~:change/nextjournal.id":"~u5b7430ce-e0bf-4c81-b2c1-aee21676992b","~:node/id":"e8f2328c-62cf-4dca-849f-73746f78304c"},{"~:runtime/inherited-environment-variables":{"~#list":[{"~:name":"PATH","~:value":"/usr/local/nvidia/bin:/usr/local/cuda/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"},{"~:name":"LC_ALL","~:value":"en_US.UTF-8"},{"~:name":"LANGUAGE","~:value":"en_US.en"},{"~:name":"LANG","~:value":"en_US.UTF-8"},{"~:name":"DEBIAN_FRONTEND","~:value":"noninteractive"},{"~:name":"BASH_ENV","~:value":"/.bash_profile"}]},"~:change/inserted-at":"~t2018-08-15T13:55:26.111Z","~:transclusion":{"~:article/nextjournal.id":"~u0289217e-a1bb-440d-9224-f32b960a271a","~:change/nextjournal.id":"~u5b7430ce-e0bf-4c81-b2c1-aee21676992b","~:node/id":"e8f2328c-62cf-4dca-849f-73746f78304c"},"~:name":"Julia 1.0","~:docker/environment-image":"eu.gcr.io/nextjournal-com/environment@sha256:6f79164671b0b2e73cce884a8749d65c466d317815f70be64d2fdb367ebb7ae1","~:type":"~:nextjournal","~:environment?":true,"~:language":"bash","~:id":"e8f2328c-62cf-4dca-849f-73746f78304c","~:kind":"runtime","~:changed?":false,"~:error":null,"~:environment":["~:environment",{"~:node/id":"01204d2e-3c51-4802-88f3-a128e4d85a3e","~:article/nextjournal.id":"~u5b45dad0-dfdf-4576-9b8c-f90892e74c94","~:change/nextjournal.id":"~u5b45dded-b4f6-4d1b-8e82-0f54b79c3333"}],"~:runtime/environment-variables":[{"~:name":"PATH","~:value":"/usr/local/julia/bin:/usr/local/nvidia/bin:/usr/local/cuda/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"},{"~:name":"JULIA_PATH","~:value":"/usr/local/julia"},{"~:name":"JULIA_GPG","~:value":"3673DF529D9049477F76B37566E3C7DC03D6E495"},{"~:name":"JULIA_VERSION","~:value":"1.0.0"}],"~:diff":""},{"~:node/id":"77962345-223d-452b-aec7-697a2835df9c","~:article/nextjournal.id":"~u5b460bf8-1d8f-49ba-b922-771730bdb579","~:change/nextjournal.id":"~u5b460d01-6b78-4c68-a3dc-757613d9b3e0"},{"~:runtime/inherited-environment-variables":{"~#list":[{"~:name":"PATH","~:value":"/opt/conda/bin:/usr/local/nvidia/bin:/usr/local/cuda/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"},{"~:name":"MPLBACKEND","~:value":"svg"},{"~:name":"LC_ALL","~:value":"en_US.UTF-8"},{"~:name":"LANGUAGE","~:value":"en_US.en"},{"~:name":"LANG","~:value":"en_US.UTF-8"},{"~:name":"DEBIAN_FRONTEND","~:value":"noninteractive"},{"~:name":"BASH_ENV","~:value":"/.bash_profile"}]},"~:change/inserted-at":"~t2018-07-11T13:58:25.290Z","~:transclusion":{"~:node/id":"77962345-223d-452b-aec7-697a2835df9c","~:article/nextjournal.id":"~u5b460bf8-1d8f-49ba-b922-771730bdb579","~:change/nextjournal.id":"~u5b460d01-6b78-4c68-a3dc-757613d9b3e0"},"~:name":"Jupyter Default","~:docker/environment-image":"eu.gcr.io/nextjournal-com/environment@sha256:f1aceea0c6c3acb91c77c0da2dfa5732856f9d16c0f872fc8b35ad415d5619e8","~:type":"~:nextjournal","~:environment?":true,"~:language":"bash","~:id":"77962345-223d-452b-aec7-697a2835df9c","~:kind":"runtime","~:changed?":false,"~:error":null,"~:environment":["~:environment",{"~:node/id":"940b6faa-77d6-4977-8262-18b62e73509a","~:article/nextjournal.id":"~u5b45e08b-5b96-413e-84ed-f03b5b65bd66","~:change/nextjournal.id":"~u5b45e65b-cb0a-4dfd-9530-cf379acc0510"}],"~:diff":""},{"~:article/nextjournal.id":"~u5b45e08b-5b96-413e-84ed-f03b5b65bd66","~:change/nextjournal.id":"~u5b75173e-3d02-475e-a514-fbfbe28d4543","~:node/id":"02749dd5-944f-44c0-9fe5-26ab6635296c"},{"~:runtime/inherited-environment-variables":{"~#list":[{"~:name":"PATH","~:value":"/opt/conda/bin:/usr/local/nvidia/bin:/usr/local/cuda/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"},{"~:name":"NVIDIA_VISIBLE_DEVICES","~:value":"void"},{"~:name":"NVIDIA_DRIVER_CAPABILITIES","~:value":"compute,utility"},{"~:name":"LC_ALL","~:value":"en_US.UTF-8"},{"~:name":"LANGUAGE","~:value":"en_US.en"},{"~:name":"LANG","~:value":"en_US.UTF-8"},{"~:name":"BASH_ENV","~:value":"/.bash_profile"},{"~:name":"NEXTJOURNAL_MOUNT_CUDA","~:value":"9.2-cudnn7-devel-ubuntu18.04"}]},"~:change/inserted-at":"~t2018-08-16T06:18:38.978Z","~:transclusion":{"~:article/nextjournal.id":"~u5b45e08b-5b96-413e-84ed-f03b5b65bd66","~:change/nextjournal.id":"~u5b75173e-3d02-475e-a514-fbfbe28d4543","~:node/id":"02749dd5-944f-44c0-9fe5-26ab6635296c"},"~:name":"Python 2","~:docker/environment-image":"eu.gcr.io/nextjournal-com/environment@sha256:7e2b7e0accabd6392f5989360f830076846e7bb79e2126d7e2416e6fa22b41a8","~:type":"~:nextjournal","~:environment?":true,"~:language":"bash","~:id":"02749dd5-944f-44c0-9fe5-26ab6635296c","~:kind":"runtime","~:changed?":false,"~:error":null,"~:environment":["~:environment","badcbdc9-f404-418d-9ec6-2c51348d5206"],"~:runtime/environment-variables":[{"~:name":"MPLBACKEND","~:value":"svg"}],"~:diff":""},{"~:article/nextjournal.id":"~u5b45dad0-dfdf-4576-9b8c-f90892e74c94","~:change/nextjournal.id":"~u5b750a51-bd70-424c-9e46-864bc3dc85f5","~:node/id":"01204d2e-3c51-4802-88f3-a128e4d85a3e"},{"~:runtime/inherited-environment-variables":{"~#list":[{"~:name":"PATH","~:value":"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"}]},"~:change/inserted-at":"~t2018-08-16T05:23:29.289Z","~:transclusion":{"~:article/nextjournal.id":"~u5b45dad0-dfdf-4576-9b8c-f90892e74c94","~:change/nextjournal.id":"~u5b750a51-bd70-424c-9e46-864bc3dc85f5","~:node/id":"01204d2e-3c51-4802-88f3-a128e4d85a3e"},"~:name":"Minimal Bash","~:docker/environment-image":"eu.gcr.io/nextjournal-com/environment@sha256:37caaff71128fe0d4f1aabdd0e73665b23378153a3f72d963f54f6d0d5aada8e","~:type":"~:nextjournal","~:environment?":true,"~:language":"bash","~:id":"01204d2e-3c51-4802-88f3-a128e4d85a3e","~:kind":"runtime","~:changed?":false,"~:error":null,"~:environment":["~:environment","ee160d76-93be-4fd6-8eed-05b0cc4ed09b"],"~:runtime/environment-variables":[{"~:name":"PATH","~:value":"/usr/local/nvidia/bin:/usr/local/cuda/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"},{"~:name":"LC_ALL","~:value":"en_US.UTF-8"},{"~:name":"LANGUAGE","~:value":"en_US.en"},{"~:name":"LANG","~:value":"en_US.UTF-8"},{"~:name":"BASH_ENV","~:value":"/.bash_profile"},{"~:name":"NEXTJOURNAL_MOUNT_CUDA","~:value":"9.2-cudnn7-devel-ubuntu18.04"}],"~:diff":""}]},"~:default-environments":{"~:nextjournal":{"bash":[{"~:article/nextjournal.id":"~u5b45dad0-dfdf-4576-9b8c-f90892e74c94","~:change/nextjournal.id":"~u5b750a51-bd70-424c-9e46-864bc3dc85f5","~:node/id":"01204d2e-3c51-4802-88f3-a128e4d85a3e"}],"python":[{"~:article/nextjournal.id":"~u5b45e08b-5b96-413e-84ed-f03b5b65bd66","~:change/nextjournal.id":"~u5b75173e-3d02-475e-a514-fbfbe28d4543","~:node/id":"0149f12a-08de-4f3d-9fd3-4b7a665e8624"},{"~:article/nextjournal.id":"~u5b45e08b-5b96-413e-84ed-f03b5b65bd66","~:change/nextjournal.id":"~u5b75173e-3d02-475e-a514-fbfbe28d4543","~:node/id":"02749dd5-944f-44c0-9fe5-26ab6635296c"}],"r":[{"~:article/nextjournal.id":"~u5b45e6f7-fe51-488a-b89b-1c8f74dfb387","~:change/nextjournal.id":"~u5b751e08-cc0f-4e29-8944-bd8f3056cdc6","~:node/id":"e8cb826f-2c7d-4cdd-b45b-0ad4282c5394"}],"julia":[{"~:article/nextjournal.id":"~u5b18ea0a-61b4-42dd-8743-7a4c62479773","~:change/nextjournal.id":"~u5b79b7be-159b-4ac9-8b16-844c4f06f6a7","~:node/id":"e8f2328c-62cf-4dca-849f-73746f78304c"},{"~:article/nextjournal.id":"~u0289217e-a1bb-440d-9224-f32b960a271a","~:change/nextjournal.id":"~u5b7430ce-e0bf-4c81-b2c1-aee21676992b","~:node/id":"e8f2328c-62cf-4dca-849f-73746f78304c"}],"clojure":[{"~:node/id":"19e3027e-20bc-47c9-8f60-edcdbf613a23","~:article/nextjournal.id":"~u5b45eb52-bad4-413d-9d7f-b2b573a25322","~:change/nextjournal.id":"~u5b45f778-3e8c-4dc6-807a-f83956c63dca"}]},"~:jupyter":{"python":[{"~:node/id":"77962345-223d-452b-aec7-697a2835df9c","~:article/nextjournal.id":"~u5b460bf8-1d8f-49ba-b922-771730bdb579","~:change/nextjournal.id":"~u5b460d01-6b78-4c68-a3dc-757613d9b3e0"}],"r":[{"~:node/id":"9571eacc-e781-4fea-80de-1d9145e7aa12","~:article/nextjournal.id":"~u5b34cf3c-172d-4c84-a965-12ff182d12cf","~:change/nextjournal.id":"~u5b3a3b44-4259-430c-b1ee-98a0a2928dd6"}],"julia":[{"~:article/nextjournal.id":"~u028acb25-56e5-4ede-bf23-552785783878","~:change/nextjournal.id":"~u5b7d73b5-7b33-4d04-ac5e-9c0b8ec6128d","~:node/id":"90436991-2923-44bb-b0d1-f235aec846ef"}]}}}}


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK