Threads for smaddox

  1. 1

    Integrating with patterns that cannot be proved safe by the rust compiler is always going to require unsafe. Instead of attempting to provide a general solution for all data types of interest, perhaps each one should be manually wrapped in a safe interface, when possible? And if a gauranteed safe interface is not possible, then resort to unsafe.

    I would like to see support for safe in-place intialization of heap structures prioritized, though. That is definitely a weakness in the existing Rust language that has bitten me before. Particularly when attempting to keep a very large struct containing multiple arrays contiguous in heap memory, to make memcopy’s cheap.

    1. 4

      Great, so, now I just need to install Nix successfully on macOS…

      1. 2

        Try riff cargo install nix ;)

        1. 1

          Have you been having issues with the install script recently? It’s been quite stable for me on macOS for the past year or so.

          1. 1

            The last time I tried it was probably 8 months ago or so. It broke to the point where I couldn’t uninstall and reinstall it fresh. I’m on an older macOS version, which could be part of the problem.

            1. 2

              Specific issue number? macos version?

              1. 1

                Ehh, I was probably too lazy to open an issue. Or maybe there already was one. I don’t remember. I’ll revisit it at some point.

                1. 6

                  You’ll have to follow the uninstall instructions before reinstalling it: https://nixos.org/manual/nix/stable/installation/installing-binary.html#macos.

                  If I had to guess, these may not have been up to date in the official docs yet when you ran into trouble.

                  Feel free to message me if you still have trouble.

                  1. 2

                    Thanks, that did the trick.

            2. 1

              yes, the installation script works fine and dandy… but all of my mac-using coworkers have to reinstate the changes to /etc/zshrc (that sources the nix-daemon.sh) on every macos update now. there is an open issue on the github repo.

              from what I have seen, installing is very reliable and very easy. updating your mac, though, causes nix to lose it’s mind every time.

              1. 3

                I wouldn’t really say nix is losing its mind.

                Apple is leaving zshrc editable but then treating it like their property (and for some reason not using the mechanism they made to place a new system default bashrc in the relocated items folder on update). Your coworkers could use bash (or set up another shell–though they’d have to do that manually) to skirt the problem. The update doesn’t affect nix itself.

                (I realize this isn’t a satisfying workaround to anyone who wants to use zsh, but if they are just using the default shell they may not have an opinion…)

                I’ve been trying to get Apple’s attention on this. (I am finally in touch with someone in developer relations, but haven’t gotten a response…)

                1. 1

                  you’re right, I’m exaggerating… but I support whatever shell the devs want to use. I use bash and I’m happy with it (I would probably change it from zsh on mac), but other people use whatever it is that they like and are productive using.

                  that seems like a somewhat serious flaw (oversight?) in the OS that bash will save it’s configuration between updates but not zsh. If you do hear from anyone, I would be interested to hear what they have to say on the matter. Maybe there is a Proper Reason that changes are reset on each update?

                2. 2

                  Yep, that’s the issue nowadays. IIRC you can source it from ~/.zshrc and not have to worry about it though

                  1. 1

                    So the question with that suggestion is: what about all of the build users? Single-user installations are not supported on arm64 macs, so all 32 of those build users also won’t be able to have nix in their path, either. Is that a problem in practice? (I don’t have a mac right now, I can’t answer that question)

                    I also tried putting it into /etc/zshenv, and that kind-of worked… but the developer also had rbenv installed and somehow it was being loaded after everything else, so even though direnv loaded the nix environment, the $PATH had the “wrong” ruby from rbenv instead of nix.

                    1. 2

                      The build users don’t need Nix in the path. The build users is an implementation detail of process isolation, and their specific shell / environment is immaterial because Nix manages it.

                  2. 1

                    For what it is worth, I tested an update to Ventura and the Nix installation remained properly configured in the zshrc through the upgrade. It sounds like abathur might have reached the right people.

                    1. 1

                      I think I did, but I doubt it was early enough to have affected this yet–I did bug them again and got an initial response yesterday.

              1. 1

                Very cool. Applying this concept to producing caustics sounds particularly interesting.

                1. 8

                  I stumbled upon this collection when searching for a PDF of Claude Shannon’s master thesis, and thought others might find it interesting as well. If collections like this are not appropriate for this site, my apologies, and please feel free to remove.

                  1. 4

                    Thank you. I found it useful and I think it is entirely appropriate for this site.

                  1. 2

                    This looks like a logic error:

                    if (mask & (WGPUColorWriteMask_Alpha|WGPUColorWriteMask_Blue)) {
                        // alpha and blue are set..
                    }
                    

                    Shouldn’t it be this?

                    if ((mask & WGPUColorWriteMask_Alpha) && (mask & WGPUColorWriteMask_Blue)) {
                        // alpha and blue are set..
                    }
                    
                    1. 2

                      No, because that requires that both flags are set. The equivalent condition to s & (A|B), which works no different, would be (s & A) || (s & B).

                      Notice the commonality in how the bitmasks are joined: always with some form of the disjunctive (“or”) operator, either bitwise | or boolean ||. In any case, the bitmask A or B must be applied to the operand s using the & bitwise “and” operator: s & A, s & B, s & (A|B).

                      The equivalent operation to your “correction” (s & A) && (s & B), using the technique of joining the bitmasks first, would be s & (A|B) == A|B. This checks that all of the bits are set, rather than that any of the bits are set.

                      Edit: I got confused 😅 You are right: the original code tests whether either alpha or blue is set. My initial comment above would have been applicable if the commented-out text had read, “// alpha OR blue is set..”. I think that’s as good a case as any for “tagged” bit-fields over “untagged” bit-masks.

                      Note for any lurkers who have read this far and are rather confused: You may want to read up on how bitmasks are used.

                      1. 2

                        Which makes either the comment or the code wrong. The comment says “and” not “or”. @smaddox was matching the code to the comment

                      2. 2

                        As others have pointed out, the comment is a bit misleading. But if you want to check if both are set, this would work:

                        if (mask & (WGPUColorWriteMask_Alpha | WGPUColorWriteMask_Blue) == (WGPUColorWriteMaskAlpha | WGPUColorWriteMask_Blue))
                        {
                            // alpha and blue are set ...
                        }
                        
                        1. 1

                          Wouldn’t the | operator join the two bit masks together to create a new intermediate with both bits set? It’s a matter of A == (B|C) versus (A==B) && (A==C) at this point.

                          1. 3

                            It does, but you get “or” instead of “and”. If either bit is set, the result is not zero.

                            1. 2

                              Correction: (A == B) && (A == C) is always false (0) when B != C, due to the transitive property of strict equality. You probably meant (A & B) && (A & C). See my other comment.

                          1. 9

                            In C, this feature is called Bit Fields. It’s been part of the language since the 1980’s.

                            1. 9

                              Zig’s bitfields are a bit better, though. You can take the address of a bitfield. Andrew wrote a blog post about the feature a few years ago: https://andrewkelley.me/post/a-better-way-to-implement-bit-fields.html

                              1. 3

                                That’s an interesting design decision. You can’t pass the address of a bool bitfield to a function expecting a bool pointer, because the bit offset is part of the type.

                                1. 2

                                  That’s true. But this allows any variable or struct member to be passed by reference, which is important for writing generic functions that are polymorphic over argument types.

                                2. 2

                                  That’s a great link: a list of all the problems with C bitfields and how Zig fixes them.

                                3. 3

                                  At first I thought this, and then I read the article and thought “oh, they mean like pascal sets” and then I read it again and realized that no they really are just describing an API that uses a struct of bitfields rather than an integer.

                                  Now obviously a struct of bitfields is superior to an untyped uint, but it lacks the flexibility that comes from getting confused about what int means what :D

                                  I think that the intended benefit of the API being an int is twofold: It is very easy to do things that result in structs being passed in memory even if they fit in registers (clang even has an attribute you can apply to a struct to force register passing), but you can very easily do set operations like unions, intersections, etc. Whether that trade off is worth it I think depends on the use case, but I think we can agree that it would be nicer if we could have set enums in C. There’s no reason for Zig not to have them as it isn’t confined to whatever can get through a standards body.

                                  1. 2

                                    Zig should extend the bitwise operations to work on structs and arrays of ints. That would eliminate the objection that you can’t do unions/intersections on bitfield structs.

                                    If you have this language extension in Zig, then Pascal style enum sets become a coding idiom, rather than a new builtin language feature. You could write a helper function (using comptime zig) that takes an enum type as an argument, finds the largest enum in the type using @typeinfo, then returns the appropriate array type to use as a set.

                                    There are many precedents for extended bitwise operators, all the way from APL in the 1960’s and Fortran 90 (where scalar operations are extended to work on arrays), to vector instructions in modern CPUs that would directly support this extension.

                                  2. 1

                                    From the article:

                                    mask |= WGPUColorWriteMask_Blue; // set blue bit

                                    This all works, people have been doing it for years in C, C++, Java, Rust, and more. In Zig, we can do better.

                                    1. 4

                                      Exactly, they say in C we use integers with various mask bits, and then propose a “superior” solution in zig. Which is to just use bitfields, as have existed in C, C++, [Object] Pascal, … forever.

                                      The point the I think @doug-moen is making is that the authors are claiming that the masked integer is an inherent requirement of C, etc and then that Zig can do better because of this special Zig only feature, pretending that the identical feature does not exist in C, and ignoring that maybe there are other reasons that this (terrible :D) style of API exists.

                                      1. 7

                                        Except bitfields in C are bristling with footguns, most importantly that the mapping to actual bits is implementation-dependent, which makes bitfields incompatible with any C API that defines bits using masks, or any hardware interface.

                                        From the GCC docs:

                                        The order of allocation of bit-fields within a unit (C90 6.5.2.1, C99 and C11 6.7.2.1).

                                        • Determined by ABI.

                                        The alignment of non-bit-field members of structures (C90 6.5.2.1, C99 and C11 6.7.2.1).

                                        • Determined by ABI.

                                        Having an actual useable implementation of bitfields is awesome for Zig.

                                        1. 2

                                          Is Zig’s packing of bitfield independent of ABI?

                                          And why does it matter unless the bitfield leaves your program’s address space? (Just about anything other than byte arrays leaving your address space is ABI dependent. Why single out bitfields?)

                                          1. 1

                                            It matters because if you’re interfacing with nearly any existing C API, which specifies bits using integers with masks, you cannot declare a C bit-field compatible with that API without knowing exactly how your compiler’s ABI allocates bits.

                                            1. 2

                                              Yes. How is that different from a struct?

                                              1. 1

                                                You know exactly how it’s laid out, because that’s defined by the platform ABI. There is no mystery here.

                                                1. 1

                                                  Not a mystery, but highly platform-specific. So if you’re interfacing with anything using explicit bit masks, that makes it a really bad idea to use in cross-platform code, or in code you want to survive a change in CPU architecture. As a longtime Apple developer I’ve been through four big architecture changes in my career — 68000 to PowerPC to PPC 64-bit to x86 32/64 to ARM 32/64 — each with a different ABI. And over time I’ve had to write code compatible with Windows, Linux and ESP32.

                                            2. 1

                                              Yes, the platform defines the ABI for that platform and the compiler is expected to follow that ABI. So bit fields in C/C++ can (and are) used in plenty of APIs.

                                              I get that you may like Zig, and prefer Zig’s approach to specifying things, but that doesn’t mean you get to spout incorrect information. The platform ABI defines how strict an are laid out. It defines the alignment and size of types. It defines the order of function arguments and where those arguments are placed. So claiming bitfields are footguns or that they are unusable because they .. follow the platform ABI? is like saying that function calls are footguns as the registers and stack slots used in a call are defined by the platform ABI.

                                              If Zig is choosing to ignore the platform ABI, then Zig is the thing that is at fault if an API doesn’t work. If there is an API outside of Zig that uses a bit field then Zig will need some way to say that bits should be laid out in the manner specified by the platform ABI.

                                              1. 2

                                                I’m not actually a Zig fan (too low level for me) but I like its treatment of bitfields.

                                                You seem to be missing my point. A typical API (or hardware interface) will define flags as, say, a 32-bit int, where a particular flag X is stored in the LSB. You can’t represent that using C bit-fields in a compiler- or platform-independent way, because you don’t know where the compiler will put a particular field. Each possible implementation will require a different bit-field declaration. In practice this is more trouble than it’s worth, which is why APIs like POSIX or OpenGL avoid bit-fields and use integers with mask constants instead.

                                                Yes, if you declare your own bit-field based APIs they will work. They’re just not interoperable with code or hardware that uses a fixed definition of where the bits go.

                                                1. 1

                                                  You can’t represent that using C bit-fields in a compiler- or platform-independent way, because you don’t know where the compiler will put a particular field.

                                                  No, that is all well defined as part of the platform specified ABI. You know exactly where they go, and exactly what the packing is. Otherwise the whole concept of having an API is broken. POSIX and GL use words and bit fields because many concepts benefit from masking, or to make interop with different languages easier - the API definition is literally just “here’s a N bit value”.

                                      1. 3

                                        Nix language and Guile scheme is really well suited to interact with the nix store. Lazy loading, etc. Has anyone tried anything crazy, such as creating an experimental javascript framework for the task? The author reminding everyone of the different layers made me wonder this.

                                          1. 9

                                            Does this exist as anything beyond a Discord channel? The web page doesn’t seem to have any links to actual software, and a quick web search didn’t turn up anything related. (But there sure are a lot of unrelated projects with the same name.)

                                            1. 6

                                              I chatted with someon on Tangram. Says they are in development and were seed funded. They’ll make the Github public in the future. He said it doesn’t work with Nix. It’s a content addressable system though. Looks interesting. Will definitely check out the docs when it comes out.

                                              1. 4

                                                It’s a dev tools startup. I think the dev environment management stuff is still in development, which is probably why it’s just a discord chat atm

                                          1. 8

                                            Computationally homogeneous. A typical neural network is, to the first order, made up of a sandwich of only two operations: matrix multiplication and thresholding at zero (ReLU). Compare that with the instruction set of classical software, which is significantly more heterogenous and complex. Because you only have to provide Software 1.0 implementation for a small number of the core computational primitives (e.g. matrix multiply), it is much easier to make various correctness/performance guarantees.

                                            Well actually classical software can be made up of only one instruction (NAND) so it’s twice as good as neural networks

                                            The 2.0 stack also has some of its own disadvantages. At the end of the optimization we’re left with large networks that work well, but it’s very hard to tell how. Across many applications areas, we’ll be left with a choice of using a 90% accurate model we understand, or 99% accurate model we don’t.

                                            The 2.0 stack can fail in unintuitive and embarrassing ways ,or worse, they can “silently fail”, e.g., by silently adopting biases in their training data, which are very difficult to properly analyze and examine when their sizes are easily in the millions in most cases.

                                            This seems like the crux of it, though? If we don’t understand how it works and it can fail in unintuitive and embarrassing ways, how can we actually trust it?

                                            1. 3

                                              This seems like the crux of it, though? If we don’t understand how it works and it can fail in unintuitive and embarrassing ways, how can we actually trust it?

                                              ML is generally good for problems where either:

                                              • You don’t actually understand the problem,
                                              • There might not be a correct answer, but a mostly-correct answer is useful, or
                                              • The problem changes frequently.

                                              Shape detection is a good example of the first. Plato onwards have tried to define a set of rules that let you look at an object and say ‘this is a chair’. If you could define such a set of rules, then you could probably build a rule-based system that’s better than example-based systems but in the absence of such a set of rules the example-based approach is doing pretty well.

                                              The middle category covers a lot of optimisation problems. Even where there is a correct (optimal) answer for these, the search space is so large that it’s not in a complexity class that makes it even remotely feasible. Example-based solutions over a large set of examples let you half-arse this and get something that is a lot better than nothing and a lot less computationally expensive than an optimal solution.

                                              The last category is particularly interesting. A lot of fraud detection systems are like this: they’re spotting patterns and the attacker adapts to them pretty well. Spam filtering has been primarily driven by ML for a good 20 years (I think the first Bayesian spam filters might have been late ‘90s, definitely no later than 2002) because it’s trivial for a spammer to change their messages if you write a set of rules and much harder for you to change the rules. These things are not flawless for security because they’re always trailing indicators (the attacker adapts, then your defence adapts) but they’re great as a first line of defence. Project Silica at MSR one floor down from me used ML for their voxel recognition for data etched into glass to massively speed up their development flow: they could try new patterns as fast as they could recalibrate the optics and then retrain the same classifier and see how accurate it could be. A rule-based system might have been a bit more accurate, but would have required weeks of software engineering work per experiment.

                                              Things like Dall-E fit into all three categories:

                                              • Generating a set of rules for how to create art is a problem that various artistic movements over the centuries have tried and failed to do.
                                              • If you really want an image with a particular characteristic, you probably need to hire an artist and have multiple rounds of iterations with them, but an image that’s more-or-less what you asked for and is cheap to generate is vastly cheaper than this and much better than no image.
                                              • The prompt changes every time, requiring completely different output. Artistic styles change frequently and styles for commercial art change very rapidly. Retraining Dall-E on a new style is much cheaper than writing a new rule-based generator for that style.

                                              I see ML as this decade’s equivalent of object orientation in the 1980s/ 1990s and FP in the last decade or so:

                                              • Advocates promise that it can completely change the world and make everything better.
                                              • Lots of people buy the hype and build stuff using it.
                                              • A decade or so later, it’s one of the tools in a developer’s toolbox and people accept that it’s really useful in some problem domains and causes a lot of problems if applied in the wrong problem domain.
                                              1. 2

                                                As far as I can tell, software that worked 99% of the time would generally be an improvement.

                                                1. 3

                                                  As far as I can tell, software that worked 99% of the time would generally be an improvement.

                                                  That’s an obvious nonsense. Imagine routers only routed 99% of traffic correctly. After just 4 hops TCP would break down. We are very close to 100% everywhere it matters enough for people to care.

                                                  You will get at most 95% at typical ML tasks while people care.

                                                  ML models also tend to suck at failing. Typical router will just reject unintelligible packets while ML model will do something totally arbitrary. Such as classifying furniture as animals.

                                                  What I mean is: please don’t use ML for ABS and always make it so that it assists real people, never let it run unattended.

                                                  1. 3

                                                    It’s also worth bearing in mind that 1% in terms of a particular sample doesn’t mean 1% in the real world. There are probably no bug-free routers today, but if you buy ten good routers, it’ll take years to get all ten of them to incorrectly route packets due to implementation bugs, and it’ll involve quite a lot of reverse engineering and testing. Meanwhile, you can get most 2.0 software (!?) to fail in all sorts of funny ways with a few hours of trial and error, and I guarantee that each of them is backed by a slide deck that claimed 99.99% accuracy on ten different data sets in an internal meeting.

                                                    Bugs in the implementation of a model tend to cluster in poorly-understood or unexamined areas of the model, and you can usually tell which ones they are without even running the software, just reading the code, the source code repository history, and doing a literature survey if it’s a well-documented problem (like routing). Figuring that out on statistical models usually devolves into an exercise in stoner philosophy at the moment.

                                                  2. 1

                                                    As a general statement that is probably true.

                                                    However, we can prove (or disprove) 100% correctness of traditional software. We don’t do it for all software because it’s hard but we know how to do it in principle. At the same time interpretability is an open problem in ML. We can reverse engineer (more like guess) the algorithm encoded in some simplest models but it’s far from perfect. The algorithm is approximated most of the time and not exact. It can differ subtly from a classical algorithm we infer. And we can’t do it for big models like GPT-3. We also can’t do it reliably even for all simple models. So it might look like model works 99% of the time but you can’t rigorously prove it does or that it’s actually 99%.

                                                    1. 2

                                                      I think you can only (trivially) disprove it by producing a counter-example on which it fails. The example that springs to mind is with facial recognition that was trained on mostly white faces failing on black faces.

                                                      There might be ways to construct such examples like the “adversarial models” to cause image recognition to fail.

                                                      1. 1

                                                        The adversarial model problem strikes me as a hard one to ever solve because any attacker with access to the original model can just use it to train an adversary.

                                                        1. 1

                                                          Then I’d rather say instead of it being 99% correct, it’s actually 100% incorrect, because it can never be fixed. At least if traditional software has a bug, you can fix it.

                                                          1. 2

                                                            Well, you just train the model against the adversary, obviously. :-)

                                                  3. 1

                                                    This seems like the crux of it, though? If we don’t understand how it works and it can fail in unintuitive and embarrassing ways, how can we actually trust it?

                                                    IMO, the crux of it is that “software 2.0” is good at solving a class of problems that are commercially relevant, and that “software 1.0” is not so good at. Typically domains where we’ve needed expensive humans to do things, and in which human practitioners have developed a great deal of tacit knowledge about how to perform their tasks that are hard to make explicit. It really is incredible that we’ve now got a generalizable approach for automating things that used to require human practitioners with a great deal of human experience.

                                                    But in domains where explicit knowledge is more important, I’d think “software 1.0” will dominate. Though, if AGI ever becomes practical / powerful enough, I don’t discount the idea of “software 2.0” AGI programmers developing (in partnership with humans, at least at first) “software 1.0” systems.

                                                    Anyway, to respond to your actual point, “how can we actually trust it?”:

                                                    1. We won’t necessarily have a choice. Economics being what they are, and human drive being what it is, a technique being more effective more easily will result in its winning in its niche, whether or not that’s considered good for us. I can probably mock up a prisoners dilemma scenario to illustrate this better, but I’m already writing too much.
                                                    2. At some point of examination, trust will break down, in any system. We probably all know about https://www.cs.cmu.edu/~rdriley/487/papers/Thompson_1984_ReflectionsonTrustingTrust.pdf. In math, ZFC set theory is a major foundation of many results, but then there’s this: https://scottaaronson.blog/?p=2725. IMO, the reasonable approach to trusting “software 2.0” systems is similar to the way we establish faith in the sciences: through hypothesis generation and statistical testing.
                                                    1. 1

                                                      In the mean time, I believe we’ll soon experience a new AI winter. All it takes is one spectacular failure, or a huge money sink not paying off, like, let’s say, self-driving cars.

                                                  1. 3

                                                    This matters because understanding that a normal type system is not Turing-complete means that there are truths it can’t express

                                                    Yeah, but it’s pretty rare in production software to want to be Turing complete. It’s more common to want restrictions on space and time usage, for example. Such restrictions are typically implemented in such a way that proofs of restriction should be straightforward, and totally doable in a dependently typed language, if not in a much simpler type system. But, so far, such type systems are quite rare to non-existent in production software.

                                                    1. 1

                                                      Currently waiting for Odesza / San Holo concert to start. Incubus / Sublime concert tomorrow night. And then dinner with the fam Sunday night :-)

                                                      1. 2

                                                        Seeing Odesza at the end of Aug. here in Mtl :)!

                                                      1. 16

                                                        Having scoped threads in std is great. First language, AFAIK, with this kind of structured concurrency in it’s stdlib. Definitely the first that also statically prevents data races.

                                                        1. 3

                                                          Yeah I agree!

                                                          I was wondering - what remains missing from “structured concurrency”? Just looking again at [1] I can see cancellation, error propagation and being able to create and pass around and inject the scope object s elsewhere (for spawning background tasks that outlive whatever calls s.spawn) as being things that I can’t immediately see as being possible or not. Does anyone know - would these already be possible with what’s there now?

                                                          [1] https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful

                                                          1. 3

                                                            Error propagation is already possible (as (Scoped)JoinHandle<T>::join() returns whatever was returned by the scoped/spawned routine).

                                                            1. 1

                                                              Lifetimes stop you from exfiltrating the scope handle outside the callback, yes. You can’t mem::swap it somewhere because the destination would need to have the exact same lifetime (and therefore, created from inside the callback).

                                                          1. 2

                                                            Good writup. I personally found this, combined with looping (see handmade hero looped live code editing), to be indespensible when debugging the netcode in my endless arcade multiplayer game.

                                                            I’m glad that Zig is making this an explicitly supported and optimized workflow. It’s a bit awkward in Rust, unfortunately, since traits don’t play very well with dynamic libraries. It still works with C-style interfaces, though.

                                                            1. 9

                                                              Microservices serve to introduce artificial boundaries that you cannot cheat around, like you might in a monolith. And it’s the sum of those little “one-time” boundary breaks that make old monoliths unmaintainable. Moving the boundaries to network makes you do it right every time. No more of those little breaks that make the monoliths unmaintainable. But the boundary doesn’t have to be network, it just has to be unbreakable. While this could be achieved with strict code review, I would be interested in seeing something that could be automatically enforced in language level.

                                                              1. 12

                                                                makes you do it right every time

                                                                I beg to disagree. At work, we use microservices extensively, and you end up with the wierdest boundaries. For example, we created an entire service to store a boolean per user, where the only operations are “set” and “get”, and very little scope for change.

                                                                Or you have an example where you have a central kind of entity lives in one service, and the lifecycle for that same entity is managed by another.

                                                                Or there was the case where we had one core service, driving 1) creating a message in the message store, 2) creating a new file in the file service, 3) binding that message to the new file, 4) finalizing the file, then 5) telling another service to upload that batch file. All that core service cares about was “this message needs to go out sometime”, so that should have been the interface. But, you know, that’s easy to say in hindsight, and when you’re not under time pressure.

                                                                Unless you have folks who have learned in an environment where it’s cheap and easy to restructure your boundaries, and you give folks enough time to design things well, then there’s a greater risk that you ossify bad interfaces. The (reasonble) trendency to have disjoint datastores per service, without being easily able to migrate data between services only makes this worse in my experience.

                                                                1. 8

                                                                  I’d say “no true Scotsman developer does this!” but they do, oh god do they ever.

                                                                  I want to pick on this specific example which is very much lived experience for me:

                                                                  Or you have an example where you have a central kind of entity lives in one service, and the lifecycle for that same entity is managed by another.

                                                                  DDD talks about bounded contexts, which group different parts of your business together. Sales, support, and reporting all care about “customers” – but they care about very different parts of that customer. If your microservice crosses bounded contexts, you’re in for hurt.

                                                                  Inside that bounded context are aggregates. Inside the sales context you might talk about a customer’s orders – maybe you have a customer, one or more ordered items, and a shipment date. But that’s an aggregate that should accept business messages: “create an order”, “void an order”, “approve an order”, (…). The functionality and data should live together. If you have a microservice for each piece, the burning will never stop.

                                                                  The problems I’ve seen with microservice design are almost always because - beating this drum again - developers select them for perceived technical reasons: I can use MongoDB here and it’s fast, I can use serverless and let it scale, whatever. And when you don’t do the proper business analysis around what you’re building, you end up building shiny little things that work great when you run ab against them in dev but fall flat on their face when - like your “get/set a boolean service” - introducing them means adding a network boundary for zero gain.

                                                                  1. 4

                                                                    What I meant is that it makes you perform the communication between the components without cheating every time. Not that those components make sense. Both monoliths and microservices do nothing to stop you dividing things in stupid ways, but microservices at least force you to divide them, instead of allowing you to access whatever and externalizing things that were meant to stay internal. Any interface design is still better than none, and in monoliths, you can pass by for a good while with zero time spent on designing the interfaces between the components.

                                                                    Oh, and I don’t like microservices. I very much enjoy a small set of well engineered deci-services over a ton of microservices.

                                                                    1. 4

                                                                      Any interface design is still better than none

                                                                      No. Nothing could be further from the truth. The wrong interface, once ossified, can kill a project or company.

                                                                  2. 3

                                                                    The problem is that people end up still “cheating” their way through boundaries, mostly by simply throwing the initial design over board and having it take other roles by exceptions thereby complicating things.

                                                                    It all sounds nice in theory and I used to be a huge proponent of microservices, but the reality in both small and big companies is that in cases where it works fine - good engineering, design, separation of concerns, etc. - the same thing could have been done in a monolith.

                                                                    A lot of the benefits only hold as long as they would if you took the same care with monoliths, only that you usually end up having to take care of the additional complexity of dealing with microservices.

                                                                    1. 1

                                                                      At least the disregard for initial design is explicit, in both ends, just by it being described as some sort of API. Often in a monolith you can’t even tell what was intended to be internal and what was not, and similarly, you can’t really tell if the usage of another component is done in an intended way or not.

                                                                      And yes, monoliths, or coarsely separated services are better than microservices IMO.

                                                                  1. 1

                                                                    Obligatory reference to Anscombe’s quartet:

                                                                    https://en.m.wikipedia.org/wiki/Anscombe%27s_quartet

                                                                    1. 2

                                                                      This puts the chip designer in a bind: if an instruction calls for v0 to be used as a vector input, then the low-order bits of v0 would want to be stored near the low lanes. But if an instruction calls for v0 to be used as the mask, suddenly those same bits want to spread across all the lanes! The ISA basically wants the designer to have the same bit in two different places at once.

                                                                      And, although I’m not knowledgeable enough about chip design to know what the best work-around is here, that’s basically what they’re going to have to do. I’d imagine most high-performance designs will resort to some kind of shadowing where they try to keep the bits stored as if they were a mask register when they come out of mask instructions, as if they were a vector register when they come out of vector instructions, and then have some hacks in there to work around cases where the instruction stream does something unexpected and the bits aren’t in the right place.

                                                                      Yeah, I don’t really think it is a big deal to have a shadow register for v0 to be used as a mask register. In the general case, the actual implementation of a CPU looks wildly different than what you might think just looking at the programming model.

                                                                      I think the github issue linked by /u/mordae covers the concerns of the OP fairly well.

                                                                      I’ll wait until we hear from some chip designers as to whether the current RVV v1.0 design is actually a problem or not.

                                                                      1. 2

                                                                        I’m certainly no computer architecture expert, but I also don’t see how it as a major concern. It doesn’t seem any worse than having a carry flag that is used as both an output from high-order bits and as an input for low-order bits. Or any worse than element re-ordering in packed SIMD. Yes, there will be wires crossing lanes. That’s quite common, though.

                                                                      1. 1

                                                                        Alternative title: Web dev rediscovers cache coherence

                                                                        1. 12

                                                                          A alternate way to approach this article is as learning material for all the other “web devs”.

                                                                          It’s a well told story with good insights into how one can improve performance by looking at it holistically.

                                                                          1. 2

                                                                            Putting aside the fact that the author is not a web dev (per grand-poster), the reasons why I posted the article are exactly the ones you phrased so well.

                                                                        1. 2

                                                                          These seem somewhat useful from a DevOps/SRE standpoint, but less useful from a Dev standpoint. Is that the intention? It would be great to see Devs caring about and measuring important things, like how long it takes to fix bugs and implement new features, and how long it takes for a new team members to become productive. If this was standard practice, maybe there would be less BS programming advice, or at least fewer people willing to accept it on faith.

                                                                          1. 3

                                                                            I’m impressed. I didn’t think CNTs were this manufacturable. But the on-off ratio isn’t great. And I’m guessing the contact resistance is terrible. Perhaps it will be competitive with silicon/germanium nanowires and perhaps it won’t. I’ve been out of the VLSI game for a while now, but I’m not holding my breath for anything to beat silicon/germanium anytime soon.

                                                                            1. 2

                                                                              I didn’t either. Really impressed with what they have achieved. Even if it took 100 tries to get to this point, the fact that we have created a non-silicon based microelectronic device is amazing.

                                                                              1. 2

                                                                                Yes, it’s an incredible achievement. I’m surprised I didn’t hear about it when it originally was published. I wonder if this is the only example we have of a modern large scale CPU implemented in a substrate other than silicon or gallium arsenide (which works pretty much like silicon).

                                                                            1. 1

                                                                              I’m not so sure we can dismiss this as not consciousness outright. While we can certainly say this entity is not as intelligent as humans in a plethora of ways, there are likely ways in which it is more intelligent than humans. And it’s not entirely clear that any particular amount or kind of intelligence is a prerequisite for consciousness.

                                                                              If Stephen Wolfram’s model of fundamental physics is in any way close to the truth, then consciousness is some kind of sophisticated computation. And these large language models are certainly a kind of sophisticated computation.

                                                                              What’s special about the way we humans experience the world? At some level, the very fact that we even have a notion of “experiencing” it at all is special. The world is doing what it does, with all sorts of computational irreducibility. But somehow even with the computationally bounded resources of our brains (or minds) we’re able to form some kind of coherent model of what’s going on, so that, in a sense, we’re able to meaningfully “form coherent thoughts” about the universe. And just as we can form coherent thoughts about the universe, so also we can form coherent thoughts about that small part of the universe that corresponds to our brains—or to the computations that represent the operation of our minds.

                                                                              But what does it mean to say that we “form coherent thoughts”? There’s a general notion of computation, which the Principle of Computational Equivalence tells us is quite ubiquitous. But it seems that what it means to “form coherent thoughts” is that computations are being “concentrated down” to the point where a coherent stream of “definite thoughts” can be identified in them.

                                                                              […]

                                                                              These are biological details. But they seem to point to a fundamental feature of consciousness. Consciousness is not about the general computation that brains—or, for that matter, many other things—can do. It’s about the particular feature of our brains that causes us to have a coherent thread of experience.

                                                                              From this perspective, I see no reason to conclude that LaMDA is not conscious or sentient.

                                                                              1. 5

                                                                                If Stephen Wolfram’s model of fundamental physics is in any way close to the truth

                                                                                Wolfram is universally regarded by physicists and mathematicians as a crackpot.

                                                                                1. 1

                                                                                  By no means universally. There are certainly many people who consider him a crackpot, though I doubt many of them are physicists or mathematicians. Mostly, though, I think people find him off-putting for completely unrelated reasons.

                                                                                  I have a PhD in Electrical Engineering and considerable training and experience in both physics and mathematics, and I certainly don’t consider him a crackpot.

                                                                              1. 5

                                                                                Whiley is a really interesting project. David, I really like your paper “Sound and Complete Flow Typing with Unions, Intersections and Negations”. I’ve found much use for its ideas several times in the past year. Thank you.

                                                                                1. 2

                                                                                  Wow, I wish I had known about this paper a year and a half ago, when I was trying to re-invent this, for an early prototype of Dawn. Looking forward to studying and implementing a prototype concatenative language using this.

                                                                                  1. 1

                                                                                    Hey, thanks!! Great to hear someone actually read it :)

                                                                                    1. 2

                                                                                      Hey, question for you: Have you thought about how flow typing could be combined with compilation to machine/object code? You could of course assign a unique type id to each type, but that would have to be done at link time, presumably, and then it’s not clear how dynamic linking could work. Any thoughts on this problem?

                                                                                      1. 1

                                                                                        Hey, so your question seems connected with low-level representation of flow typing. Yes, I have thought about this. Probably my preferred solution is actually to use type tags. So, a type like null|int has a one bit tag to distinguish null from int. However, this then raises separate questions, as to how map arbitrary combinations of unions and intersections (as in the paper) down to this. Happy to talk more about it!

                                                                                        1. 1

                                                                                          Okay, interesting. I had considered that, initially, but then thought all the conversions between encodings would be too expensive. But maybe the compiler could choose encodings so as to minimize conversions, at least within a procedure?

                                                                                          1. 2

                                                                                            Hey, so yeah … this is a key question. I have thought about it a bit, though not made any specific decisions. You are right that you’d want to minimise conversions. I also agree that some conventions could be used to minimise the coercions, or at least make them efficient. Anyways, yeah, needs thought :)