12 ms·
A single line of code made a 24-core server slower than a laptop (2021)
- fulafel 3y agoAlso 24-core servers from cloud services usually run your app slower than a laptop anyway.
- orionblastar 3y ago[flagged]
- jeffbee 3y agoAtomics don’t scale. In this age that needs to be widespread elementary knowledge. They are particularly bad on armv8 without atomic extensions because that platform has no equivalent to “lock; xadd” and an atomic increment could theoretically become an infinite loop.
- iaaan 3y agoIgnorant question: what's the alternative? A normal mutex? I just sort of assumed atomics were abstractions around some type and a mutex.
- bbatha 3y agoOther way around mutexes abstract around atomics but in typical implementations will yield to the kernel scheduler fairly. That system is called futexes on Linux. The kernel will also use atomics on its end.
- kccqzy 3y agoPartition your data structure to be per-CPU and get rid of any sharing.
- MuffinFlavored 3y agohow is this done in rust or c?
- flatline 3y agoMPI partitioning by rank? I’m curious what other solutions there may be.
- bottled_poe 3y agoThis is not a language problem. It’s an algorithm design problem. There’s no silver bullet. The basic principle is to divide the problem space into independent blocks of work. How to achieve that depends on the problem.
- fooker 3y agoAlgorithm design problems, when general enough, warrant being treated as language problems! Every feature of programming languages started in this fashion.
- bottled_poe 3y agoIndeed, the holy grail (or perhaps the rapture) of programming languages is a compiler which generates the entire program with zero human-written code. I fear it may be on the horizon.
- inopinatus 3y agoThere are programming languages e.g. occam designed with the intention of exposing parallelism algebraically, but I wouldn't call them a silver bullet either.
- j16sdiz 3y agoThey are great in theory, but sucks in practice. We don't have good optimising compiler for that
- 3y ago
- otabdeveloper4 3y ago> Just don't use any shared state, bro Thank you, Sherlock. But for the rest of us: when you need shared state, lockfree atomic spinlocks are roughly 1000 times more performant than mutexes. (Not a scientific estimate, numbers taken from real-word experience.)
- petters 3y ago> spinlocks are roughly 1000 times more performant than mutexes That is absolutely not universal. See e.g. but there are of course many places discussing this: https://news.ycombinator.com/item?id=21970050 https://news.ycombinator.com/item?id=21970050
- saagarjha 3y agoLocking is hard in general. Don’t do an unbounded spin in userspace is typically good advice though. The typical mutex construction these days will spin for a little while in an attempt to take advantage of mostly uncontended locks and then yield to the kernel.
- happymellon 3y agoThis immediately popped into my head too when I saw the comment. Spinlocks are terrible, and written by people who are trying to do quick hacks because they work in terrible environments and are taught to do bad things. There is a reason that most GPU drivers are just lists of hacks to get games working correctly.
- imtringued 3y agoYeah, spinlocks sound cool but actually they are terrible. Hybrid locks are generally quite good. The only way to get better is to pin cores and let nothing else run on them. Only then will spinlocks have a chance to increase performance by a tiny bit.
- Ono-Sendai 3y agomutexes are not that slow. Uncontested (single thread): incrementing using atomics took 0.002011 s (0.2513 ns / increment) incrementing using mutex took 0.005515 s (0.6894 ns / increment) Contested (8 threads trying to increment a single protected integer): incrementing using atomics took 0.1069 s (13.36 ns / increment) incrementing using mutex took 1.970 s (246.3 ns / increment) So mutexes are roughly the same speed in the uncontested case, and about 20x slower in this heavily contested case. This is on Windows.
- stevefan1999 3y agoaka share-nothing architecture
- throwdbaaway 3y agoDon't share atomics among threads. For example, envoy proxy mostly doesn't share atomics among threads, and can scale nicely on arm64 without requiring the atomic extensions.
- cdogl 3y agoHonest question: why would atomics be necessary or useful if data isn’t shared between threads?
- NL807 3y agoBecause at some point data has to be exchanged across threads. For example a task queue might have tasks that can independently executed in a thread pool, but the queue index has to be atomically modified when some other thread emplaced a new task. Or if you want to transfer ownership of a heap allocated object between threads, you need to atomically transfer the pointer, or modify the reference count of that pointer. Things like that.
- tormeh 3y agoYou can and should use atomics, just not in any kind of hot loop. Using atomics is fine but expensive.
- loeg 3y agoYou can reduce sharing probabilistically, for example -- because contention is an N-squared problem, reducing sharing by some linear factor is enough for a large reduction in contention. You aren't eliminating contested atomics entirely, just making them low-contention rather than highly contended.
- jeffbee 3y agoIt's the other way around. Mutex is an abstraction over atomics.
- fulafel 3y agoReference counting (the use of std::sync::Arc covered in the article) is a parallelism unfriendly type of garbage collection algorithm - so use a better one. But might be that Rust doesn't make that easy.
- rramadass 3y agoNot ignorant at all, You have actually pointed to the core of the problem. IMO It is not enough to know the logical constructs used for synchronization in parallel programs, you have to know the hardware too. A little bit of everything from high-level parallel algorithms/data structures through memory consistency models, compiler optimizations to processor micro-architecture (cache coherency protocols, atomic instructions, NoC overhead etc.) is needed. Basically we need to be aware of the overheads for contention at every level in the system.
- TedDoesntTalk 3y agoWhat about variables locked with mutexes or semaphores?
- sweetjuly 3y agolocks and mutexes will perform worse than things like atomic increment.
- bluGill 3y agoMaybe. It depends on the algorithm. If you share a lot of data than a mutex, update it all then unlock is fastest. If you share little data atomics can be faster. This is case be case on both the algorithm and hardware, so nobody can say which is better. Of course not sharing at all is of course best, but often you have no choice in that.
- loeg 3y agoThe significant cost comes from contending on memory. Changing the synchronization primitive doesn't help much. You have to change your algorithm not to contend. This is significant because if you profile your code and find that a mutex is expensive, you should change your algorithm to avoid contention rather than blindly trying to change the code to use atomics instead of mutexes.
- trws 3y agoLet’s be clear here, while you are 100% right that armv8 atomics kinda suck by default, neither compare and swap nor load linked store conditional scale but some atomics can scale if implemented and used appropriately. As parent points out, an atomic increment can scale, we proved they could scale to the performance of a load in the 80s for goodness sake. The fact that arm, ppc, and some others tend to implement these in the absolute worst way possible for performance doesn’t mean atomics can’t scale.
- ghusbands 3y agoYou're not being clear - are you claiming that they do scale well on ARM/PPC, despite the "absolute worst" implementation or that they don't?
- bonzini 3y agoHe means that locks anyway have the same problems as atomics on platforms with load locked/store conditional. Therefore yes, that's a problem of the platform, but even on arm/ppc atomics scale better than locks. Which is true, but eliminating sharing works even better if possible as proved by the article.
- moonchild 3y ago> an atomic increment can scale, we proved they could scale to the performance of a load in the 80s Interesting--can you link a reference for this?
- trws 3y agoThis is the citation I most often use from that time, though the primary source is probably another reference down the line: https://dl.acm.org/doi/10.1145/69624.357206 https://dl.acm.org/doi/10.1145/69624.357206 The short version is that if atomics are implemented as part of the memory network, common cache, or memory controller, then atomics of the form “fetch-and-X” can be implemented in roughly equivalent complexity to a load of the current value (plus an instruction for the op, give it take) with the cost only scaling past that as op queues or other implementation-specific limits fire. It’s the infinite consensus ops that just can’t scale no matter what you do. The coherence and memory model matter a lot too of course, which is part of why x86 tends to be slow for atomics, while arm and ppc (with fetch-and-X extensions) or GPUs tend to do much better.
- throwawaylinux 3y ago`lock ; xadd` isn't really fundamentally different than `ll ; add ; sc; b again` The latter is a bit clunky but the core more or less implements them in the same way. Acquire a line exclusive, load value, increment it, write it back. And you can hold the line exclusive such that the conditional store failure cause is mostly a formality, and can't actually become an infinite loop. No general purpose atomics are done by shipping the operation to the cache or to memory controllers, it just doesn't work[*]. So even if they look slightly different in the core, they all end up looking exactly the same at the caches and coherency protocols, and that is where atomics are slow. Well any sharing of cache lines updates really. [*] EDIT: That is to say it doesn't work for performance, for many reasons. Some CPUs do have "remote atomics" something like that which does exactly this, but they are not intended to be broadly used.
- gpderetta 3y agoYes, AFAIK in practice many architectures special case some ll/sc sequences to guarantee forward progress and fairness.
- saagarjha 3y agoIt’s not just theoretical. In bad situations (many cores, heavy contention…) you can get cores to starve each other as they try to each poke the monitor and fail continuously. I know of at least one platform which moved to LSE immediately partly because it fixed stuff like this. LL/SC is nice from some perspectives but it fails if you scale it up and also can be difficult to reason about (cough, cough, Linux getting their cmpxchg implementation wrong for years…)
- jlokier 3y agoCan you say a bit more about what Linux got wrong in cmpxchg or provide a link?
- eldenring 3y agoAtomics scale very well if you are reading often and writing rarely.
- anaisbetts 3y agoYep, uncontended atomics are quite fast. When they're contended is when things start to slow down like OP has seen
- klabb3 3y agoSorry to nit, but this is important. Parent was saying that reads are cheap, which is true. Writes can be expensive even if uncontended, because they invalidate cache lines. I guess you could say they contend with unrelated data but that would stretch the definition a bit. So what does this mean in practice? In my view, the way to think about it is that atomic writes have non-local side effects. But since atomics are necessary for synchronization, and involves both reads and writes, we should compartmentalize and minimize synchronization as much as possible, to avoid these gnarly issues creeping up and tanking real world performance. Arc<T> (and it’s relatives in other languages) constitute textbook violations of this rule. In Rust they are everywhere in non-trivial code, including in the async runtimes themselves. Of course, they also violate (or evade if you’re generous) ownership principles of idiomatic Rust, (or “hello world-Rust”, if you will). I think we need to take a hard look as an industry at ref counting as a silver bullet escape hatch to shared data.
- loeg 3y ago> Writes can be expensive even if uncontended, because they invalidate cache lines. This isn't expensive if cache lines are uncontended, though. > I guess you could say they contend with unrelated data but that would stretch the definition a bit. I think you might be talking about "false sharing." This is real contention on the cache line due to co-location of apparently unrelated variables. > Arc<T> (and it’s relatives in other languages) constitute textbook violations of this rule. Definitely! > In Rust they are everywhere in non-trivial code Ehh.. only the hot ones matter. Most are not actually contended much, and the article's solution (unshared clone) is a very reasonable approach to scale these without an API change.
- mjan22640 3y agothe x86 lock prefix does the same loop, this is the best performant and scaling way to do it
- moonchild 3y agoContended atomics don't scale. It is possible to construct concurrent structures which contend rarely (but which must still use atomics to guard against the rare case when mediation is required). There was also an interesting paper from a few years ago about using HTM to detect contention in a scalable fashion. I will aver that such code may be difficult to reason about—shared-nothing has far more obvious correctness and performance properties. (Queueing/amortisation also works, and lands somewhere in the middle wrt ease of reasoning.) Riscv has an interesting compromise, which is to delineate a subset of ll/sc loops which is guaranteed to eventually make global progress. I do agree that it is better to include real wait-free primitives like cas and faa; but I wish that such guarantees of global progress would be provided to HTM.
- Liquid_Fire 3y ago> an atomic increment could theoretically become an infinite loop Only if your software is badly implemented. If you follow the requirements specified by the architecture, forward progress is guaranteed. Of course there is no guarantee how long it will take, but the things that make it slow are essentially the same things that make atomics slow.
- lll-o-lll 3y agoSame problem occurs with c++ std::shared_ptr. I guess all reference counting has this inherent scaling issue due to contention ruining cache lines. Makes me wonder how/if you get linear parallelism in Swift.
- malkia 3y agoNot sure what is currently in swift, but this paper described biased reference counting approach - e.g. in way two counters - one non-atomic to be used only by specific thread (supposed owner?), and another (atomic) by all other threads - so the sum of these two shows the real reference count (somewhat). Paper here - https://dl.acm.org/doi/pdf/10.1145/3243176.3243195 https://dl.acm.org/doi/pdf/10.1145/3243176.3243195 (Before reading the paper I was expecting that the additional bytes were put for the split counter, plus thread id - but it actually packs them using lower bits for reference counting). I wonder what abseil/folly/tbb do - need to check (we are heavy std::shared_ptr users, but I don't think 14 bits as described in the paper above would be enough for our use case)
- ot 3y agoCan't speak for abseil and tbb, but in folly there are a few solutions for the common problem of sharing state between a writer that updates it very infrequently and concurrent readers that read it very frequently (typical use case is configs). The most performant solutions are RCU (https://github.com/facebook/folly/blob/main/folly/synchronization/Rcu.h https://github.com/facebook/folly/blob/main/folly/synchroniz...) and hazard pointers (https://github.com/facebook/folly/blob/main/folly/synchronization/Hazptr.h https://github.com/facebook/folly/blob/main/folly/synchroniz...), but they're not quite as easy to use as a shared_ptr [1]. Then there is simil-shared_ptr implemented with thread-local counters (https://github.com/facebook/folly/blob/main/folly/experimental/ReadMostlySharedPtr.h https://github.com/facebook/folly/blob/main/folly/experiment...). If you absolutely need a std::shared_ptr (which can be the case if you're working with pre-existing interfaces) there is CoreCachedSharedPtr (https://github.com/facebook/folly/blob/main/folly/concurrency/CoreCachedSharedPtr.h https://github.com/facebook/folly/blob/main/folly/concurrenc...), which uses an aliasing trick to transparently maintain per-core reference counts, and scales linearly, but it works only when acquiring the shared_ptr, any subsequent copies of that would still cause contention if passed around in threads. [1] Google has a proposal to make a smart pointer based on RCU/hazptr, but I'm not a fan of it because generally RCU/hazptr guards need to be released in the same thread that acquired them, and hiding them in a freely movable object looks like a recipe for disaster to me, especially if paired with coroutines https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p0561r5.html https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p05...
- asicsp 3y agoPrevious discussion: https://news.ycombinator.com/item?id=29747921 https://news.ycombinator.com/item?id=29747921 (617 points | Dec 31, 2021 | 195 comments)
- hinkley 3y ago> Therefore the Arc had to stay. Instead of using a single Arc value we can use one Arc per thread. I thought the title sounded familiar, and the culprit is more or less the same (false or in this case unnecessary sharing). But I didn’t think it was quite that long ago, so maybe it’s two articles about the same classic blunder.
- malkia 3y agoin C++ std::shared_ptr (similar story) has similar effect. One of our applications (3D editor) went way slower when an artist were given a server-class machine (NUMA) and we had to ensure that all threads would run on a single CPU socket (yes they were still accessing the "other" memory, but it was better somehow).
- BazookaMusic 3y agoI might be wrong on this explanation, but the reason why it was faster might have been the following: During execution you had two kinds of memory locations, some in CPU caches and some in RAM. By running all the threads on one socket, everything accessed from the cache was just a fast cache access. Everything accessed from the memory was a slower memory load. Frequently loaded/stored locations will tend to go to the cache. In the NUMA setup, you would have a larger cache (more than one socket) which would mean that more locations were likely to be in the cache. However, if a core on a socket tries to access a location which is on another socket's cache, it will use the interconnect between them to access it. If you have an unfortunate memory layout, this can make it so that you end up having a large percentage of the accesses using the interconnect (slower than cache access) and values get swapped between the caches constantly, which forces subsequent accesses to also use the interconnect. Another way to avoid this except using just one socket is for the designer of a program to consider NUMA nodes as separate processing units and design around that. Both should be processing separate data and they should only share small amounts of data for synchronization/communication. Then the caches will be much less affected.
- malkia 3y agoThat's a pretty reasonable explanation, and one day I should sit down and write some artifical test/bench to get more details.
- loeg 3y agoCache synchronization operations between CPU cores (MOESI) is cheaper (lower latency) between cores in the same socket than across sockets, often by a factor of 2x or more. But also limiting the program to one socket would reduce contention significantly. Both help.
- Sniffnoy 3y ago(2021)
- saagarjha 3y agoUpdated
- deleted 3y ago[deleted]
- samsquire 3y agoThis is really good. Thank you for this blog post. Asynchronous code, coroutines, async/await, parallelising problems is my deep interest and I blog about it everyday. I think the easiest way to parallelise is to shard your data per thread and treat your multithreaded (or multimachine) architecture as a tree - not a graph - where dataflow doesn't need to pass between tree branches. This is similar to the Rust's "no interior mutabiliy" and Rust data structures pattern. My machine can lock and unlock 61570760 times a second. But it can count to 2 billion in 1 second. So locks are expensive. I recently worked at parallelising the A* graph search algorithm that I'm using for code generation/program synthesis. For 16 processes it takes 35 seconds to synthesise a program but with 3 processes it takes 21 seconds. I think my approach to parallelising A* needs a redesign. We hit Amdahl's law when it comes to parallelising. I need to split up my problem into spaces that don't require synchronization/serialisation. EDIT: I've mentioned this whitepaper before ("Scalability! But at what COST?") but this whitepaper would be useful reading of anybody working on multithreaded or distributed systems. In summary: single threaded programs can easily be faster and more performant (wall clock time) than multithreaded/multimachine distributed machines, but they don't scale. https://www.usenix.org/system/files/conference/hotos15/hotos15-paper-mcsherry.pdf https://www.usenix.org/system/files/conference/hotos15/hotos...
- tgv 3y agoThe basic algorithm of A* is very sequential, isn't it? It works by taking the best scoring unexpanded node and expand that. Most of the time, there's only one such node. When expansion has finished, you need to re-sort the queue/heap of unexpanded nodes. All those steps are sequential. So I guess the only gain is when the node expansion can be done in parallel; expanding the top-N nodes probably is counterproductive for many problems. How much you gain then depends on the time expansion takes. The advantage of parallelism then depends on how much time one step "down" takes.
- asimpletune 3y agoThis is correct, and Bellman-Ford, while naive compared to A*, actually parallelizes much better. That said, an optimized Bellman-Ford doesn't even resemble the original, naive version of the algorithm.
- alexott 3y agoNot directly related, but https://github.com/nosqlbench/nosqlbench https://github.com/nosqlbench/nosqlbench is very flexible benchmark tool for Cassandra and other distributed systems
- eldenring 3y agoGood article, but if i had to guess the subsequent L3 cache access after an increment is likely far overshadowed by the overhead of coherence messages, or the cores communicating between each other.
- VectorLock 3y agoThis is what I'd describe as the deepest of the deep magic.
- saagarjha 3y agoOh, the rabbit hole goes far deeper than this.
- hahhahanananana 3y agoWould love to see someone recommend some beginner-friendly books to learn more about the theory behind this blogpost. Seems like a CS degree is the only straightforward way.
- metaphor 3y agoPatterson/Hennessy on Computer Organization and Hennessy/Patterson on Computer Architecture are considered foundational canon. The implied expectation underlying "beginner-friendly" seems naively misguided; it's an advanced undergrad computer/software engineering topic in the most permissive sense, and the blog's prose appears to have been tailored with that minimum target audience in mind.
- Animats 3y agoOuch. I had no idea that contended Arc could be that expensive. I found a contention bug inside of Wine a few weeks ago. Something that is supposed to be "lockless" really had three nested spinlocks. With many threads contending for a lock, performance would drop to about 1% of normal.[1] [1] https://bugs.winehq.org/show_bug.cgi?id=54979 https://bugs.winehq.org/show_bug.cgi?id=54979
- lionkor 3y agolockless is often the wrong term, goal, idea and solution. mutexes/futexes do very well, almost zero cost when not contended.
- eldenring 3y agoLockless is not the same as lock-free, which is not the same as wait-free which seems to be what you are describing
- piyh 3y ago>Lockless is not the same as lock-free Can I get the HN comment length explanation of what this means?
- gpderetta 3y agoLock-free is a term of art with a formally defined meaning (at least one thread makes forward progress at any time). Lock-less doesn't really have a well defined meaning (first you would have to define what a lock is).
- MichaelZuo 3y agoSometimes I wish HN would have a term-of-art dictionary built in so users can check if there's a special meaning to reduce the chance of misinterpretation.
- HlessClaudesman 3y agoHey, noob question guy here. Can anyone explain why the last graph shows a slight performance drop going from 48 to 96 threads?
- kovacs_x 3y ago96 processes on 24 cores, not 96 cores. apparently 2 process per core is more efficient than 4.
- frankreyes 3y agoIt can be many things, my guess is likely memory bandwidth. There's so much MB/s the RAM can handle. Also, above 48 cores, those are hyper threading and for CPU bound tasks, hyper threading is known to be slower. Example: https://ieeexplore.ieee.org/document/7804711 https://ieeexplore.ieee.org/document/7804711 Edit: looks like there's only 12 cores per CPU so that's 24 physical cores. 48 HT cores. So the drop must be cache trashing?
- hinkley 3y agoHigher parallelism is usually about timeliness for interactive workloads, not throughput. Unless it’s just the classic fallacy that parallelism = speed. For throughput tasks it’s often the case that you go with less parallelism to reduce Amdahl’s law a few percent, and instead investing in keeping the pipeline saturated, so that the variance in concurrent tasks is lower. Work stealing being one of the more notable tricks.
- vlovich123 3y agoI feel like using hybrid-rc [1] (biased reference counting [2]) instead of Arc should be more popular. You rarely need to send data between threads so when you do you pay the atomic cost but otherwise you’re doing normal super fast arithmetic. [1] https://docs.rs/hybrid-rc/latest/hybrid_rc/ https://docs.rs/hybrid-rc/latest/hybrid_rc/ [2] https://dl.acm.org/doi/10.1145/3243176.3243195 https://dl.acm.org/doi/10.1145/3243176.3243195
- kzrdude 3y agoThanks for the link. Biased reference counting is a topic I've heard of in Python now too (the Nogil discussions). However, HybridRc would still be as contended in the scenario in the blog post, wouldn't it (before the fix that solved it)? Just checking my understanding.
- vlovich123 3y agoThat’s a good question and I’m not sure but I think so? The unit and RuntimeContext would be sent once to each thread and Rune would take the RC variant everywhere and only upgrade to ARC when they actually need ‘Send (which you don’t use). It requires changes to Rune obviously but I think just converting Arc into HybridRc types.
- fulafel 3y agoReminder: refcounting is garbage collection. There are parallelism friendly GCs too. I wonder if the same interface in Rust could accommodate them.
- lionkor 3y agoArc and shared_ptr should be used sparingly - especially in languages with more or less real ownership semantics, sharing ownership of state seems like a hack job.
- ychompinator 3y ago[dead]
- nottorp 3y agoHmm so the embarrassingly parallel task wasn't embarrassingly parallel because of an implementation detail, basically?
- klabb3 3y agoThe issue with Rust in particular is that the zero-cost fearless yadda yadda is real, but often not feasible in practice. Arc is this magical escape hatch which always appears like an uninvited guest in all non-trivial real-world programs. Heck, Arc is even part of the async specification itself. It’s very complicated and I don’t blame anyone in particular. It’s an ok solution, but it’s definitely not in the zero-cost category. I’d rather accept that this is the state of things and working towards systemic solutions in allowing borrowing in more situations, but that requires a compile-time verifiable hierarchical thread- and task model. Fun fact: Arc was partially the reason Rust disallowed borrowing across threads. Arc had already become popular and combined with thread borrowing someone demonstrated use-after-free. Borrowing across threads seemed less important at the time, so Arc was kept. This led to the famous “leaks are safe” rule, which was sold as a mere clarification of an inherent truth. It’s possible that all that was inevitable and correct, but I was never convinced by the arguments made in those old threads or the subsequent writings about it. To me, it looked like details were glossed over in order to get to a swift resolution. I’m quite content waiting for someone else to figure out whether Rust could have gone down a different path. Worst case, I’ll come down peacefully from this tiny hill. Best case, there’s an alternate timeline where Rust could live out its full potential in concurrent environments.
- andrepd 3y ago>Although using atomic instructions is often referred to as “lockless programming”, this is slightly misleading – in fact, atomic operations require some locking to happen at the hardware level. Lockless/lockfree refers to the fact that there are no deadlocks.
- dist1ll 3y agoThat's not entirely correct. Deadlock-freedom means that at least one thread will make progress in the absence of failures. Lock-freedom is a much stronger statement. It guarantees progress of at least on thread in finite time, regardless of failures. Source: Dan Alistarh, Keren Censor-Hillel, and Nir Shavit. "Are lock-free concurrent algorithms practically wait-free?" December 2013
- FeepingCreature 3y agoSmall note: > In Rust, it is very easy to generate flamegraphs with `cargo flamegraph`. ... Also in pretty much every other language that can generate perf stacktraces, because this is just a wrapper around Brendan Gregg's FlameGraph visualizer: https://github.com/brendangregg/FlameGraph https://github.com/brendangregg/FlameGraph
- emi2k01 3y ago`cargo-flamegraph` uses a port of FlameGraph written in Rust and handles the "capture" and "fold" steps so I'd say it's at least a bit easier
- FeepingCreature 3y agoOh, I see! Well then, almost-full credit to them.
- sandworm101 3y agoLet me guess: someone dared to right-click a pdf in windows explorer? My computer at work locks up every time i do that. It takes exactly 24 seconds for the server to respond. I think i could physically run back and forth to the server room in less time. (Started last month after an update. They are still trying to figure it out. I instead not use ctrl-c to copy files as that doesnt need the little menu to load. I kick myself every time i forget and accidentally right-click.)
- jcparkyn 3y agoJust in case you haven't already done this: Try using ShellExView to disable all 3rd party shell extensions. There's a good chance that something like Acrobat is running some code that doesn't play nice with your windows version (or hardware etc).
- sandworm101 3y agoIve done nothing. I can do nothing. On this network i am a simple end user, unable to change even basic settings let alone install extra software. I can only submit a work ticket and wait for our support staff to address my issue.
- salawat 3y agoThe joys of parallelism. Communication/signal propagation is hard, yet extremely rewarding once you nail it. You've really gotta be willing to dig into the guts of what you're doing though.
- willtemperley 3y agoPleasingly parallel is a far better term than “embarrassingly parallel“ - it was a poor description and I never understood why people liked using a pejorative for an elegant solution.
- birdyrooster 3y agoSome people like to be embarrassed? It’s that kind of community.
- extasia 3y agoI like the writing style of this post, it doesn't gloss over the details and conveys a lot of information from first principles. Good work pkolaczk.