OCaml
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Write
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Help
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Write
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    --- tags: minutes --- # Multicore Effects meeting notes ## 21 Jan 2022 - @talex5 - @patricoferris - @bikal - @sudha - @avsm ThomasL: porting Dream to Eio, using Lwt_eio internally. The main tutorial examples are now all working: https://github.com/aantron/dream/pull/194/files Eio now supports OCaml 5.00, with compatibility with 4.12+domains too (as 5.00 is hard to install at the moment). We should probably make an Eio 0.1 release soon. Patrick: collecting together all the multicore stuff here: https://github.com/patricoferris/awesome-multicore-ocaml/ Getting everything running together is tricky due to diamond dependencies, but working on it. Bikal: has been testing with 5.00. Hit lots of problems with broken packages due to deprecated stuff being removed. Sudha: testing 5.00 on Windows. Had some problems with CI, but now has a Windows VM for testing. Anil: worried the Leo objects to objects. There are some arguments in the rationale document, however. ## 14 Jan 2022 - @talex5 - @patricoferris - @avsm - Bikal Anil is keen for eio to track OCaml 5.00+trunk. Breaking compatibility with 4.12+domains is OK. Patrick has an awesome-eio repository, linking to various Eio projects (link?). Bikal is working on cohttp-eio, which is working well now. ThomasL is looking at Dream (currently segfaults on multicore due to ssl bug). Antron is interested in Eio support. Looking for feedback on Eio multicore guide. ## 07 Jan 2022 - @talex5 - @patricoferris - deepali - @avsm - Bikal - Sudha ThomasL added more text to the rationale document, filed issues about various missing features, addded a README example of caching, fixed some problems with error reporting, and made [Lwt_eio](https://github.com/talex5/lwt_eio), an adaptor to allow running Lwt code alongside Eio code to help with porting. Bikal is testing Lwt_eio. Will work on creating a cohttp-eio library. Sudha has adding polling support to the luv backend. This will allow Lwt_eio to work with that too. Patrick has the macos backend mostly working, and it should work on ios too. Is experimenting with direct-style JSON parsing, but unclear how effects help here as it still needs to produce a lazy stream of items rather than returning one final result, as Angstrom does. Deepali is measuring the speed of IO using eio and domainslib: [report](https://docs.google.com/document/d/11F0DJHVjdxByYJxRrhwC186S85lP0cTR-y-87_70yEA/edit) and [code](https://github.com/deepali2806/code-snippets/). Should try on tmpfs. Also, record Linux version, etc to make results reproducible. Anil mentioned that Jake is now looking at using post-POSIX APIs for e.g. capabilities, which should work well with Eio. Martin is using Eio for a tree simulator. Bartosz is doing lock-free structures. He might want to check Eio's [promise.ml](https://github.com/ocaml-multicore/eio/blob/main/lib_eio/promise.ml) file. The other concurrency primitives could use some lock-free goodness too. ## 3rd Dec 2021 - @talex5 - @patricoferris - deepali - @avsm - @ctk21 - @kayceesrk - Bikal Lots of discussion about how to combine domainslib and eio. It's not clear how e.g. a LIFO and a FIFO scheduler can coexist usefully in a single domain. When a task in the nested scheduler performs IO using the outer scheduler, it will block the whole inner scheduler. To fix this, the schedulers will need to be aware of each other. For 5.00, it makes sense to provide a single library (Eio), while continuing to do research into other scheduling options. We want to avoid an Lwt/Async style split. ThomasL has been continuing to add support for multiple domains to Eio. The Luv backend now has a lock-free queue. `make bench` has been added, which runs various multi-domain benchmarks. Patrick is looking at adding support for GCD, based on the existing libuv backend. Is also porting jsonm to eio. Bikal is removing the use of Lwt tasks from Tezos. Wants to make sure all CPUs can be used. Is planning to test this on Eio. Deepali is looking at how Rust deals with having two separate threading systems (similar to our eio/domainslib split). Wants to perform a parallel grep operation on large files. Would be interesting to know the performance difference between eio and domainslib for this. ## 26th Nov 2021 - @talex5 - @patricoferris - deepali Short meeting as not many people available. Notes: - Deepali is working with KC. Wants to port the Mirage libraries to use direct style. e.g. tcpip or a web server. - talex5: just merged https://github.com/ocaml-multicore/eio/pull/99 which adds support for communicating between domains. Not optimised yet. - Patrick: "I don’t have much time to work on effects, so what should be done about the GCD branch (it’s pretty far behind main) but to get it back up to main shouldn’t be awful if it is only filesystem support (not networking), but it would also require CI support to make sure it continues working." ## 18th June 2021 - @avsm - @ctk21 - @kayceesrk - @patricoferris - @talex5 Notes: - Eio now has a filesystem abstraction with capabilities: https://github.com/ocaml-multicore/eio#filesystem-access Is uses `openat2` on Linux. - Rust has a new capability system (cap-std, https://blog.sunfishcode.online/introducing-cap-std/). Might be worth checking how they do this in a cross-platform way. - Switches need to reraise exceptions. Need to use `Printexc.raise_with_backtrace` to get correct backtraces. - Would be useful if OCaml had chained exceptions. This way, you can wrap an exception without losing the original (e.g. a cross-platform `Permission_denied` exception, caused by a `Unix_error` if you need the full details.) Also useful for finalisers, to record that one exception happened while handling another. See e.g. https://www.python.org/dev/peps/pep-3134/ - Would be good to get some early feedback on the Eio API. - GCD on macos seems slow. Possibly due to it creating lots of extra threads because the main one is blocked. ## 11th June 2021 - @avsm - @craigfe - @ctk21 - @kayceesrk - @patricoferris - @talex5 Notes: - Cancellation support in `uring`, and lifted to `eieio`. Can also cancel sleeps -- want to add support for e.g. `pick`. - What should we do with this cast? (https://github.com/ocaml-multicore/ocaml-uring/blob/main/lib/uring/uring_stubs.c#L119) - Concurrency primitives currently provided by multicore currently block the entire domain, so these aren't immediately usable for scheduling. @kayceesrk: primitives with the right semantics could be implemeted in terms of e.g. https://github.com/ocaml-multicore/effects-examples/blob/master/mvar/MVar.mli. ## 4th June 2021 - @avsm - @craigfe - @ctk21 - @kayceesrk - @patricoferris - @talex5 Discussion of @talex5's performance benchmarking results: ![](https://i.imgur.com/HfMNQbx.png) Overall takeaways: - effects have negligible overhead for this use-case; - `httpaf` suffering some overhead from hand-implementation of fibres. These benchmark cases are very low-level and simple, which is not where OCaml shines against Rust. @ctk21: could we use more complex scenarios that are relatively simple to implement in OCaml, but not in lower-level languages? e.g. use of `https`, staging, application features (caching). @kayceesrk: would like to what the impact of the GC is on these scenarios (number of minor / major GC cycles, exact memory used per request, using `v=0x800`). If the GC is having a significant effect, then we could tune it: e.g. space overhead. Not yet benchmarking parallelism. ## 21st May 2021 - @avsm - @ctk21 - @kayceesrk - @talex5 - @patricoferris The OCaml memory model paper left some things as "future work" (acquire/release). This is needed for high performance lock-free structures. If we don't add an official way to do it, people will likely try themselves and make something that only works on x86. Need to discuss this with sdolan. Tom has a fast work-stealing system, which can be used once we need communication between domains. Testing splice support with the eio Linux backend. It gives a very high-performance `cat` automatically. However, the fallback method of doing reads and writes via a pre-shared chunk is surprisingly slow - need to profile this. We should test eio on the [C10k problem](https://en.wikipedia.org/wiki/C10k_problem) (and C10m). We need a network abstraction that can handle e.g. a backend that natively supports http requests (e.g. JavaScript). Could do this by adding probing to the stdenv object. ## 14th May 2021 - @avsm - @ctk21 - @kayceesrk - @talex5 - sadiq Looking at structured concurrency (https://github.com/ocaml-multicore/eio/blob/main/README.md). - Does the `~sw` argument really need to be passed explicitly? Could it come from the context? The problem is that cleanup operations still need to run after cancellation has been requested, so doing it implicitly would be a problem. Next steps: - Want to try using `sendfile` as an optimisation. - BSD netgraph in userspace? - Try multiple domains. We probably need to get io-uring to release to runtime lock when it waits. ## 7th May 2021 - @craigfe - @ctk21 - @kayceesrk - @talex5 @talex5: Deployed @ewanmellor's multicore CI and enabled for `io_uring` and `eioio` :tada: @craigfe: no updates. @kayceesrk: gave another talk on multicore effects. Working on ParaFuzz with a student: a tool for fuzzing multicore OCaml programs with AFL. Will be submitted as a talk to the OCaml workshop. Ideally want another multicore talk for the workshop this year. Could we submit something re. parsing with effects? Yes; we could show that a simple blocking parser can be used concurrently without modification thanks to effects, showing examples of Angstrom with and without callbacks. Status of CTF tracing: should be merged in a few days, but doesn't include the user-level tracing yet. ## 30th April 2021 - @avsm - @craigfe - @kayceesrk - @talex5 Want to allow other developers to play with modernising their async code (e.g. aantron and Dream). @talex5: we should invest some time in writing initial documentation for these libraries, even if they're going to change. @talex5: `retro multicore` benchmarks still showing slowdown on some loads (e.g. concurrent TCP connections with sequential load) if we do parallel reads and writes on a single socket. Needs access to a baremetal machine rather than a VM to get accurate numbers (`multicore-dev`). Potential usage of domains in Index: currently using systhreads and implementing an ad-hoc backpressure mechanism with yielding. Perhaps this could be swapped in with `lwt+effects`. `domainslib` has a parallel work queue. @talex5: `eioio` now contains a [Sync library](https://github.com/ocaml-multicore/eioio/pull/18) with synchronisation primitives for fibres. Using a user-space CTF framework. Need CI for these multicore libraries; will require an extra runner (@talex5 to take charge of this). Can we turn on @ewanmellor's multicore CI directly for `eioio`? (We can try.) @talex5: how to manage security of effect handlers? Transmissing an effect is insecure because all intermediate points in the stack can intercept it / potentially change its perceived environment. @kayceesrk: the latter problem is mitgated by using local / generative effect handlers (which then can't be introspected); fixing the former would require a change to remove `_` patterns for effect handlers. @avsm: we need to support multiple domains soon too. Must ensure that libraries don't spin up domains independently or we won't use the hardware well. e.g. we have 8 cores. One library creates 8 domains for itself, and another creates 4. Instead, there needs to be some top-level scheduler that finds out what the modules require and allocates domains between them. ## 23rd April 2021 - @avsm - @craigfe - @ctk21 - @kayceesrk - @talex5 @talex5: running [old httpaf benchmarks](https://github.com/ocaml-multicore/retro-httpaf-bench), comparing an implementation using Lwt_engine vs. one using `io_uring`. Would like to plug these benchmarks into some continuous system; discuss with the benchmarking team? How do we allow a package to specify compatibility with multicore OCaml? We could have packages opt-in to a multicore switch with a dep-opt on some `base-multicore` package, or opt-out via incompatibility. @avsm to work on adding a multicore runner to OCaml-CI. **TODO**: Setup benchmarking CI for running retro-httpaf-bench: https://github.com/ocaml-multicore/retro-httpaf-bench/. Add this to the medium-priority queue of benchmarking team. ## 16th April 2021 - @avsm - @craigfe - @ctk21 - @kayceesrk - @samoht - @talex5 @talex5: tracing works :tada: Using this to debug read / write sequentialisation in HTTP benchmarks. @avsm: Reading TokIO, modern Rust library for IO. They're providing a separate API for `uring`. Linked SQEs: mechanism in `uring` for expressing data-dependencies between operations (which are, by default, parallelised in the uring). @talex5: this may be necessary in httpaf, since there are sequential write pairs that should be submitted as a block. How should fibres interact with the explicit `submit` step in uring? What API do we want to use to encode data dependencies in linked batches of requests? Discussed pros and cons of using first-class values to encode this relationship, w.r.t. error handling, avoidance of redundant syncs etc. @avsm, @kayceesrk, and @talex5 to discuss this further. ## 9th April 2021 - @avsm - @craigfe - @talex5 - @kayceesrk Getting rid of explicit callbacks in `httpaf`. Have added the direct-style code to the benchmarking suite, so can now compare effects code against Lwt: https://github.com/ocaml-multicore/retro-httpaf-bench Optimisation work still to be done: some benchmarks are significantly improved, some significantly worst. Been fixing up mirage-trace-viewer (last touched in 2014) to work with modern tools/libs. Can use that to do proper profiling rather than guessing where bottlenecks are. @ctk21: still need to shape out perf tracing in a multicore world. One possible approach: using statmemprof to trace continuations from user-space. Could also try hooking into @engil's improved CTF tracing of the runtime (should be available in the next few weeks). @talex5 running CTF scheduler tracing in user-space; can we unify these? Would sequentialise GC events and user events in the same trace. @craigfe: has a prototype with index+uring. There is a problem with the design of index which makes it hard to adapt to the uring world. A data structure depends on receiving offsets from disks, but need to update it to take a batch. @patricoferris and @avsm working on GCD bindings, but it's a complex API. Need some more working experience from a MacOS developer; perhaps from the Swift world? ## 1st April 2021 Just @talex5 and @avsm a day earlier due to Easter break. @talex5 looking at httpaf this week. Found a [bug in the parser](https://github.com/inhabitedtype/httpaf/pull/194). Going to continue looking at httpaf to speed it up -- an interesting insight is that the callback style makes it quite a rewrite to turn it back into direct style. Cohttp might end up being easier to port! But going to stick with httpaf for the moment. @avsm is going to look into Tokio to see how the domain-per-uring model looks like. Suggested we ## 26th March 2021 - @avsm - @craigfe - @ctk21 - @kayseesrk - @samoht - @talex5 #### Dune internal fibres library Talked to @jdiminio about Dune internal fibres library, which he was considering open-sourcing. Will pull out example of fibre use that is independent of the build system core. Looking to coordinate this work w/ effects-dev. #### Multicore stack limit Should we raise the default stack limit in multicore? @talex5: causes devs to be more defensive by allocating on the heap. @ctk21: will discuss with the wider multicore team; unclear if some limit is necessary here, and want to be careful of ruining UX. As the stack expands, it's expanded to 2x size via copy; not segmented. Performance cost to each expansion, but should be negligible for massive stacks as logarithmically spaced. #### Leaked captured continuations Currently: if one doesn't call a continuation, then it will never be GCed. @kayceesrk: there's a non-trivial cost to attaching a finaliser to each one. Perhaps we could have a debug mode that does this and warns each time, similar to dynamic open-FD check that exists in OCaml. Want to enable library authors to provide fast APIs without being too defensive internally; if this requires multicore tooling then so be it. @avsm: so long as this doesn't require compromising the API. #### Zero-copy Should we adopt zero-copy as a goal? @talex5: good to be fast, but some concerns remain: sometimes copying is faster, sometimes passing an offset into a full buffer is a security concern (e.g. when sharing a page of memory with another process). Currently the multicore GC doesn't move roots, since it doesn't do compaction. Could we exploit this directly in user code (scrapping bigarrays)? @kayceesrk: this has always been true, because compaction can be disabled via `OCAMLRUNPARAM`. @samoht, but it's not a conistently-available property for library developers, which is prohobitive. @ctk21: this might be a good reason for avoiding complexity (stop-the-world compaction phase). @kayceesrk: even w/ compaction we could introduce an immovable string tag. #### Angstrom Using effects cleans the implementation a lot. Every operation may require further input, but most inputs do not (since input is buffered); so the direct style code is more natural. ~10% speed improvement in benchmarks, but perhaps this would be amplified in a more intense benchmark (e.g. inside a GC-intensive library like Irmin). Linux kernel version changes perf by 2x: 5.4. fast and crashy; 5.5 slow and stable. @talex5 has been bisecting between 5.4 and 5.5 to discover where the regression comes in, but difficult due to density of bugs. #### Grand central dispatch Progressing with @patricoferris, but is complex. `N:M:K` (kernel thread : userspace threads : domains). Each domain has two threads, a main one and a backup thread that runs stop-the-world GC when the main one is blocked. @kayceesrk / @avsm to discuss how best to meet GCD requirements from multicore end. #### Irmin abstracted over IO (@samoht rewrites Irmin once again...) Parallel iteration over lists difficult to translate to direct style. Probably don't need unbounded parallelism, but some kind of parmap abstraction is needed. (Similar to the Dune fibre requirements.) @kayseesrk: ideal use-case for fibres over multiple domains w/ simple combinators; will require an extension of domainslib. Currently using Lwt streams to receive file-system events, so need a multicore equivalent of this. Similar requirements in GCD, so will try to meet in the middle at `irmin-watcher`. ## 19th March 2021 * @kayceesrk * @avsm * @talex5 * @samoht * @craigfe talex5: - Added promises to the interface and have unit tests - [updated](https://github.com/talex5/httpaf/tree/eio) httpaf to the new binding code - seems to be mostly working but Linux kernel bugs. 5.10 is a reasonable base. - next: add support for cancellation and tracing support? - avsm: better to get a fully effect-based version of httpaf first to see what the maximum performance is. - fibre local storage and logs could be separate handlers. The number of hops to find out the handler will be fewer. - backtraces is one option and the traces is the other one (lwt patches) - kc: shagun exposes a user level api to push events into the common channel that we use for runtime tracing. CTF is not expressive enough to express multiple streams and we can. Max latency was because of a gc request and something happened during this time. The assumption that we made was that Lwt had a thread id (not obvious, but it doesnt). Couldnt demux the different threads. Chrome trace viewer separates thread/process concepts but there is support in viewers to visualise invidivual threads. - talex5: the tracing patch to lwt added thread IDs using Lwt's thread local variables. - kc: local state support was removed with the move to promises (there is no notion of a thread) - avsm: can we have one buffer? - kc: engil is integrating the ctf stuff into multicore in the next few weeks. ocaml/ocaml also does not support user effects, but multicore will soon. - logging: discussion about how it should look and how its not polymorphic enough. That could be fixed with tagging. not clear if this needs effect handling or not. - there's a noop operation if logs are disabled. if we use effects then we pay the costs of fibre switching. - discussion about unifying tracing and logging but keep the compiler separate. craigfe: - has stubs for index but is sidetracked by the hacl* build system. ## 12th March 2021 Participants: * @kayceesrk * @avsm * @ctk21 * @craigfe * @talex5 **Goal**: Demonstrate the utility of async io against 5.0 branch. [eioio](https://github.com/ocaml-multicore/eioio) -- effects based parallel IO for OCaml Using modern IO APIs on each platform: - io_uring ↔ Linux (@talex5 / @craigfe – prototype bindings used in `mirage/index`) - GCD ↔ MacOS (@avsm / @patricoferris) - iocp ↔ Windows kc: are we farming out io across multiple system threads. How does this work? e.g. you can use systhreads today for parallel IO even with single-core OCaml. We don't want unbounded parallism by default; it's too hard to program that way. queuing is difficult and fertile research ground. what are the applications? - lets table this and focus on io parallelism to start and come back to it. - look at other PLs like swift etc to gain start of the art. See [Swift Concurrency Roadmap](https://forums.swift.org/t/swift-concurrency-roadmap/41611). Use of the EIOIO scheduler requires the introduction of a `main` function (in application code) to supply effect handlers: value definitions must remain ambient. @talex5: - plan was to write a select-based backend - wants to write a uring interface first and then write a new backend - adding in new io_uring support for polling fds when ready for network access - useful for old versions of Linux and we need backwards compat support - has httpaf support working with it. httpaf roughly twice as fast as cohttp and then roughly twice as fast again with uring. - avsm: use most bleeding edge kernel features and then go back to deal with compatibility - plan is addition of promises but only when you want them. See [promises example from effects-examples](https://github.com/ocaml-multicore/effects-examples/blob/master/promises.ml). - avsm: want to rethink all the fd stuff and what the channel abstraction is; cross-platform considerations GCD & iocp @craigfe: - in theory could change all the unix IO operations in Irmin Index as localised in one place, but there are a bunch of places where things have crept out. - doing a rewrite on the Irmin Index side (with @pascutto) to make sure that larger blocks can be passed in one go - avsm: can we use language features (?combinators?) to express the parallelism tom: see a danger with thread pools that we want things to be super clean, but we have language parallelism that can get We need to design in back pressure for lots of things: memory, IO, accept backlog (things we monitor with Prometheus). We need a firehose channel for the effects work.

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully