{
  "path": "/julia-1-11",
  "site": "at://did:plc:gfrmhdmjvxn2sjedzboeudef/site.standard.publication/3md7ylshxzk2y",
  "$type": "site.standard.document",
  "title": "Cool Julia 1.11 features",
  "content": {
    "$type": "site.standard.document#markdown",
    "value": "There's some cool Julia features in the [NEWS files](https://github.com/JuliaLang/julia/blob/v1.11.0-alpha2/NEWS.md) I thought I'd highlight. Very brief stuff but good to note.\n\n## The `Memory` type for arrays\n\nYou can read more about the design of this feature [here](https://hackmd.io/NnLXBeoyRymWgPtHYlW7-A?view#New-Builtin-functions). It's a really good design doc and I'd recommend taking a look. I've summarized it here, but please check it out for a much more thoughtful treatment. Thanks to [Oscar Smith](https://github.com/oscardssmith) for the work. \n\nArray types are powerful + general, but have a few shortcomings:\n\n- They use a lot of C, which means the Julia/LLVM compiler can't work magic\n- There's lots of overhead (also due to C calls)\n- `push!` is slow\n- No element-wise atomic operations\n\nThe `Memory` type is out, which is a low-level type that is intended to address some of this stuff. Some performance improvements:\n\n- Array appends (`push!`) are about 2.2x faster\n- Empty array gen is 3x faster (`Int[]`)\n- 80% faster for empty `Memory` implementers `Dict{Int,Int}`\n\nThe system image is a little larger.\n\n\n## `Lockable` for carrying locks with resources\n\nA common pattern when working with multithreaded code is to use a lock to [protect a value](https://docs.julialang.org/en/v1/manual/multi-threading/#Data-race-freedom) from multiple threads accessing it at the same time. For example, you might do something like\n\n```julia\nx = [1,2,3]\nlck = Threads.SpinLock()\n\nThreads.@threads for i in 1:100\n    position = i % 3 + 1\n    lock(lck) do\n        x[position] += 1\n    end\nend\n```\n\nThe above code is safe because the `lock` function ensures that only one thread can access the `x` array at a time -- no data races.\n\n`Lockable` is a super convenient feature where you can attach a lock to a resource, so you don't have to worry about managing the lock yourself. Here's an example:\n\n```julia\nz = Lockable([1,2,3], Threads.SpinLock())\nThreads.@threads for i in 1:100\n    position = i % 3 + 1\n    lock(z) do x\n        x[position] += 1\n    end\nend\n```\n\nNotice that now we're just using `lock` on the raw resource `z` and the lock is managed for us. This is a nice feature because it makes the code cleaner and easier to read.\n\n## The `public` keyword\n\nThe `public` keyword is applied to symbols that are considered part of the public API of a module, but are not exported when you call `using`. This is part of the wider discussion in the Julia community that exporting everything is not always the best idea -- there's been a lot of clutter and such with people being trigger-happy about `export`s. Thanks to [Lilith Hafner](https://github.com/LilithHafner) for the work.\n\nAs an example, you might have a module like\n\n```julia\nmodule MyModule\n\nexport foo, bar\n\nfoo() = println(\"foo\")\nbar() = println(\"bar\")\nbaz() = println(\"baz\") # not exported, have to use MyModule.baz()\n\nend # module\n```\n\nwhen you `using MyModule`, you get `foo` and `bar` directly accessible:\n\n```julia\njulia> using MyModule\n\njulia> foo()\nfoo\n\njulia> bar()\nbar\n\njulia> MyModule.baz()\n```\n\nNow, you'll be able to use public functions like `foo` and `bar` without having to `export` them. This is a nice feature because it allows you to keep your module clean and not export everything.\n\n```julia\nmodule MyModule\n\npublic foo\nexport bar\n\nfoo() = println(\"foo\")\nbar() = println(\"bar\")\nbaz() = println(\"baz\") # not exported, have to use MyModule.baz()\n\nend # module\n```\n\nNow you'd have the following behavior:\n\n```julia\njulia> using MyModule\n\njulia> MyModule.foo() # have to use MyModule.foo() because it's not exported\nfoo\n\njulia> bar() # can use bar() directly, as it is exported\nbar\n\njulia> MyModule.baz() # no change\nbaz\n```\n\nI'm curious to see how the community will use this stuff. I'm not sure it's immediately obvious to me how I'll use it, but it seems like standard engineering practice.  \n\n## The `:greedy` thread scheduler\n\nYou can now use a greedy thread scheduler, which greedily works on iterator elements as they are produced. Greedy threads simply take the next available task in an iterator without regard to how hard the task is, how many threads there are, etc. If you have a lot of tasks that are all about the same difficulty, greedy scheduling can be a good choice. \n\n```julia\nThreads.@threads :greedy for i in 1:100\n    println(i)\nend\n```\n\nJulia has the other scheduling options `:dynamic` and `:static`, which are more sophisticated and can be more efficient in some cases. `:static` will partition the iterator into chunks and assign each chunk to a thread, while `:dynamic` will dynamically allocate small chunks to threads. `:dynamic` is the default scheduler, but I suspect `:greedy` will be useful in some repeated, small multithreading tasks.\n\nThe [PR](https://github.com/JuliaLang/julia/pull/52096) is here. Thanks to [Valentin Bogad/Sukera](https://seelengrab.github.io/about/)."
  },
  "description": "A look at exciting new features in Julia 1.11, including the Memory type for faster arrays, Lockable for thread-safe resources, and other performance improvements.",
  "publishedAt": "2024-03-25T00:00:00.000Z",
  "textContent": "There's some cool Julia features in the NEWS files I thought I'd highlight. Very brief stuff but good to note.\n\nThe  type for arrays\n\nYou can read more about the design of this feature here. It's a really good design doc and I'd recommend taking a look. I've summarized it here, but please check it out for a much more thoughtful treatment. Thanks to Oscar Smith for the work. \n\nArray types are powerful + general, but have a few shortcomings:\nThey use a lot of C, which means the Julia/LLVM compiler can't work magic\nThere's lots of overhead (also due to C calls)\nis slow\nNo element-wise atomic operations\n\nThe  type is out, which is a low-level type that is intended to address some of this stuff. Some performance improvements:\nArray appends () are about 2.2x faster\nEmpty array gen is 3x faster ()\n80% faster for empty  implementers \n\nThe system image is a little larger.\n\n for carrying locks with resources\n\nA common pattern when working with multithreaded code is to use a lock to protect a value from multiple threads accessing it at the same time. For example, you might do something like\n\nThe above code is safe because the  function ensures that only one thread can access the  array at a time -- no data races.\n\n is a super convenient feature where you can attach a lock to a resource, so you don't have to worry about managing the lock yourself. Here's an example:\n\nNotice that now we're just using  on the raw resource  and the lock is managed for us. This is a nice feature because it makes the code cleaner and easier to read.\n\nThe  keyword\n\nThe  keyword is applied to symbols that are considered part of the public API of a module, but are not exported when you call . This is part of the wider discussion in the Julia community that exporting everything is not always the best idea -- there's been a lot of clutter and such with people being trigger-happy about s. Thanks to Lilith Hafner for the work.\n\nAs an example, you might have a module like\n\nwhen you , you get  and  directly accessible:\n\nNow, you'll be able to use public functions like  and  without having to  them. This is a nice feature because it allows you to keep your module clean and not export everything.\n\nNow you'd have the following behavior:\n\nI'm curious to see how the community will use this stuff. I'm not sure it's immediately obvious to me how I'll use it, but it seems like standard engineering practice.  \n\nThe  thread scheduler\n\nYou can now use a greedy thread scheduler, which greedily works on iterator elements as they are produced. Greedy threads simply take the next available task in an iterator without regard to how hard the task is, how many threads there are, etc. If you have a lot of tasks that are all about the same difficulty, greedy scheduling can be a good choice. \n\nJulia has the other scheduling options  and , which are more sophisticated and can be more efficient in some cases.  will partition the iterator into chunks and assign each chunk to a thread, while  will dynamically allocate small chunks to threads.  is the default scheduler, but I suspect  will be useful in some repeated, small multithreading tasks.\n\nThe PR is here. Thanks to Valentin Bogad/Sukera."
}