_______               __                   _______
       |   |   |.---.-..----.|  |--..-----..----. |    |  |.-----..--.--.--..-----.
       |       ||  _  ||  __||    < |  -__||   _| |       ||  -__||  |  |  ||__ --|
       |___|___||___._||____||__|__||_____||__|   |__|____||_____||________||_____|
                                                             on Gopher (inofficial)
   URI Visit Hacker News on the Web
   URI   4x Smaller, 50x Faster
   DIR   text version
        71a54xd wrote 2 days ago:
        Asciinema server is still quite frankly my favorite Elixir Phoenix
        project of all time.
   URI  [1]: https://github.com/asciinema/asciinema-server
          sickill wrote 2 days ago:
          Oh wow, thank you! Frankly I'm the least proud of it, as it's been my
          first Elixir/Phoenix project and there's many things I'd have written
          differently today, especially after spending last 4 years writing
          Elixir at work. But time will hopefuly come to bring more love to it
          too :)
        moonchrome wrote 2 days ago:
        It's funny how it starts out with "immutability is really fast and GCs
        are soo good" and ends up with "rewriting everything in unmanaged code
        made it 50x faster".
        Similar how "Ruby and Python interpreters are slow but webapps are IO
        bound anyway so it doesn't matter" to "how can I get this to handle
        more than x req/sec, can we get a JIT to speed up our dog slow
          gwern wrote 1 day ago:
          One of the arguments high-level language proponents always made was
          that you could do exploratory programming and prototyping and develop
          your systems in nice easy high-level languages, and then FFI out or
          reimplement parts in the painfully low-level languages when you
          discover you need their performance.
          asciinema prototyped and worked out their design, and discovered it
          needed more performance. And then it got it. Sounds like a success
          story to me.
          smrtinsert wrote 2 days ago:
          It's almost like one side had the ability to say "we told you so" and
          they never bothered to :)
          aasasd wrote 2 days ago:
          > "Ruby and Python interpreters are slow but webapps are IO bound
          anyway so it doesn't matter" to "how can I get this to handle more
          than x req/sec, can we get a JIT to speed up our dog slow backend"
          Turns out that if you write business logic with abandon, you end up
          with a lot of business logic.
          Personally I wish that Python, Ruby and the ilk all get replaced with
          Lua, but also that Lua gets a proper `null`.
            seniorivn wrote 2 days ago:
            and array numbering from zero
              mmastrac wrote 2 days ago:
              This is why I've never been able to do anything serious with Lua.
              Small thing, but fatal for me.
        RMPR wrote 2 days ago:
        Reposting here since the first submission didn't make it to the
        I started using asciinema two months ago and I must say that it's
        excellent! One minor annoyance though, it forces the use of the default
        shell instead of using the shell you launched it in. Other than that I
        am very excited by this release, more speed is always welcome.
          sickill wrote 2 days ago:
          It does try to launch the same shell as your current one by looking
          at SHELL env variable, and only falls back to sh if it's not there.
          See: [1] It seems that's not the most reliable method.
   URI    [1]: https://github.com/asciinema/asciinema/blob/9ccf4efd4d3babc4...
        unnouinceput wrote 2 days ago:
        I get it, Clojure bad, Rust good. Yeah, absolutely nothing to do with
        the brain in the chair.
          fnordsensei wrote 2 days ago:
          More like, know when to apply Clojure and when to apply Rust.
          Having written a ClojureScript app with a Rust/WASM component, for me
          it's very clear that they have different strengths that can be
          That the author dropped CLJS along with React is fair enough, in my
          opinion. The CLJS ecosystem is pretty React-centric.
            unnouinceput wrote 1 day ago:
            Listen, Clojure isn't born yesterday, it's around for quite some
            time and it's a mature product. And when you have mature products
            that are still in development and people behind it do evolve it
            then you also get all kind of optimizations.
            I would expect the system maybe to be slower like 20 to maximum 50%
            (as in Rust to be maximum 2 times faster) but 50 times faster?.
            That's already brain in the chair mistake.
              fnordsensei wrote 1 day ago:
              If we were talking about Clojure, I would agree that 50x would
              indicate that something is off. But since we're talking
              ClojureSCRIPT, it doesn't surprise me.
              Although, fair enough, it's a rewrite, and the author has the
              benefit of experience with the first implementation when writing
              this one. A second implementation in ClojureScript would likely
              have been faster than the first one. But 50x faster? Unlikely.
              > Is WebAssembly faster than Javascript?
              > Yes - about 50 times faster in my experience. [1] Just as a
              data point that the author isn't the only one to see this type of
              performance increase.
              I expect that it isn't 50x across the board over JS, but that it
              would vary wildly across the board, including being slower if
              you're doing something trivial and end up paying more in
              serialisation to and from WASM than you gain in speed.
   URI        [1]: https://www.quora.com/Is-WebAssembly-faster-than-Javascr...
        iveqy wrote 2 days ago:
        This makes me worry less about the end of Mores law. It's clear that
        software development still has a long way to go.
        agys wrote 2 days ago:
        For my own realtime browser based ASCII projects I update the DOM
        “manually”: the biggest bottleneck is frequent -horizontal- changes
        in color as each character with a new color needs to be wrapped in its
        own  element. After all you can’t avoid a DOM repaint.
        After several benchmarks I realized that the fastest way to update the
        entire window is to compose a string and assign it to each
        line/line-element via innerHTML. I usually get 60fps with 5-8k chars in
        fullscreen (browser text rendering has become really fast).
          stjohnswarts wrote 2 days ago:
          Maybe you could benchmark it against this. It would make an interest
          HN followup post.
            sickill wrote 2 days ago:
            It's not the rendering / DOM part that got 50x faster though, so
            that would be apples to oranges.
        fnordsensei wrote 2 days ago:
        As an enthusiastic Clojure/Script user, the new tech stack absolutely
        makes sense for this application.
        For the decrease in size, I expect most of the gain to come from
        dropping ClojureScript. For the speed increase, though, I expect most
        of the gain to come from WASM. JS and ClojureScript are within the same
        margin of error compared to the performance that can be achieved with
          badestrand wrote 2 days ago:
          > Due to ClojureScript’s immutable data structures, there’s a lot
          of objects created and garbage collected all the time
          ^ From the article, sounds like a plausible cause for the speed
            fnordsensei wrote 2 days ago:
            It's almost certainly part of it. I doubt that a ClojureScript
            application written mutably (which you can do) would compare
            favorably to WASM regardless.
              sickill wrote 2 days ago:
              Before attempting JS/Rust rewrite I tried using transients [0]. I
              converted small part of the vt code to use mutable data
              structures to see if it's giving any improvements. It barely did,
              the difference was negligible. I believe it was because I didn't
              go all the way, so the perf critical pieces deep down were still
              using immutable data structures. Maybe it would bring decent
              improvement if I converted most of the vt code to transients, but
              it would absolutely destroy the idiomatic aspect of the code, and
              even then it would never compete with Rust anyway.
   URI        [1]: https://clojure.org/reference/transients
                fnordsensei wrote 2 days ago:
                I get that. And then you'd have to find a way to get rid of
                React (and Reagent/whatever was used on top of it) to get it
                all the way to as lean as possible. At this point, you'd be
                leveraging almost nothing that ClojureScript brings to the
                table, while getting most of the cons.
                  sickill wrote 2 days ago:
                  Spot on.
        MrYellowP wrote 2 days ago:
        Reading through the comments and as an assembly programmer, I think
        this whole topic is completely bonkers.
          genuine_smiles wrote 2 days ago:
        w-m wrote 2 days ago:
        The blog post mentions that seeking to any point works very well
        without having to create keyframes, just from the speed of the
        implementation. It would be great if the progress bar could actually
        respond to click-and-drag events, to quickly find a specific location.
          Tobu wrote 2 days ago:
          Scrubbing would be fun, I agree!
        atoav wrote 2 days ago:
        While I applaude the engineering effort that went into the project, I
        really dislike documentation that uses asciinema for regular
        non-interactive CLI interfaces: instead of showing me the commands in
        an overview I have to sit through the whole thing.
          rodamaral wrote 2 days ago:
          asciinema is for showing off, specially TUI interfaces.
          tomxor wrote 2 days ago:
          Those same authors would probably have used a youtube video instead,
          so asciinema's existence is not the cause of this.
          hnarn wrote 2 days ago:
          Documentation does not belong in asciinema. It’s for presentation,
          and using it as an excuse not to document is pure laziness.
          Cthulhu_ wrote 2 days ago:
          It's fine for a quick demo of a CLI application, but documentation
          and usage instructions should follow.
          qwertox wrote 2 days ago:
          It's a lean-back experience, like the difference between a book and a
          video. It does have its perks.
            atoav wrote 2 days ago:
            It has, if the thing you are showing fits the media. If you show
            the different flags of ls without explaination you would be better
            of just showing me the printout of ls -h
            If you are showing off some beautiful TLI, an interactive prompt or
            ascii animations, this is the perfect tool.
            As someone who works in film: film also doesn't lend itself to
            everything. Certain internal observations that work great in
            literature would be unwatchable on film etc.
            Or to take it to the extreme: you don't expect the overview of an
            database to be experimental performance video art.
              ReleaseCandidat wrote 2 days ago:
              > If you are showing off some beautiful TLI, an interactive
              prompt or ascii animations, this is the perfect tool.
              If you want to just show something off, a GIF (or real video) is
              the answer.
              The defining feature of asciinema is that you can copy the text
              out of the animation. Which is actually not that useful most of
              the time, because you can't search for text in asciinema (AFAIK?)
              like you can in, well, documentation.
                uasi wrote 2 days ago:
                > a GIF (or real video) is the answer.
                Why so? You can't pause or seek GIFs. GIFs and video files are
                usually several times larger than asciinema recordings of the
                same content. And you can't copy text out of them when it is
            vladvasiliu wrote 2 days ago:
            I think it can work well as a demo, but I wouldn't call that
            If you need to find something out, having to wait or randomly click
            through a video is extremely frustrating.
              omnicognate wrote 2 days ago:
              Ew, people are using it for docs? It always seemed clearly to be
              for those quick-look demos on projects' home pages, and it's
              fantastic for that.
                nyanpasu64 wrote 2 days ago:
                Asciinema is used for docs-ish in git-branchless's readme: [1]
                It does have formal docs, but I didn't fully understand the
                program after reading them. I didn't enjoy sitting through
                branchless's videos or clicking around for the right point in
   URI          [1]: https://github.com/arxanas/git-branchless/blob/master/...
                ReleaseCandidat wrote 2 days ago:
                What exactly is the advantage of asciinema over a video or a
                GIF if you're just showing something? If I don't need to copy
                text out of it, it has the same function as a video.
                  omnicognate wrote 2 days ago:
                  It's rendered by your browser so you get properly
                  subpixel-hinted text instead of the horrible, blurry,
                  compressed screenshot effect a video or gif gives. Also it's
                  more convenient to generate than faffing around with
                  screencasting software.
                  Edit: well, not (lossily) compressed in the case of a gif but
                  not as nice as text properly rendered for your display.
                  qwertox wrote 2 days ago:
                  I'd guess it's the bandwidth and compared to a GIF, you can
                  pause them. While it's not usual to copy text from it, I've
                  done that once this month and thought it was neat to be able
                  to do that. I do believe that they have an advantage over
                  1_player wrote 2 days ago:
                  It's definitely better than a GIF because you can seek
                  forward. You never know when a GIF ends, and if you're
                  distracted, you have to wait for it to loop back again.
                    dredmorbius wrote 2 days ago:
                    Note that this is a function of the GIF viewer.  An image /
                    video viewer that can play GIFs with length, speed, and
                    movement controls would afford this.
                    vlc seems to do this (Android version).
                    I'll grant that the usual methods of interacting with GIFs
                    don't do this, and am not arguing that they do.  But if you
                    really want a specific functionality, you can look for a
                    tool which might provide that.
              vidarh wrote 2 days ago:
              If it's a video, it does noe exist for me. I could watch them,
              but I have better things to watch if I want to watch something.
              So I move on to something else.
                EdwardDiego wrote 2 days ago:
                Most of us read far faster than someone umming and ahing
                through a presentation in a monotone can speak.
                At the very least, give us a frigging transcript.
                  vidarh wrote 1 day ago:
                  A transcript would be great, but it struck me in these days
                  of deep learning it's just a question of time before someone
                  does a "speedup" option for videos that instead of just
                  increasing the speed of the video, combines that with "smart"
                  cuts like removing umming and ahing and unnecessary dead
                  space, and tries to apply other simplifications as well (I'd
                  still prefer text, but I'm sure there'd be an upside for
                  people who like video).
                  And imagine even a more aggressive "video summarizer"
                  generating articles from videos with interspersed screenshots
                  or brief video segments where they matter for the
        ajaxexact wrote 2 days ago:
        > ClojureScript is not that easy to integrate with the JS ecosystem. I
        know, there’s been a lot of improvements done in this space over the
        years, and I’m sure someone will immediately point me to relevant
        docs, but it’s still the extra mile you need to go when compared to
        regular JS codebase
        This always kills me. Clojure(script) is one of the neatest languages
        I've ever used, but it is just such a pain to work with. I spent hours
        getting NPM imports working in a project, when it ought to take
        seconds. It really makes it hard to recommend, even though the language
        itself is amazing.
          nojito wrote 2 days ago:
          Fable is becoming the best way to compile to JS.
   URI    [1]: https://fable.io/
          Cthulhu_ wrote 2 days ago:
          I just wonder why not just write straight JS? Adding a layer on top
          just means you have more complexities and steps to worry about. And
          clearly, in this case it was a big compromise in terms of performance
          and bundle size.
            Scarbutt wrote 2 days ago:
            Yep, the author learnt their lesson. I bet they won't be using cljs
            again, not even for non-performant apps.
              kaliszad wrote 23 hours 44 min ago:
              Perhaps, you should ask a question, why didn't the author reverse
              the question? Something like "How on earth was my implementation
              in a JITed language 50x slower on a warmed-up benchmark?" Where
              is the output of the profiler showing the exact bottlenecks? Of
              course, you can look at the repo and deduce some stuff, but it is
              a good habit to mention some key points about the environment
              such as compiler/ language/ browser versions, compiler settings,
              the hardware used etc.
              Could he use more appropriate data structures? Could he avoid all
              the schema stuff that doesn't really improve the readability?
              Could he use better data structures later avoiding slow functions
              like update-in and migrating the bottlenecks to transducers and
              transients perhaps?
              The author just did a rewrite and that is totally ok. He is
              trying things out and that is also quite alright. He provided
              some rather high-level benchmarks that would be really time
              consuming the reproduce and explain in more detail.
              We have looked at the cljs code (e.g. [1] ) with my colleague and
              it definitely isn't the best possible Clojure(Script) code around
              from a readability nor it seems performance standpoint.
              To summarize, good that @sickill got a discussion going but it is
              best to step back and think about it in more depth. We all should
              apply more of this "extraordinary claims require extraordinary
              evidence" [2] Yes, it is his free time and good explanations/
              understanding take time. We should treat the blog entry
              accordingly. It is a good exercise in critical thinking and code
              review, if you actually take the time to at least run through the
              code briefly.
   URI        [1]: https://github.com/asciinema/vt-clj/blob/master/src/asci...
   URI        [2]: https://en.wikipedia.org/wiki/Sagan_standard
                Scarbutt wrote 20 hours 14 min ago:
                Performance was not his only pain point. He also had issues
                integrating with the JS ecosystem. IMO Clojurescript is not
                worth extra layer of complexity.
                  kaliszad wrote 3 hours 48 min ago:
                  We are using shadow-cljs [1] and that integrates with npm
                  just fine. It doesn't get much easier than that I guess and
                  if you have trouble, you can contact Thomas Heller or other
                  people on the Clojurians Slack-channel.
                  If I did any app or system, that could be written in Java or
                  JavaScript (browser or Node.js) I would take Clojure or
                  ClojureScript any day. I don't know how to match the comfort
                  and power in a different language. If it turned out e.g. by
                  using a profiler, I just need way better performance in some
                  bottlenecks and after I have exhausted all options that a) a
                  better algorithm b) better technology/ different approach
                  such as Canvas/ WebGL you name it in addition to what
                  Clojure(Script) performance features offer, I would perhaps
                  consider learning to write a module in Rust/ WebAssembly or
                  just use a bit more resources (if this was e.g. a money
                  Besides embedded, really high-performance stuff, some parts
                  of the infrastructure that need to be as efficient as
                  possible or run without GC or need to talk to very special
                  interfaces or a library, Clojure and ClojureScript or related
                  dialects/ implementations are suited for pretty much
                  everything else I can think of.
                  We should be thinking about how to implement a Clojure-like
                  language in more places, perhaps even without a GC but with
                  AOT compilation + interpreter for the REPL along the way of
                  Babashka. We should explore how to have a REPL to multiple
                  systems at once and handle them like it was a single machine
                  to some degree. We should be thinking, how to make a running
                  Clojure program interruptible easily (perhaps with an extra
                  setting), like it was a program in the shell. We should think
                  about adding a Clojure-like language to the browser natively
                  so that programs don't have to load it like they do now and
                  that a browser tab could have some kind of REPL that you
                  could authenticate and connect to over a socket. That way,
                  you could rewrite the code of the web app at runtime if
                  allowed by the user.
                  And we definitely should design more APIs in much simpler way
                  working more with data and less with invoking some specific
                  functions. E.g. browser "history" could just be a vector/
                  array of maps/ objects or whatever instead of some finicky
                  getters and setters that obscure the problem and are just
                  another thing you have to learn to do useful work.
   URI            [1]: https://github.com/thheller/shadow-cljs
            keymone wrote 2 days ago:
            You'd have to torture me before I would make a conscious choice to
            pick JS over Clojure.
              michaelcampbell wrote 2 days ago:
              It sounds like that's exactly what happened to the GP.
                keymone wrote 2 days ago:
                GP didn’t pick JS for a rewrite either, so yes, I’m pretty
                sure GP is with me on that.
                  xfer wrote 1 day ago:
                  Yes they did, maybe take a look at the repo, the core vt
                  layer is rust everything around it is in js(it's not cljs,
                  you might ponder why not).
                    keymone wrote 1 day ago:
                    Well sure, but the important parts aren’t in JS. I can
                    understand the argument that to provide npm library you may
                    need to touch that garbage of a language, I’m just happy
                    others do it for me.
                      xfer wrote 20 hours 59 min ago:
                      The entire project used to be cljs, so obviously it's
                      done on purpose, that is stated in the article.
                        keymone wrote 15 hours 17 min ago:
                        what is done on purpose?
          jaxn wrote 2 days ago:
          I have also spent hours getting npm packages to work in JS before.
        qwertygnu wrote 2 days ago:
        Went to [1] and clicked the demo video. I'm a bit confused because it
        looks like future lines are being rendered after the cursor while lines
        are being typed. Is that...correct? It looks like either a blatant bug
        in their product (that they're displaying on their homescreen demo!) or
        on purpose for some reason I can't find.
   URI  [1]: https://asciinema.org/
          sickill wrote 2 days ago:
          Feel free to report a bug here [1] (including browser version, OS
          would help a lot).
          Btw, it's not a product, just a side, hobby project of mine.
   URI    [1]: https://github.com/asciinema/asciinema-player/issues/new
            qwertygnu wrote 1 day ago:
            Ohhh weird, I thought it was a literal mp4 on the front page haha.
            Must be one of my browser extensions messing with it!
          nawgz wrote 2 days ago:
          You made me click through, and I could not recreate. Is this the new
          viral marketing? I truly just was reverse psychology'd
            qwertygnu wrote 1 day ago:
            not a plant I swear!
        cunthorpe wrote 2 days ago:
        Maybe it’s just me, but I don’t think showing a string on screen
        requires React or any other view library.
        I’d probably paint it on canvas and then overlay an invisible
        plaintext node to allow selection.
          stjohnswarts wrote 2 days ago:
          People work with what they know and I'm sure they'd love to see a
          similar implementation done with the method you suggest. It's open
          source and hugs and thumbs up all around here :)
          sickill wrote 2 days ago:
          The player has other UI parts, mainly the control bar with current
          time, progress/seek bar, full-screen toggle etc. Of course it could
          be built with plain JS but a small library like Solid.js does the job
          The terminal lines with (colored) text definitely don't need a view
          library if you just want to display it, true. Canvas would be way
          more efficient here, and it's not out of the question for the
          terminal part in the future. One thing that using DOM (spans here)
          gives for free is copy-paste. Like you mentioned it could be solved
          by overlaying a text element on top of canvas (on mousedown, or when
          paused) or custom implementing copy-paste for canvas with
          mousedown/mousemove/mouseup, but that's all extra work, and as I
          mentioned in the blog terminal emulation was the bottleneck, not
          Cthulhu_ wrote 2 days ago:
          Yeah but how can you quickly re-render the plaintext node in sync
          with the canvas? How are you doing font rendering? How are you
          handling things like accessibility and interactive controls?
          I feel like you may not be seeing the whole picture / problem domain.
          winstonewert wrote 2 days ago:
          But that just means you have to manage both the text (what
          React/SolidJS are already doing) and a canvas, which means your twice
          the work for no good reason.
          eatonphil wrote 2 days ago:
          If you made a library that does this I'm sure it would be welcome
        ryansolid wrote 2 days ago:
        It's awesome to see performance-oriented projects to find their way to
        SolidJS( [1] ). So satisfying to see success stories like this one.
        Great work.
   URI  [1]: https://www.solidjs.com
          kevincox wrote 2 days ago:
          I'm struggling to find out how it actually updates the DOM. The way
          that re-rendering happens is clear but it looks like the top-level
          function just returns a JSX.Element. There is no explanation how this
          is efficiently rendered.
          Also how are mid-level invalidations handled? For example if I update
          a list to remove one element do the other elements get re-rendered or
          are they cached?
          It might be because I am browsing the docs in mobile but I find them
          hard to navigate.
          sickill wrote 2 days ago:
          Thanks Ryan! And thanks again for your work on Solid.js.
          skrebbel wrote 2 days ago:
          Wow, I just read about Solid for the first time, and I'm very
          impressed at the API design. I love how it's actually a fully
          reactive data flow thing, but it looks and feels like React Hooks.
          The other reactive/observable-based frameworks I've seen (eg Cycle)
          very much put the observable streams center piece, and I always felt
          that was distracting, and that nuances about how the underlying
          observable stream library worked quickly got in the way.
          Solid still puts the components firmly at the center, just like
          React, but replaces React's state concept by fully reactive
          observable state, called "signals". You use them pretty much like you
          useState in React, but deep inside it's an observable stream of
          changing data, and you get all the finegrained update control that
          comes with that.
          Also, I love how noun-heavy it is. Resources, tracking scopes,
          effects, signals. It's just like how React moved from "do this thing
          after the component updated" to "ok we have this concept called an
          effect", but extended to more topics such as dealing with async data
          loading, when exactly a signal is observable, etc.
            com2kid wrote 2 days ago:
            > but it looks and feels like React Hooks.
            I am confused about why this is a positive.
            React Hooks are the reason why I want to stop using React. They are
            confusing and seemingly magical, compared to lifecycle methods that
            make a ton of sense. While I agree they can make complex things
            easier, they are also incredibly easy to get wrong, as they are
            order dependent.
            Reading the Solid landing page, what I see is "Solid takes the most
            confusing part of React, and runs with it."
              ryansolid wrote 2 days ago:
              Reactivity like this predates React Hooks. It doesn't have the
              hook rules/stale closures etc... No dependency arrays, useRef, or
              useCallback. It's a very powerful model and very different. The
              similarities are surface level but aren't without benefit.
              Composable declarative data patterns, read/write segregation,
              traceable state dependencies.
              Some people position it more like, "Solid makes Hooks the way
              they should have been in React". Personally understanding how
              React works this doesn't make sense. But I think it might be
              helpful for people just approaching the framework.
          aidos wrote 2 days ago:
          I've been keeping an eye on your work for a long time now. We're a
          mobx shop so I was hoping to see you explore the ideas you had around
          that a little more ( [1] ).
          I like and know where I'm at with React, but bringing a beginner
          through it recently definitely made me re-appreciate how nuanced it
          is. Also, you have to do a bit of voodoo to get good performance, and
          when you do the intention of the code vanishes pretty quickly.
          For someone using React on top and mobx stores in the background (say
          50k LOC all in), how big of a task would you say it is to move to
          something like Solid?
   URI    [1]: https://github.com/ryansolid/mobx-jsx
          ano88888 wrote 2 days ago:
          congrats on the performance. But I just hope that React will quickly
          catch up and improve in terms of speed & performance. It is really
          tough to learn a new framework every few months.
            alvarlagerlof wrote 1 day ago:
            Going to be very different in React 18
            austincheney wrote 2 days ago:
            Not going to happen. The key difference: virtual DOM.
            laurent92 wrote 2 days ago:
            I hope React won’t have all of those gotchas which make a good
            year of experience necessary when training a junior, until he
            migrates to a job that pays for his upgraded skills… Cost of
            skills is a thing.
            ryansolid wrote 2 days ago:
            It's unlikely. Solid has had this performance profile since 2017.
            React hasn't really budged. Fundamentally different approach. There
            are different types of performance to explore but raw performance
            for small things is not something React is going to "catch up" on.
            Might be worth a read:
   URI      [1]: https://javascript.plainenglish.io/javascript-frameworks-p...
          eatonphil wrote 2 days ago:
          Congrats! One nit I have about SolidJS documentation is that it
          doesn't have a clear path for "migrating from React" which I take it
          is probably actually important to you given how similar SolidJS looks
          to React and how much faster it claims to be. Also might be good to
          include a section on how to interop with existing React libraries --
          even if the answer is that you can't.
          And it might be worth putting in your HN profile that you are the
          creator of SolidJS. :)
            ryansolid wrote 2 days ago:
            Yeah it is sort of buried and probably not very clear: [1] .
            And good point thank you about updating my profile.
   URI      [1]: https://www.solidjs.com/guide#react
        eatonphil wrote 2 days ago:
        Historically asciinema was not easy to embed in React because you can't
        have multiple copies of React. So my team at the time wrote and
        open-sourced an embeddable alternative to it. Unfortunately that
        project seems to have disappeared by now.
        But now that asciinema is no longer in React maybe it will be possible
        to embed now. See [1] .
   URI  [1]: https://github.com/asciinema/asciinema-player/issues/72#issuec...
          sickill wrote 2 days ago:
          Yup. No more React.js conflict. That was caused by the way how
          ClojureScript bundle has been built (reagent's copy of React added to
          the bundle's global/window namespace).
          Also, the dependency on Solid.js is unlikely to cause any conflicts
          even if you used Solid.js yourself in your app given the new package
          doesn't globally export anything else other than the minimal public
          API for mounting the player in DOM.
          kazinator wrote 2 days ago:
          Historically, you could embed an animated .gif into a web page going
          back before Y2K, and there are tools to make such a thing from
          recording terminal sessions. No Javascript, no third party websites.
            sickill wrote 2 days ago:
            > no third party websites
            FYI you're not forced to use asciinema.org for hosting the
            recordings, it's fully self-hosting friendly:
   URI      [1]: https://github.com/asciinema/asciinema-player/#quick-start
              kazinator wrote 2 days ago:
              I don't want to engage in anything by the name of "hosting" to
              convey an animation.
              Last time I made an animation, like this, over a year ago, I just
              pasted it into a team Slack channel.
            akavel wrote 2 days ago:
            Is there some tool like this that you could recommend? I tried
            searching for one at one time for my project, assuming someone must
            have written something like that for the script command, but didn't
            seem to be able to find a reasonable one (or any? I don't recall).
            I ended up writing an ad hoc single use tool for gif-izing an
            asciinema transcript... ( [1] )
   URI      [1]: https://github.com/akavel/asciinema2gif
            Cthulhu_ wrote 2 days ago:
            If you want to go that way, prefer html5 videos, they are a lot
            more compact and have options to pause and navigate.
            5e92cb50239222b wrote 2 days ago:
            Those are pretty heavy unless it's only a second or two (then why
            would you need a gif at all?) You'll be further cutting your target
            audience to the extremely privileged. I've had to suffer with
            dial-up (3-4 KB/s) until 2009, and with shitty ADSL (15-30 KB/s)
            until 2013. Many parts of the world are still like that.
              kazinator wrote 2 days ago:
              I have a here 90 second .gif I made a year ago (demo of a
              particular Vim syntax highlighting scheme for a commit message
              format). It contains 930 frames, 10 fps. The resolution is
              1200x768. The .gif is 600.0 kilobytes, so the coding density is
              about 660 bytes per frame.
              Someone downloading at 3-4KB/s might find the download annoying;
              on the other hand, it could play back on a 66 MHz 486 DX they
              found in the dumpster. Good luck bringing up a modern browser
              with Javascript.
              4KB/s second would be enough to get it to average around 6FPS in
              the first pass through the animation, if it were being rendered
              as it is being downloaded.
                com2kid wrote 2 days ago:
                > it could play back on a 66 MHz 486 DX they found in the
                ooh actually, I got banned from the Ars-Technica forums way
                back in the day for posting a giant 800x600 animated GIF. It
                took browsers a few minutes to render! People thought I had
                crashed their browsers, but they just needed to be patient. ;)
                I think I had a Duron 700mhz back then, so a 486 would've been
                turned into toast.
                (Or, possibly, IE's GIF code was just really bad at that time!
                :) )
                JasonFruit wrote 2 days ago:
                You're clearly more careful with resource consumption than
                many, then.  Many times I've had enough time to start coffee
                while waiting for a .gif to get through its first pass so I can
                usefully watch it, using rural satellite from Hughes.  We're
                still using your stuff out here.
                  kazinator wrote 2 days ago:
                  Was it a TTY animation with most frames changing very few
                  pixels (like one character cell), often with quiescent
                  periods when nothing changes due to nobody typing?
                  Gifs can do silly things, like store a sequence of full
                  hedora wrote 2 days ago:
                  Hughes is latency-bound, not bandwidth bound.  For it to take
                  that long, they’d have to try really hard.  For instance,
                  the map could issue an http[s] request per frame.
            eatonphil wrote 2 days ago:
            I have no aversion to JavaScript. It's easier just to fetch the
            session as text and render it in the browser with JavaScript.
            If you're building gifs then you need additional file storage and
            an async job queue for generating the gif. I try to avoid image
            processing personally.
            codetrotter wrote 2 days ago:
            You can’t pause and rewind or skip ahead in a gif in any of the
            major browsers.
              stjohnswarts wrote 2 days ago:
              9 times out 10 if you need something to be shown in video it's
              better to use an mp4 and screen recorder rather than stick it in
              a gif. It's usually smaller too.
              kevin_thibedeau wrote 2 days ago:
              Or copy/paste text.
                hedora wrote 2 days ago:
                FWIW, iOS 15 automatically OCR’s screenshots, so now copy
                paste works again (even from non-selectable text in apps).
                However, most times I have problems with copy paste, it’s not
                due to gif’s.  It’s due to some BS framework thing
                rendering text in some non-standard way.
                kazinator wrote 2 days ago:
                If I had users who needed to be that engaged with the content, 
                they would be the sort of target audience I could easily have
                run scriptreplay in a terminal.
                (I see this more as a demo thing to show people what is
                possible than, say, documentation to crib from, but I can see
                the value in being able to do that.)
                unrealhoang wrote 2 days ago:
                Or render it beautifully for every future screen resolution.
        lxe wrote 2 days ago:
        The immutable hype is finally fading. People starting to realize the
        drawbacks of treating hardware as an infinite resource.
          legulere wrote 2 days ago:
          The benefits of immutability are still there though. You should
          generally design immutable by default and use mutability for
          performance where it matters.
          Rust which was used here also has immutability by default and
          mutability is an explicit opt-in.
          mping wrote 2 days ago:
          Well the immutability hype allowed the author to build the previous
          incantions of the library. At this point, I hoped everyone understood
          the tradeoffs of using abstractions. Otherwise we should all be
          programming on assembly.
            zupa-hu wrote 2 days ago:
            This. If the project was reimplemented in assembly, it would be
            faster and smaller and the same conclusion could be drawn:
            “finally the high level programming hype is over”.
            come on..
          peoplefromibiza wrote 2 days ago:
          or maybe is JavaScript hype and the make concurrent programming
          impossible that it's finally dying.
          Immutability is alive and well, it's simply a matter of js runtime
          not supporting it, because the developers thought "one thread ought
          be enough for anybody".
          niels_bom wrote 2 days ago:
          This talk by Richard Feldman about Roc (a new language) goed into why
          immutable does not necessarily mean slow and given enough attention
          can mean higher performance in certain cases:
   URI    [1]: https://youtu.be/vzfy4EKwG_Y
          rtpg wrote 2 days ago:
          What a boring takeaway from this. Beyond the fact that the immutable
          data structures proposed by Clojure/script tend to perform very well
          in a lot of "normal" cases (and in a lot of normal web-app workflows
          your stuff is immutable, like "query then display the result from an
          API"), at least to me it feels like asciinema is a very good example
          of a case where you have  tougher-than-average performance
          Not to say that we shouldn't have "everything be performant" but
          drawing a bunch of stuff to screens is _the classic_ performance
          question. Whereas most "business apps" people here work on to a
          day-to-day have different performance issues.
          Rewriting your CRUD frontend in Rust isn't going to make your DB
          queries faster
            derefr wrote 1 day ago:
            On the other hand, rewriting the DBMS itself in Rust might.
            Especially if the DBMS was originally written in Clojure (see:
              KingMob wrote 1 day ago:
              I'm not Datomic's biggest fan, but you know it's a hosted
              database with multiple backends, right? A lot of the heavy
              lifting is delegated to storage like DynamoDB or Postgres.
            hedora wrote 2 days ago:
            > Rewriting your CRUD frontend in Rust isn't going to make your DB
            queries faster
            A typical consumer disk can do 1,000,000 IOPS (enterprise is one
            generation behind, and slower at the moment), with millisecond read
            and write latencies.
            Are there any managed CRUD frontend languages that are fast enough
            to keep up with that?
            (By “keep up”, I mean “be less than half the hardware I
            provision at scale”)
            ncmncm wrote 2 days ago:
            Won't make them slower, anyway.
            There are often performance bottlenecks you didn't know about, and
            had blamed on database (or whatever) interaction overhead. It will
            never feel worthwhile to dig into each candidate, because any
            payback seems too unlikely. Not having left scope for such
            bottlenecks means you can be confident they are not there.
            Re-implementing once is a lot less work than diving into each
            possible bottleneck. Improving your actual database operations,
            after, is more likely to have an effect when some other bottleneck
            doesn't mask the improvement.
            You don't have to do it in Rust. Any optimization you could do in
            Rust is probably easier in C++, and also easier to find maintainers
              pohl wrote 2 days ago:
              > Any optimization you could do in Rust is probably easier in C++
              That's kind of funny in light of the history that certain
              optimizations in web layout engines were attempted,
              unsuccessfully, in C++ multiple times and ultimately they
              invented Rust to make them easier.
                ncmncm wrote 2 days ago:
                That is how the marketing goes, anyway.
                The facts on the ground probably have more to do with
                improvements to the C++ code being obliged work as deltas
                against existing C++ code, where the Rust code was a complete
                re-implementation, thus not constrained.
                Both C++ and Rust are today different languages from when that
                project ran.
                  derefr wrote 1 day ago:
                  Another subtle consideration is that with long-lived,
                  highly-backward-compatible languages like C++, you'll have a
                  bunch of people on your project who still write C++ like it
                  was ten or twenty years ago (because that's when they learned
                  it), and so bring down the code quality of your project.
                  Whereas choosing a new language (like Rust at the time) means
                  that everyone who claims to be able to write it at all, is
                  working from the same, recent set of language idioms.
              paulgdp wrote 2 days ago:
              "Any optimization you could do in Rust is probably easier in C++"
              I think this feeling comes from the fact that it takes longer to
              learn the basics of Rust compared to C++.
              However, once one has learned C++ or Rust to a reasonable level,
              I would argue that Rust is actually easier to use.
              This is not the same thing but many people make this claim.
                jhgb wrote 12 hours 52 min ago:
                > it takes longer to learn the basics of Rust compared to C++.
                Does it really? For example I'd think that initialization of
                objects is a topic that should be in "basics", yet
                initialization of objects in C++ seems disproportionately
                complex compared to Rust (at least to me).
                ncmncm wrote 2 days ago:
                Each is easier than the other, depending on where you look and
                where you come from.
                But it is a fair bet that changes to C++ code to implement a
                point performance optimization will be smaller than the same
                sort of change would be for Rust code. For the latter, you are
                likely to need to re-architect that part of the system some to
                get your optimization and still satisfy the borrow checker.
                Having a borrow checker that demands satisfaction is a virtue,
                but there is no denying it adds cost in the small, where we're
                talking about, notwithstanding that such cost may be paid back
                at the system level.
              Ar-Curunir wrote 2 days ago:
              > Any optimization you could do in Rust is probably easier in
              C++, and also easier to find maintainers for.
              At least the first part is not necessarily true. E.g., in C++,
              you might make defensive copies, whereas in Rust the lifetime
              system will track things for you.
                ncmncm wrote 2 days ago:
                Thus, "probably".
          bsg75 wrote 2 days ago:
          What is the connection between mutability and excessive hardware
            cjcampbell wrote 2 days ago:
            As an oversimplified example, in using an immutable screen buffer,
            a change between frames results in allocation of a full buffer
            rather than overwriting memory within the buffer (though one could
            probably think of ways to optimize this for the use case). Excess
            comes either in the form of unnecessary (relative to mutable)
            memory usage and/or CPU cycles necessary to support high-levels of
            garbage collection.
              knubie wrote 2 days ago:
              > in using an immutable screen buffer, a change between frames
              results in allocation of a full buffer rather than overwriting
              memory within the buffer
              That’s actually not how clojure’s immutable data structures
              work. They use structural sharing, so only the portion that
              changes (roughly) needs new memory allocated, and only the parts
              that changed get garbage collected, so it is a bit more efficient
              than that.
                ben-schaaf wrote 2 days ago:
                You can't deallocate part of a frame buffer, it's a single
                allocation. Unless every pixel is individually allocated, which
                would clearly be insane.
                  __s wrote 2 days ago:
                  But if your frame buffer is text, you can store the text in a
                  rope data structure like JS vms do
                  So you don't necessarily have an allocation for every single
                  character, but you're still able to share memory between
                  I've implemented a game engine with immutability (makes for
                  fast cloning in AI search) where much of the game state is
                  shared between clones. With reference counting it also means
                  if there's a unique reference being modified then no copy is
                  made. This same trick is used by Python to optimize string
                  knubie wrote 2 days ago:
                  I'm explaining how clojure's data structures are different
                  from OP's screen buffer example.
              peoplefromibiza wrote 2 days ago:
              that's only because js is "stupid"
              if you have the string "hello" and somewhere else "hello world"
              you can retain only one copy of "hello" because it's guaranteed
              it won't change.
              but js vm it's not smart enough for that.
                __s wrote 2 days ago:
                You're wrong, JS has immutable strings & so VMs use ropes to
                make mutable usage & slicing fast [1]
   URI          [1]: https://gist.github.com/mraleph/3397008
   URI          [2]: https://twitter.com/rauschma/status/126996415427579904...
                  peoplefromibiza wrote 2 days ago:
                  my bad, I learned something new.
                  anyway this proves that immutable data structures are not
                  inherently slow, this is infact an optimization that makes
                  things faster.
          bachmeier wrote 2 days ago:
          This is an application where the goal is to squeeze every last drop
          of performance possible out of the processor. The "every last drop of
          performance" crowd has never been the source of the "immutability
          hype". Your comment is dismissive of many domains of programming -
          immutable didn't become a thing because others weren't as enlightened
          as you.
            jcelerier wrote 2 days ago:
            > This is an application where the goal is to squeeze every last
            drop of performance possible out of the processor.
            I have a hard time thinking of an application for which this isn't
            the case. If my cooking recipe app / website is too slow and/or
            eats too much battery (and god fucking knows they are) I'll look
            for a competitor immediately.
              detaro wrote 2 days ago:
              not being too slow and "squeeze every last drop of performance
              possible" is not really the same, and the latter is an expensive
              tradeoff to make.
          slmjkdbtl wrote 2 days ago:
          I fantasize about a future where we have enough CPU and memory that
          we can waste them on nice stuff like immutable data structures and
          software rendering.
            zupa-hu wrote 2 days ago:
            Welcome to now. Totally works like 99.99% of the time.
            gpderetta wrote 2 days ago:
            That future is our past. The era of free, continuous
            order-of-magnitude single threaded CPU improvements  is well behind
            us. Performance is only growing very slowly. On the other hand ram,
            disk, and network bandwidth/latency is improving continuously,
            making the CPU even more of a bottleneck.
            josephg wrote 2 days ago:
            I fantasize about a future in which buying a faster computer means
            my software runs faster.
            I don’t spend thousands on computer hardware so that lazy devs
            can get lazier.
              hayley-patton wrote 2 days ago:
              The use of persistent data structures can make concurrent
              programming easier, which allows for better use of many cores.
              Functional programming can also scale to many computers in
              distributed systems, e.g. in Erlang.
              jacquesm wrote 2 days ago:
              That ship sailed three decades ago.
              devwastaken wrote 2 days ago:
              You cannot physically click faster than about 100ms in reaction
              to a UI. A proper application that isn't 100x less efficient will
              never be noticed by you unless you go out of your way to measure
              it. Stable software is more important than unusable fast.
              "Lazy devs", Work on a team in C graphic code and watch nothing
              get done.
                rightbyte wrote 1 day ago:
                Ever played a FPS game with 100 ms ping? Then compare that
                feeling to LAN latency.
                ben-schaaf wrote 2 days ago:
                A 100ms delay is trivially noticeable - I suggest trying to
                type with that delay. In terms of video people reliably
                distinguish between a 144hz and 240hz refresh rate which is a
                difference if just ~3ms.
                ncmncm wrote 2 days ago:
                Comparing to C is always pointless. There is no environment
                constrained to using C for performance. There are only
                individuals who refuse to move on from C.
                It doesn't matter what language they move on to. Rust and C++
                are both good.
              askonomm wrote 2 days ago:
              You may call functional programmers who prefer immutable data
              structures lazy because we want to actually understand what we
              create, but I don't see how the 10 billion layers of abstractions
              and state duplications somehow end up making better software.
                nyanpasu64 wrote 2 days ago:
                To me, it is lazy to use immutable data structures in
                situations where they generate large amounts of garbage and/or
                result in user-facing GC pauses. (In Firefox, I encounter many
                slow sites (with or without immutability) with multi-frame
                pauses, often GC pauses. I think the slowdown is coming from
                the website and not my extensions.)
                I believe it's possible to understand the code you create, even
                in the presence of mutation (though you can no longer store old
                values for free, and need to use cloning or other approaches).
                You need to restrict which code is responsible for mutating
                state (using careful program architecture), and restrict the
                ability to mutate data while other pointers to the data exist
                (Rust imposes these restrictions). Interestingly, the Relm
                architecture is a translation of the Elm architecture (Elm is
                an immutable language) to Rust code (Rust is a mutable
                language) which restricts which code is responsible for
                mutating state, and Rust restricts the ability to mutate data
                while other pointers to the data exist.
                Interestingly, Rust unifies immutable and mutable data
                structures. The im library ( [1] ) uses the same tree-based
                immutable data structures as Clojure and such, but exposes an
                API closer to Rust's stdlib containers (including mutation).
                However im's performance characteristics are different from
                Rust's stdlib; clones are shallow and take O(1) time, while
                IndexMut is slower and copies tree nodes whenever that node's
                refcount is greater than 1. immer.js ( [2] ) has a somewhat
                similar API, but a different implementation (I think it uses
                standard JS arrays and copies the whole thing when you mutate
                one element).
   URI          [1]: https://docs.rs/im
   URI          [2]: https://github.com/immerjs/immer
                drenvuk wrote 2 days ago:
                I'm not sure if you feel attacked or not but you shouldn't. 
                Being lazy is good for programming I think. You should keep in
                mind that your functional programming base is built on a
                figurative 10 billion layers of abstraction already.  It's just
                that those abstractions are somewhat well made so you don't
                have to think about it.
                Proper abstractions aid in understanding and can ideally be
                optimized away.  Poor abstractions hinder it and slow things
                josephg wrote 2 days ago:
                I agree - I think having 10 billon layers of abstraction is
                worse for everyone. It’s buggier, more expensive to make and
                (ironically) often slower in practice anyway; because you
                can’t optimize what you can’t understand. Java style OO has
                a lot to answer for.
                Functional programming is great. What’s not great is loading
                the entire closure VM environment into my browser - resulting
                in the software running (in this case) 50x slower. FP is no
                excuse for making my computer crawl to a halt.
                And there’s no essential reason FP needs to be slow. For
                example, look at how well llvm optimizes map/filter/fold in
                rust. In many cases they’re faster than their imperative
                counterparts! There are other ways to benefit from immutability
                that don’t involve burying a garbage collector in useless
                work. For example, React is a lovely example of immutable
                ideas, and a fast runtime.
                I spent a few thousand dollars on a new Mac laptop recently. I
                wonder what percentage of my clock cycles are going to be
                wasted due to bad abstractions and inefficient code? Probably
                most of them. I wish I could take the money I spent on the
                machine and instead pay people to improve their software. I
                don’t have billions of cycles per second of actual work to
                  comma_at wrote 2 days ago:
                  > What’s not great is loading the entire closure VM
                  environment into my browser
                  There's no VM in clojurescript, it compiles to JS, it is
                  tree-shaken and heavily optimized and minified through
                  Google's Closure compiler.
                  > resulting in the software running (in this case) 50x slower
                  The speedup is not a result of abandoning clojurescript, it's
                  from moving from immutable data structures to mutable arrays
                  and primitives. The same can be done in clojurescript or
                  I think this is a common misunderstanding, that's why I'm
                  reacting. Nobody claims immutable data structures to be the
                  silver bullet. Computation-heavy parts need to be done in low
                  level code and with primitive types.
                    sickill wrote 2 days ago:
                    > The speedup is not a result of abandoning clojurescript,
                    it's from moving from immutable data structures to mutable
                    arrays and primitives. The same can be done in
                    clojurescript or javascript.
                    More or less, yes.
                    The majority of the perf increase here came from two
                    things: 1. going from immutable->mutable, 2. going from
                    CLJS/JS->Rust in the perf critical part. Doing just 1.
                    would likely improve the performance, but not as much as
                    doing both 1. and 2.
                    Doing just 1. while staying with ClojureScript could
                    potentially be accomplished with transients [0] at the cost
                    of making a major chunk of the code non-idiomatic Clojure.
                    I actually played with transients here before attempting
                    the rewrite, but haven't got too promising results though.
   URI              [1]: https://clojure.org/reference/transients
              slmjkdbtl wrote 2 days ago:
              I wouldn't characterize hoping to use immutable data structures
              and software rendering as trying to be lazy
                drenvuk wrote 2 days ago:
                I would.
                Instead of properly managing mutable state (which can be
                difficult in situations with growing teams and complex
                application logic) people are opting to just copy everything or
                copy a subset of the tree they need so they don't have to think
                about it.
                Immutability is bad for performance when the purpose is not
                recalculating a certain state after a certain number of
                operations(memoization).  Many of the best practices that have
                cropped up in the past few years has been more for helping
                teams of people deal with growing code bases rather than
                helping programmers deal with limited hardware.  In computer
                science this should be self explanatory. For optimal runtimes
                the act of making copies is avoided.  Why people usually seem
                to ignore the fact that they're wasting cycles for the sake of
                the holy grail of clean, functional programming and
                immutability has eluded me.
                Typescript, focus on immutability, microservices even.    I hate
                all of them but they have their purposes.  They solve people
                problems.  Maximizing hardware performance is not in the list
                  rackjack wrote 2 days ago:
                  Abstraction is at the core of programming... complaining
                  about people not "properly managing mutable state" is like
                  complaining about choosing Java over C because they're not
                  "properly" managing memory and instead using a garbage
                  collector. Maximizing hardware performance is, truth be told,
                  largely irrelevant for the vast majority of applications. If
                  they can meet their goals/deadlines/whatever, you can call
                  them lazy, but I think most would call them efficient.
                  Though I must admit, every time my browser lags when viewing
                  what SHOULD be a static site, I do die a little inside.
                  slmjkdbtl wrote 2 days ago:
                  Yeah now I wouldn't use immutable structure in an performance
                  intensive application, but I do _hope_ one day we have some
                  cycles to spare and can use it without worrying too much. To
                  me it's like garbage collection, it's nice to have if we can
                  afford some performance cost.
                ncmncm wrote 2 days ago:
                Yet, we may characterize lazy programmers as hoping to use
                immutable data structures and pay no attention to performance.
                We all know that performance takes extra work to ensure, then,
                and is uncertain even with the extra work.
          notacoward wrote 2 days ago:
          This part...
          > for the high frame-rate, heavy animations this puts a lot of
          pressure on CPU and memory
          ...does seem to suggest that the "garbage multiplier" effect of
          immutability is an ill fit for applications that also create a lot of
          garbage naturally. Note that this is about as close to an
          apples-to-apples comparison as we're likely to get - the same
          application implemented two different ways - so it's not the
          application's innate object-lifetime characteristics that are the
          problem. That's an implementation artifact.
          The question is: how many applications are likely to hit this same
          limit? Is this a rare case, or is it common? If it's common, it is
          indeed an indictment of the "immutable" approach. Otherwise, not so
            merijnv wrote 2 days ago:
            > ...does seem to suggest that the "garbage multiplier" effect of
            immutability is an ill fit for applications that also create a lot
            of garbage naturally
            That actually depends on how your GC is implemented. For example,
            due to laziness+immutability, Haskell produces a lot of garbage and
            a lot of allocations. This is not a problem with the GHC compiler,
            as the GC design makes allocation cheap (effectively a bump pointer
            allocator) and GC cost scales with the amount of non-garbage (this
            is, like all GC design, is a trade-off that can get you into
            trouble with some workloads).
            fnordsensei wrote 2 days ago:
            Clojure developers are not unaware of the tradeoffs between
            immutable and mutable data structures. You'll see them use mutable
            data structures, particularly in tight loops inside functions that
            take immutable inputs, mutate them, and produce immutable outputs
            (thereby preserving the promise of immutability, while leveraging
            the performance of mutability internally).
            You'll rarely see apps designed like this up-front though. Most of
            the time, the surgical mutability will come as a performance
            optimization pass later on.
            As for the apples-to-apples part, I for one am unsurprised to see
            that WASM performs better than ClojureScript, particularly for an
            application like this.
              derefr wrote 1 day ago:
              Not being too familiar with Clojure, but being familiar with
              Erlang, I'm curious whether Clojure has any popular libraries
              that approach efficient mutability the way the Erlang runtime
              The Erlang runtime exposes complex mutable resources like ETS
              tables through opaque handles, where the handle can be freely
              shared, but the resource backing the handle can never actually be
              touched by "clients." Instead, the resource backing the handle
              lives in its own heap, which is owned by a manager object; and
              accesses to the resources in that heap are done by handing the
              manager references to data that it then copies into the heap; or
              querying it by handing it a reference to a key, whereupon the
              manager will copy data back out and return it.
              It's not really the same abstraction as e.g. a Concurrent
              container-class in the Java stdlib, as it's not implemented
              through the client ever acquiring (the moral equivalent of) a
              mutex and then touching the data itself; nor does it involve the
              client adding object references to an atomic queue, where some
              async process then weaves those references into the backing
              object. Neither the client's execution-thread, nor its passed-in
              data, ever touches the handle's backing data.
              Instead, ETS and the like have guarantees almost as if the
              mutable resources they hold were living in a separate OS process
              (similar to e.g. data in a nearby Redis instance), where you need
              to "view" and "update" the resources living in that separate
              process through commands issued to that server, over a socket,
              using a wire protocol; where that serialization over the socket
              guarantees that the data reaching the other end is a copy of your
              client-owned data, rather than a shared-memory representation of
              it. The same semantics, but without the actual overhead of
              serialization or kernel context-switching, because the "other
              end" is still inside the managed runtime of your OS process.
              And, to be clear, Erlang ETS accesses aren't linearized by a
              "message inbox" queue sitting in-between, the way that regular
              Erlang inter-actor message-passing is. The ETS table manager can
              handle multiple concurrent requests to the same table, from
              different processes, simultaneously, without locking the whole
              table—just like an RDBMS would. (Instead, it uses row-batch
              locks for writes, like RDBMSes do.) The concurrency strategy is a
              concern internal to each particular black-box-with-a-handle,
              rather than something general to the abstraction. The only thing
              guaranteed by the black-box-with-a-handle abstraction, is that
              nobody can mutate the data "inside" the black box without its
              manager's knowledge, because nobody ever holds a live reference
              to the data "inside" the black box.
            xyzzy123 wrote 2 days ago:
            I've always thought of immutability as great for situations where
            you want to "explore" (clone complex current state and go do some
            "what if"), need internal transactions, or allow time travel
            (snapshot/undo/redo) - situations where the state sharing is both
            efficient and feels "natural".
            Also if the data/history are relatively small compared to the
            available memory it's a fine default that generally leads to
            "nicer" code.
            Video doesn't seem at first glance like such a great fit.
              kccqzy wrote 2 days ago:
              Video can be played backwards and forwards, can be sought, or
              jumped to a particular point in time. It fits well with the "time
              travel" benefit of immutable data structure. The author mentioned
              it was extremely easy to implement some kind of a checkpoint or
              key framing with immutability. That's exactly using immutability
              to its strength.
                titzer wrote 2 days ago:
                The downside of immutability is the sheer volume of data (in
                the form of pixels) that needs to be pushed around. A single 4k
                frame is 8.3 million pixels, so you are looking at over 30MiB
                of data for 32-bit color, and you gotta push 30, maybe 60 of
                those a second. Maybe if you have a really good garbage
                collector (or a custom one, because frames are all the same
                size) you can get away with allocating that much data and
                freeing it every second. But that doesn't free you from the
                fact that you are not utilizing hardware caches well; you don't
                get good spatial locality at the hardware level unless you
                reuse the same physical pages of memory for every frame. And
                you can basically only do that if you have a mutable design.
                  sickill wrote 2 days ago:
                  That's not how asciinema-player works though. The player
                  internally represents the terminal buffer as a grid of
                  characters. So for 80x24 terminal you have 80*24=1920 grid
                  cells, each keeping a unicode char + color attrs. When
                  rendering the adjecent cells of each line are grouped by
                  their common color attrs, resulting in (usually) a small
                  number of span elements with text and proper style/class. You
                  can see this in action by going to asciinema.org, opening a
                  random recording, pausing it, then inspecting the terminal
                  with browser's DOM inspector.
                    titzer wrote 2 days ago:
                    Sure, if you don't break it down to individual pixels, the
                    data is way less. Ultimately, getting a well-performing GC
                    is finding enough idle/spare/background cycles to scan
                    memory and recycle it at a greater rate than allocation. If
                    the GC falls behind then inevitably you are going to end up
                    with a big pause. I don't think there's enough memory
                    bandwidth to decode 4k video the naive way, but a small
                    terminal will probably be OK. That said, it's still less
                    efficient than just poking the bytes in memory.
                  littlestymaar wrote 2 days ago:
                  This comment is completely off: obviously asciinema didn't
                  store each individual pixel in its data structure. That would
                  be a completely stupid thing to do even for a regular data
                  structure instead of an immutable one…
          azakai wrote 2 days ago:
          This article doesn't really support that. ClosureScript targets
          JavaScript, which is a platform that doesn't have special support for
          optimizing immutability.
            notacoward wrote 2 days ago:
            How would one optimize for immutability in this case, other than
            turning it back into mutability behind the scenes? I've certainly
            seem some code written in an "immutable" style where it was pretty
            clear that the intent was for one data structure to be a mutation
            of another, just called something else because the language
            required it. That case might be easy to optimize ... but the
            general case?
              fulafel wrote 2 days ago:
              Generally in compiler technology, immutability is an important
              tool in letting compilers reason about and make program
              transformations. See eg the "single static assignment"
              intermediate-representation form that is mainstream in low level
              language compilers. But SSA form isn't as good as having the
              original program expressed immutably, because you get false or
              incidental data dependencies if the compiler has to
              conservatively derive the optimization-friendly SSA
              representation out of the original non-immutable code.
              In practice a JS implementation that had special optimizations
              for code using immutability as a convention might for example
              auto-parallelize code.
              Also it by no means a bad thing if a compiler turns a piece of
              easy to reason about functional code "back" to generated code
              that exploits local mutability behind the scenes in some
              circumstances, that's exactly how we want it to work. We still
              get the robustness guarantees of semantics where our objects
              don't change from under us in programmer visible ways.
                gpderetta wrote 2 days ago:
                Talking about parallelization opportunities is a bit pointless
                when you need 50 cores to match the performance of the single
                threaded code (in the best case, assuming perfect scaling).
                  hedora wrote 2 days ago:
                  These techniques are also applied by compilers such as clang.
                  fulafel wrote 2 days ago:
                  I disagree - lots of compiler work is done on languages that
                  aren't best in class for high performance work. Look at all
                  the effort that people are putting into making Python faster.
                  And indeed JS itself, it wasn't always this fast. High level
                  language users live by "There's more to life than increasing
                  its speed" and then if they take off, people come work on
                  performance and make them faster.
                  Also let's not forget that this was already "fast enough" for
                  a long time before the 50x rewrite.
                    gpderetta wrote 2 days ago:
                    My issue was specifically with the claim that persistent
                    data structures are great for parallelization.
                    I have absolutely no issues with efforts to improve single
                    threaded performance of programming languages (and indeed
                    the advances made by JS are remarkable) and I don't believe
                    any language needs to be "As Fast As Cee". But There are
                    other perfectly reasonable languages that are within a
                    small integer factor of C and C++. You do not need to pay a
                    large penalty for ergonomics.
                  azakai wrote 2 days ago:
                  Fair enough, but parallelism isn't just about cores since
                  SIMD can also help. Also, immutability doesn't just help
                  through parallelism, it also allows other things (as another
                  example, avoiding redundant work like loads of an immutable
                  field and computations on them).
                  Would that help with a huge 50x difference? Maybe not, but
                  the point is that evaluating the benefits of immutability on
                  a VM that does not optimize it - JS VMs - is not relevant.
                  (And that 50x might also be caused by other limitations of JS
                  VMs and not immutability at all, like say deopts.)
              kevin_thibedeau wrote 2 days ago:
              Pretty bog standard behavior for a compiler backend translating
              immutables in IR. Including a runtime compiler into app code is
              the next stage in the evolution of cycle burning leetness.
              memco wrote 2 days ago:
              > How would one optimize for immutability in this case, other
              than turning it back into mutability behind the scenes?
              Roc-lang, which is a functional, systems language in development
              uses something called opportunistic in-place mutation to do just
              that. Here's a video where the creator talks about it:
   URI        [1]: https://youtu.be/vzfy4EKwG_Y?t=1276
              azakai wrote 2 days ago:
              I'm not an expert on this, but one example is that immutability
              lets you operate safely on things in parallel. But JS VMs are not
              aware of objects that are immutable in Clojure's semantics, and
              in any case, do not operate on objects in parallel anyhow.
          rektide wrote 2 days ago:
          mutability is a data structuring virtualization but i'd just as much
          suspect the runtime virtualization.
          that the bundle used to be 570kB isnt an immutability issue. itcs
          that clojurescript drags in a whole clojure runtime, a new
          virtualization layer atop the js runtime. that, to me, is the most
          likely suspect.
          that said, for sure, short tbeow away high gc allocation patterns are
          generally not good. at work there's a lot of "functional" patterns,
          nary a for loop in sight. this endless .map() .filter() usage causes
          near exactly similar issues, with shortived objects. it seems ultra
          sad & silly to me. waste after waste. but i also think we have much
          more deeply rooted problems.
            casion wrote 2 days ago:
            Clojure solves these issues with unneeded garbage creation in
            algorithms with the transducers functions.
            And you can of course just use java objects whenever you want.
            cryptonector wrote 2 days ago:
            Immutability is a drag if you create lots and lots of referenced
            state because, e.g., you want to hold on to many snapshots of past
            Immutability done right need not be much worse than mutability.
            For example, jq's internal value representation is immutable in
            - mutations yield new values,
            - but when the value being "mutated" has only one extant reference
            then mutation is done in-place,
            - while when the value being "mutated" has more than one extant
            reference then mutation is done by copy-on-write.
            If you manage to always have extra references, then "immutable
            mutation" gets expensive.
            If you manage hold on to old references for a long time, then
            "immutable mutation" gets even more expensive.
            In a run-time with a GC not based on reference counting you do have
            to GC all the intermediate garbage.
            Immutable data structures really lend themselves well to reference
            counting GCs because you can't have cycles in immutable data.
              cryptonector wrote 2 days ago:
              I should add that reference counting GCs are nice because
              short-lived garbage is freed immediately and there's no need to
              look for garbage, so they're much faster than scanning GCs. 
              Reference counting GCs can have GC-like pauses when releasing
              objects that have singular references to many many many other
              objects, but the same is true in manual memory management
              fnordsensei wrote 2 days ago:
              > Immutability is a drag if you create lots and lots of
              referenced state because, e.g., you want to hold on to many
              snapshots of past state.
              Isn't it the opposite? If you want to hold on to many referenced
              states at the same time, an immutable data structure should
              provide less overhead than a mutable one, due to structural
                cryptonector wrote 2 days ago:
                A performance drag.  Let's say you have an app that for N
                inputs creates N^2 state in memory...
                But now, if that's just what you must do for some reason, then,
                yes, immutability makes the task of taking all those snapshots
                real easy.
            jdlshore wrote 2 days ago:
            Are you sure about the short-lived allocations being a problem for
            the collector? My understanding of modern generational garbage
            collectors was that they performed quite well with short-lived
            garbage. Not as well as not creating the garbage in the first
            place, of course, but not so badly as to be a problem in most
              meheleventyone wrote 2 days ago:
              This is true for small amounts of garbage but not for large
              amounts which is still slow and large is relative to the hardware
              your user is running on. It’ll also cause a death by a thousand
              cuts where it’s extremely difficult to dig yourself out of poor
              performance because the cause runs throughout the program.
              It’s better to look at cheap short lived collection as a great
              way to get the thing you’re making working but ultimately
              something that needs to be cleaned up to be production ready.
              ncmncm wrote 2 days ago:
              "Perform quite well" is always relative to some reference. The
              oldest trick in the book is comparing to something even slower
              and saying "see? fast!".
              GC apologists seek to normalize this behavior. They often
              succeed, at that. Performing quite well against actually fast
              things, less often.
                jdlshore wrote 2 days ago:
                You seem to have an axe to grind.
                Performance isn’t black and white. Optimizing your memory
                usage isn’t going to do you a whit of good if you’re
                constrained by your database queries. Optimizing your DB
                queries isn’t going to do you any good if you’re
                constrained by a chatty microservice architecture. Optimizing
                your UI response time isn’t going to do you any good if
                you’re already below the threshold of  perceived speed. Etc.
                GC performance on short-lived objects is quite good in enough
                situations to matter, such that optimizing for it, rather than
                your application architecture, is likely foolish outside of
                performance sensitive loops.
                Time is always limited. Spending your optimization budget on
                micro-optimizations is short-sighted.
                  ncmncm wrote 2 days ago:
                  Meaningless personal criticisms undermine your argument.
                  Performance problems usually appear in places we prefer they
                  would not, often runtime apparatus we poorly control such as
                  GC. It is always preferable to try to ignore and discount
                  those, as they may be arbitrarily hard to fix, so people do.
                  Yet, actually not depending on such apparatus, where it is
                  the problem, gets you free optimization.
                  Performance doesn't care where it is found or lost.
                  Micro-optimization is foundational; fail there, and there is
                  often little else you can usefully do. The best optimizations
                  are not doing the thing at all. GC is always strictly worse
                  than no memory management.
                  Fixing your chatty microservoices and your under- or
                  over-indexed DB queries may do you no good if you have built
                  in bottlenecks of your own.
                  "Quite good" means nothing except in comparison to something
                    nyanpasu64 wrote 2 days ago:
                    I want a world without garbage collection. I don't think we
                    can achieve a world without garbage collection while there
                    are codebases based around aliased pointers (which makes
                    lifetime reasoning difficult, and causes enough
                    use-after-frees to make the Linux desktop apps I use
                    unstable, unless you use refcounting) and circular
                    references (which is difficult to refcount). Maybe I'll
                    wait for Rust programmers to rewrite software around
                    tree-based ownership (restrictive but immune to these
                    problems) and ECS/arena indexes (can use-after-free but
                    won't segfault).
                    In the time being, while "spiderweb object graphs" are
                    commonplace, perhaps Nim's memory management ( [1] ) can
                    give us "non-GC by default, refcounting or GC when
                    necessary". I want it to succeed. I hope it does.
   URI              [1]: https://nim-lang.org/docs/gc.html
   DIR <- back to front page