[HN Gopher] The compiler will optimize that away
       ___________________________________________________________________
        
       The compiler will optimize that away
        
       Author : 0-_-0
       Score  : 175 points
       Date   : 2021-05-01 23:38 UTC (23 hours ago)
        
 (HTM) web link (blog.royalsloth.eu)
 (TXT) w3m dump (blog.royalsloth.eu)
        
       | polishTar wrote:
       | Why can't a compiler optimize that? Perhaps I'm naive, but it
       | seems totally possible for a compiler to have some sort of
       | heuristic that allows it to do this transformation behind-the-
       | scenes when it makes sense
        
         | Blikkentrekker wrote:
         | The ironic part is that the compiler can't because the
         | languages are fairly low level for performance's sake.
         | 
         |  _C_ and _Rust_ make various guarantees about data layout which
         | programmers rely upon. _C_ compilers cannot turn an array of
         | structs into a struct of arrays because the layout of arrays is
         | guaranteed, and needed to make common and trivial _C_ accesses
         | work which work by pointer arithmetic.
         | 
         | Turning an array of structs into a struct of arrays is indeed
         | an optimization that would be possible in a language that did
         | not make such guarantees about data layout. Perhaps it would be
         | interesting to have a data structure that provides less
         | guarantees on lower level access that in turn could be more
         | aggressively optimized.
        
           | gpderetta wrote:
           | It is technically also possible in C as long as the program
           | can't tell the difference (thanks to the as-if rule). This is
           | hard to prove though and in practice requires whole program
           | compilation.
        
             | Blikkentrekker wrote:
             | The _C_ standard provides guarantees about data layout even
             | if the compiler can prove it will never be used internally,
             | for the sake of, say, external debugging.
        
               | gpderetta wrote:
               | And C compilers routinely optimize structures in such a
               | way that they are impossible to inspect in a debugger.
               | Because of SRA, non escaping aggregates might even not be
               | allocated on a memory location at all.
        
               | Blikkentrekker wrote:
               | What is an "escaping aggregate"?; this term has 80 hits
               | on _Google_ , none of which seem to be related to
               | programming.
               | 
               | Looking it up "aggregate" is a _C++_ term for which the
               | standard indeed does explicitly allow various
               | optimizations and guarantees about data layout are
               | weakened; this is not the case with _C_ arrays.
               | 
               | Do you have any practical evidence to _C_ compilers
               | altering the data layout of an array in any way?
        
               | throwaway17_17 wrote:
               | GP's comment is referring to Scalar Replacement of
               | Aggregates on the one hand. This is an optimization where
               | by the compiler will, for a function with a Struct
               | argument, only pass in a pointer to / copy of the actual
               | element of the struct used inside a function. Hence the
               | name, the compiler is replacing the aggregate struct by
               | one or more of the individual values it contains.
               | 
               | The reference to non-escaping aggregates, describes the
               | compiler optimizing a local only struct by keeping the
               | individual elements in registers during usage, and
               | because the struct as a whole does not leave the local
               | scope, these elements are just discarded when that scope
               | closes.
               | 
               | Both of these transformations/optimizations are performed
               | by all three industrial C and C++ compilers
               | (gcc/llvm/msvc).
        
               | gpderetta wrote:
               | Exactly. Thanks.
        
       | einpoklum wrote:
       | This article makes a bunch of invalid (sometimes implicit)
       | assumptions or claims:
       | 
       | *A 20 yro language's design decisions don't make sense today
       | because of the hardware perf situation change.*
       | 
       | 1. Most languages were not designed for optimal utilization of
       | the specific hardware situation during their conception.
       | 
       | 2. Even languages designed with performance in mind somehow are
       | still incredibly general and flexible things.
       | 
       | 3. The article itself shows that CPU hardware has been changing
       | along a mostly predictable path for decades. Everyone knew
       | Dennard scaling would end, even if you couldn't predict it to
       | within the single year. Everyone saw CPU latency was decreasing
       | much much faster than memory latency, already in the 1980s.
       | 
       | --
       | 
       | * The main problem that we have today lies in utilizing the CPUs
       | to their full potential.*
       | 
       | This is _a_ main problem, not _the_ main problem:
       | 
       | * There are other, specialized processing hardware (like GPUs).
       | 
       | * There's computation happening elsewhere on the system, e.g. on
       | disk controllers and NICs.
       | 
       | * And actually, if you look at desktop environments and apps:
       | They really don't need to utilize the CPU to its full potential;
       | they're a mess of entanglement of things which get in each
       | other's way and it's difficult to figure out why your system ends
       | up being sluggish despite your brand-new shiny hardware.
       | 
       | --
       | 
       | *Another problem with the current programming languages is that
       | they didn't evolve beyond the C-style type of programming. *
       | 
       | * C-style type of programming is just fine for C-style kinds of
       | programming tasks. Every language has strengths and weaknesses.
       | Some pairs of languages have a more obvious "X is an evolved Y"
       | or "X is a better Y" relationship, but not that many.
       | 
       | * Most languages do not evolve or change to exploit specific
       | hardware capabilities. Or at least - few language changes are
       | intended to achieve that.
       | 
       | --
       | 
       | etc.
       | 
       | It's not a junk article though. The data orientation of program
       | design is a useful notion, in many programming languages. It's
       | just that, instead of reading this blog post, which quotes Mike
       | Acton, you can just watch Acton's talk on this principle:
       | 
       | https://www.youtube.com/watch?v=rX0ItVEVjHc
        
       | commandlinefan wrote:
       | I've never been able to get my head around the "anti-
       | optimization" mindset. Like... what else should we be doing? If
       | you're going to be writing code anyway, you might as well take
       | optimal advantage of the hardware while you're at it. Now, some
       | people point out that attempts to optimize sometimes backfire,
       | but that's a different problem - that's something you should work
       | on improving though practice (and measurement).
        
         | fwip wrote:
         | "Anti-optimization" advocates doesn't say "optimized code is
         | bad." It means "other things are more important than
         | optimization."
         | 
         | One of those things is the amount of work required. For
         | example, if it takes 30 minutes to get a "good-enough"
         | solution, and 2 days to get a "very optimized" solution, most
         | of the time you should go with the good-enough solution,
         | because most of the code you're writing isn't performance-
         | critical.
        
       | truth_seeker wrote:
       | Extremely well written and communicated.
        
       | albertzeyer wrote:
       | Another orthogonal aspect with modern hardware is the increased
       | amount of parallel execution. Most of the standard programming
       | languages are not really designed to support this well. So we use
       | extensions like CUDA. But this is not really general purpose but
       | only for GPU.
       | 
       | Once we reach maybe 100 cores, or 1000 cores, or some orders of
       | magnitude more cores in the CPU, we have to have better general
       | purpose language support for this. Most memory is local for some
       | subset of cores. There are warps which execute the same code
       | (SIMT).
        
       | CameraSupra wrote:
       | Why aren't hardware designers designing hardware for the
       | languages and methods we have?
        
       | Bancakes wrote:
       | Wish normal people witnessed how few innovations are being
       | introduced on the CPU/GPU side in the 21st century. The high-
       | performance software industry is low-key carrying the hardware
       | architecture industry on its back. Shaders, pipelines,
       | coroutines, etc are there to make the programmer's job easier in
       | the end.
       | 
       | On my old nokia 3110c, I could access my calendar and
       | appointments instantly by clicking right on the d-pad. On my
       | Nokia Lumia 710, I had to wait for a splash screen. On my Nokia
       | 3.1, I had to wait for internet synching. The difference between
       | slow and fast software far outstretches that of slow and fast
       | hardware.
        
         | eska wrote:
         | > The high-performance software industry is low-key carrying
         | the hardware architecture industry on its back
         | 
         | I don't understand this statement. To me it evidently looks
         | like the opposite: the software gets worse (because most
         | programmers don't even learn how CPUs/GPUs really work), and
         | the hardware has to make up for it.
         | 
         | The reason the calendar loads up slower is not because the
         | hardware got worse, it's the software.
        
         | CyberDildonics wrote:
         | I can't believe anyone would say this, it is basically the
         | opposite of reality.
         | 
         | CPUs and GPUs have come a long way in two decades. Even CPUs
         | are incredibly fast, but between memory allocations, memory
         | layout, multi-threading and just weeding out terrible
         | algorithms, most software can probably be sped up by 100x.
         | 
         | > The high-performance software industry is low-key carrying
         | the hardware architecture industry on its back.
         | 
         | That doesn't even make sense. Do you think GPUs aren't more
         | powerful than 20 years ago and that the difference between
         | quake 3 and Order 1888 is software?
         | 
         | > On my Nokia 3.1, I had to wait for internet synching. The
         | difference between slow and fast software far outstretches that
         | of slow and fast hardware.
         | 
         | Right - doesn't this contradict everything you just said?
        
           | Bancakes wrote:
           | These are steady, predictable, iterative improvements. We
           | have nothing groundbreaking like 1970s-1990s. Of course I'm
           | not saying modern hardware is slow, but it's not progressing
           | like it used to.
        
             | CyberDildonics wrote:
             | That is both not true and not what you said at first.
             | Transistor density has continued to rise but there is only
             | so much you can do when people mostly run javascript on a
             | single thread.
             | 
             | There were a lot more break throughs in the early days of
             | electronic components too because everything was new.
             | 
             | CPUs are a world away from where they were two decades ago,
             | but people don't notice because typing into a facebook
             | window still lags. It is an incredibly superficial nonsense
             | way to look at what has really happened at the hardware
             | level.
        
       | andi999 wrote:
       | Why wouldn't a new programming language like python nowadays
       | decide to use a Gil? I thought the point was to make the
       | interpreter easier to write.
        
       | conradludgate wrote:
       | If you want to utilise this data oriented approach in rust, check
       | out soa-derive[0]. SoA stands for struct of array, rather than
       | the conventional array of struct.
       | 
       | [0]: https://crates.io/crates/soa_derive
        
         | d0tnet wrote:
         | This is amazing!
        
         | teh_matt wrote:
         | I hacked up something similar (https://gitlab.com/mbryant/funct
         | iontrace/-/blob/master/src/p...) a while ago. Your variant
         | looks much more useful!
        
           | conradludgate wrote:
           | Yeah I'm a big fan of proc-macros. If utilised well, it can
           | do some really great things
        
       | pif wrote:
       | Bullshit!
       | 
       | The most important feature for the utter most of enterprise
       | software is to be maintainable over requirement, managing and
       | employee changes.
       | 
       | Such optimizations as those described in the article make sense
       | only in a handful of situations, and using them must not be a
       | light-heart decision, because their cost is big.
       | 
       | I fully agree with going all in when it makes sense, but we all
       | know what is the root of all evil.
        
         | CyberDildonics wrote:
         | > Such optimizations as those described in the article make
         | sense only in a handful of situations, and using them must not
         | be a light-heart decision, because their cost is big.
         | 
         | This is really not true. Inheritance is not only slow it is
         | needlessly complicated. It introduces dependencies and
         | indirection while gaining nothing.
         | 
         | People who have never programmed before think having a car
         | class inherit from a vehicle class makes sense. Eventually they
         | realize that what they have are arrays of positions and
         | velocities that they need to combine. That could be multiple
         | files with lots of boiler plate nonsense or it could be a
         | single loop.
         | 
         | > I fully agree with going all in when it makes sense, but we
         | all know what is the root of all evil.
         | 
         | If you are implying optimization, you should realize that that
         | quote comes from someone telling his students not to noodle the
         | tiniest details like post or pre increment in their for loops
         | to get the compiler to output one less instruction when their
         | program isn't even done yet.
         | 
         | If you want modern software to run fast, you need to architect
         | for it first. Then you can make things work, then you can
         | profile and optimize. This idea that there are no consequences
         | to the early design decisions you make is a gross distortion of
         | the context of this quote.
        
       | bvrmn wrote:
       | > 4. Data oriented way of programming has its own set of
       | problems.
       | 
       | This should be a top reason. Author is too shy to reveal the true
       | cause.
        
         | bsdubernerd wrote:
         | Data-oriented programming is not hard by itself, rather as a
         | consequence of how programming is done and what sort of
         | flexibility you get in return.
         | 
         | The traditional (or should I call "inverse") approach is to
         | define what the program should do and make it modular enough so
         | that parts can be changed easily. For this to work well,
         | functions are given a state snapshot to read/operator or
         | change. Encapsulating this state into a struct/object allows
         | functions operating on the same data to be extended easily,
         | without having to think which functions do what, when and where
         | they're called.
         | 
         | Thinking about locality though does force you to think not only
         | about program structure, but overall program flow, which can be
         | much, much harder to implement in a flexible fashion.
         | 
         | Struct-of-arrays or arrays-or-structs has little to do with it,
         | although it's a common pattern seen when working with some kind
         | of highly structured datasets.
         | 
         | Look at high-performance graphics pipelines to see how this
         | affects the structure of the code. There's a lot of duplication
         | which is required to handle data which is structured in a
         | different way for efficiency, something which is not solved by
         | just SOA/AOS swapping, but requires thinking of what data is
         | used, what are the access patterns of the algorithm and how to
         | pack the data so that locality is optimized. It's also harder
         | to change as a consequence.
        
           | bvrmn wrote:
           | I have experience with performant cache-friendly code. Yes,
           | data oriented approach can be useful in some fields. But
           | author blames the whole industry having low demand of
           | performant code and wants python to accommodate cache-
           | friendliness. This is a high level BS.
        
       | theamk wrote:
       | Summary: the computer has changed -- memory latency measured in
       | CPU cycles has grown a lot. So we should not be using traditional
       | struct/class-like programming model, where we put all properties
       | of an object next to each other. Instead, we should be using
       | game-style "data oriented programming" a.k.a. "column databases"
       | for a much higher performance.
       | 
       | However, most modern languages (C, C++, Python, Java, etc..) are
       | not making it easy, and this is bad! Meanwhile you can data into
       | columns and enjoy faster programs.
        
         | antpls wrote:
         | columnar layout is only faster if reads are sequential
        
         | mjevans wrote:
         | Structs are easy to envision as a repeating pattern
         | tessellating across memory space; it makes slicing regions of
         | data and bulk memory copy (e.g. DMA) easy.
         | 
         | Yet if the algorithms mostly work with subsets of those fields
         | then a 'column database' type of data layout clearly has
         | benefits.
         | 
         | Though for the complex case I would prefer more syntax sugar
         | from the language.
         | 
         | A "column struct" datatype that might include tuning
         | suggestions for stride, size, etc; but would present a simple
         | interface for humans. Given multiple underlying blocks of
         | records a parallalizable foreach style loop might even shard
         | across NUMA distributed threads if supported at runtime.
        
         | tom_mellior wrote:
         | > However, most modern languages (C, C++, Python, Java, etc..)
         | are not making it easy,
         | 
         | The article shows how easy it is to declare a struct of arrays
         | in Java: You declare a struct (class). Inside it you declare
         | arrays. Done.
        
           | beagle3 wrote:
           | It is easy to declare, but it is not easy to use:
           | 
           | You can pass the whole thing, but if you want to pass a
           | subset of it? just one element?
           | 
           | When you want to extend it, you have to extend many
           | individual arrays, etc. When you want to copy entry 17 to
           | entry 50, you can't loop over the fields - you have to spell
           | out every field.
        
             | tom_mellior wrote:
             | A subset of an AntColony is another AntColony containing
             | sub-arrays. One element of an AntColony is an Ant which you
             | can instantiate with the data at the appropriate index from
             | each array. I'm not trying to be difficult, I really don't
             | see the problem with implementing these basic programming
             | tasks.
             | 
             | I agree that spelling out the fields can become tedious if
             | you have a lot of structures of arrays or lots of fields
             | per structure. A 100-line Python script can generate all
             | this code for you if you prefer. Sure, it's a "limitation"
             | of most programming languages that they don't provide this
             | out of the box. But not a big limitation, I'd argue. Many
             | languages even have features that allow you to write this
             | functionality as a library, 100% inside the language
             | (Python and Java annotations, for example).
        
               | beagle3 wrote:
               | > I really don't see the problem with implementing these
               | basic programming tasks.
               | 
               | That you have to write them over and over again for every
               | single class. You offer to do that with Python.
               | 
               | > But not a big limitation, I'd argue. Many languages
               | even have features that allow you to write this
               | functionality as a library, 100% inside the language
               | (Python and Java annotations, for example).
               | 
               | This is an argument of the "all languages that are Turing
               | complete are essentially equivalent" kind. True, but not
               | interesting in this context.
               | 
               | Ecosystem matters. If your Java program is the only one
               | that does these things, it may just as well not be Java.
               | You will be unable to use any other library without back-
               | and-force adapters, for example.
        
               | tom_mellior wrote:
               | > That you have to write them over and over again for
               | every single class. You offer to do that with Python.
               | 
               | Or with some other code generator. A mythical language
               | with SoA/AoS transformation "built in" will do exactly
               | the same under the covers. But it might not offer other
               | features you are used to, or (your argument) a
               | sufficiently rich library ecosystem. You might as well do
               | it yourself and use a language you are otherwise familiar
               | and happy with.
               | 
               | > This is an argument of the "all languages that are
               | Turing complete are essentially equivalent" kind.
               | 
               | Is it? You can't do what I described in C, for instance.
               | To be clear, what I meant when I wrote "as a library,
               | 100% inside the language" was that, given the appropriate
               | definitions _in a library, not in an external tool you
               | run before your build_ , you just write something like
               | the following Python:                   class Ant:
               | someField = ...             someOtherField = ...
               | @StructureOfArrays         class AntColony:
               | pass
               | 
               | or something the following Java:                   class
               | Ant {             int someField;             boolean
               | someOtherField;         }              @StructureOfArrays
               | class AntColony {         }
               | 
               | and get all the features you can dream of generated for
               | you inside the AntColony class, which you then use
               | exactly as if you had typed things out yourself. You
               | can't do this in C. This isn't an "all Turing complete
               | languages" argument as far as I can see.
               | 
               | > You will be unable to use any other library without
               | back-and-force adapters, for example.
               | 
               | I don't understand what you mean. In the above example,
               | the Ant class exists, so you don't need adapters. The
               | arrays inside the AntColony class exist (once the
               | annotation processor is done with them), so you don't
               | need adapters.
        
               | beagle3 wrote:
               | > You might as well do it yourself and use a language you
               | are otherwise familiar and happy with.
               | 
               | Nim, Rust, D, Lisp support this already as compiled
               | languages, without sacrificing anything - perhaps also
               | Zig - at the macro level. Jai offers it as a language
               | feature, but I don't think that's a particularly good
               | idea - it's just that Jai tries to stay simple in such a
               | way that doesn't let it be a user-level thing.
               | 
               | > Is it? You can't do what I described in C, for
               | instance.
               | 
               | Not exactly, no, but you can with "X-macros", and I have
               | - in fact - been doing that since 2005 or so, using only
               | features from C89 ; I admit I have not used Java in the
               | last 10 years, and then it was impossible - maybe some of
               | the revisions in the last decade made it possible "inside
               | the language" - what's the name of the feature I need to
               | look up?
               | 
               | > I don't understand what you mean. In the above example,
               | the Ant class exists, so you don't need adapters. The
               | arrays inside the AntColony class exist (once the
               | annotation processor is done with them), so you don't
               | need adapters.
               | 
               | And now, you try to use the standard library "Sort" or
               | "Find", it doesn't work without adapters, even if you
               | have comparators defined for Ant -- and many other things
               | that assume that objects are not part of a colony.
               | 
               | It's hard to explain in a short post. I occasionally use
               | a language called "K" of the APL family, which makes all
               | of this incredibly trivial from the get go. It's not a
               | new language - K itself IIRC predates Java by a couple of
               | years, APL (of which K is often considered a dialect) was
               | first specified in 1958 and implemented in 1962. It
               | basically considers everything an array - one or multiple
               | dimensional - and field access is nothing more than
               | having one of the axes specified by symbols rather than
               | numbers. This makes everything incredibly simple and
               | uniform to an extent that is hard to describe to someone
               | who has not experienced it. (And indeed, in the turing
               | sense it makes no difference .... but if you implement
               | your Java objects as "mapping from field to value" you
               | may get some of the benefits but will not really be using
               | Java or be able to use the ecosystem)
        
               | tom_mellior wrote:
               | > Not exactly, no, but you can with "X-macros",
               | 
               | I only gave this a passing thought before and was too
               | quick to dismiss it. Yes, I see how it can be done. Which
               | is just another argument that the whole thing is a non-
               | issue, no? Languages with macro systems can do it.
               | Languages with other build time code generation systems
               | can do it as well.
               | 
               | > what's the name of the feature I need to look up?
               | 
               | Annotation processors. Here's the first thing I found
               | with an example of code generation:
               | https://cloudogu.com/en/blog/Java-Annotation-
               | Processors_3-Ge.... Yes, it's considerably more verbose
               | than a macro system. But it's still a tiny, write-once
               | (or completely off-the-shelf) part of your overall
               | project.
               | 
               | > And now, you try to use the standard library "Sort" or
               | "Find", it doesn't work without adapters
               | 
               | OK, but the same is true for all the macro based systems
               | you mentioned above, isn't it? And if it isn't: whatever
               | you can generate with macros you can also generate some
               | other way.
               | 
               | Edit: You won't like this, but looking at the docs for
               | Java's Collections.sort method (https://docs.oracle.com/j
               | avase/7/docs/api/java/util/Collecti...: "This
               | implementation dumps the specified list into an array,
               | sorts the array, and iterates over the list resetting
               | each element from the corresponding position in the
               | array." So you can use it just fine if the SoA implements
               | the List interface. But yes, at the cost of temporarily
               | materializing an array of structs.
        
               | beagle3 wrote:
               | That what I refer to as the "Turing complete" argument.
               | Yes, you can definitely do all of those. But almost no
               | one does - and those that do, usually silo themselves
               | from the ecosystem.
               | 
               | Yes, if you like Java, you can definitely implement a
               | Java compiler/interpreter in C and get everything you
               | like about Java using C. But no one does.
               | 
               | Thanks for the reference, I'll read about annotation
               | processors later tonight.
        
               | tom_mellior wrote:
               | I appreciate the discussion, but I believe you're arguing
               | in bad faith.
               | 
               | > Yes, you can definitely do all of those. But almost no
               | one does
               | 
               | <shrug> That's what you say. What is this based on,
               | considering that until a few hours ago you didn't even
               | know that Java has this built-in code generation
               | facility?
               | 
               | When I search for "entity component sytem java", the
               | first hit is this project:
               | https://github.com/Rubentxu/Entitas-Java which uses a
               | "code generator [that] generates classes and methods for
               | you, so you can focus on getting the job done. Radically
               | reduce the amount of code you have to write and improve
               | readability. It makes more of a sea code less prone to
               | errors to ensure the best performance."
               | 
               | Sure, it's probably not as widely used as other
               | frameworks in other languages. Java isn't necessarily the
               | first thing on people's mind when it comes to
               | implementing AAA games.
               | 
               | > and those that do, usually silo themselves from the
               | ecosystem.
               | 
               | You have not shown this. And if you mean that your C
               | macro implementations do this -- fine. But you still seem
               | to find them useful enough to keep using them?
               | 
               | > Yes, if you like Java, you can definitely implement a
               | Java compiler/interpreter in C and get everything you
               | like about Java using C.
               | 
               |  _That_ is a Turing equivalence argument. But using a
               | very simple code generator for a very simple problem is
               | not the same thing. Given that Java syntax is very
               | similar to C, I wouldn 't be surprised if your existing
               | macros could be used 1:1, with the hardest part being
               | getting a Java build system to call the C preprocessor on
               | a Java file. Not quite the same thing as implementing an
               | interpreter.
               | 
               | (Also, as far as I'm concerned this was never about Java
               | per se.)
        
               | beagle3 wrote:
               | I appreciate it too, and I don't believe I am.
               | 
               | I wasn't aware it is now an integrated part of the JDK,
               | but it doesn't change much; I've used ANTLR decades ago
               | (and YACC even more decades ago) which is ... essentially
               | the same thing, except Java has now standardized and
               | simplified it so that you don't need to fiddle with your
               | Ant or Maven or Makefile.
               | 
               | > Sure, it's probably not as widely used as other
               | frameworks in other languages.
               | 
               | I've been doing professional work for over 30 years now,
               | so I think I have some perspective, which may of course
               | be different than yours. Code generators and processors
               | have always had their niches, but that's what they are -
               | niches - and they are quite small.
               | 
               | People used to use YACC, or ANTLR or Bison or Lemon or
               | whatever your favorite parsing tool is. That's because
               | they save an enormous amount of work in most use cases
               | where they are applied. But they also come with great
               | costs: Hard to debug when things go wrong, but most
               | importantly - inflexible. GCC and many, many other
               | projects move off parser generators at some point in
               | their life for flexibility reasons.
               | 
               | Qt has MOC, which was essential in older C++ and is now
               | IIRC optional because modern C++ has mostly caught up. It
               | is widely hated among Qt users, though it did offer
               | significant advantages. But I'm not aware of any project
               | that used MOC except to interface with Qt - the boundary
               | is always evident.
               | 
               | > You have not shown this. And if you mean that your C
               | macro implementations do this -- fine. But you still seem
               | to find them useful enough to keep using them?
               | 
               | How could I "show" this? It is obviously predicated on
               | the projects I've been involved with, which - I noted -
               | have not been Java in the last ten years. It's not like
               | it's something that can be "shown", except in some
               | statistical sense, the validity of which we will argue
               | forever.
               | 
               | And yes, people shy away from macro heavy C code
               | (whenever J source code or A source code gets posted on
               | HN, people are less than happy, e.g.
               | https://news.ycombinator.com/item?id=8533843 ; the whole
               | J system looks like that ).
               | 
               | I use macro heavy code only when I don't have to share it
               | with others, for that reason.
               | 
               | > But using a very simple code generator for a very
               | simple problem is not the same thing.
               | 
               | Well, perhaps we're not thinking of the same level of
               | simple - e.g. c++ template meta programming, which I
               | first used in 1995 is built in, and (literally) turing
               | complete, so it can do anything, but is far from "simple"
               | - not to actually write, not to use, and definitely not
               | to debug.
               | 
               | What I had in mind was things like
               | https://github.com/Hirrolot/poica - It uses C macros to
               | do incredible magic. All it needs is one #include and it
               | gives C introspection, oop and a few other things. Is it
               | simple? I wouldn't say that. But more importantly, is it
               | C ? I wouldn't say that, and so does the guy who made it.
               | It can be used in a C program, yes. It can e.g. go
               | further and define comparators for you so you can use
               | qsort() on the new types it helps you define. But it's
               | not really C because when you try to integrate it with an
               | existing C codebase, you'll find that you keep having to
               | translate things at every boundary - which is quite
               | likely to happen with a Java SoA library.
               | 
               | I think my point is that languages in common use (Java,
               | C, C#, ...), despite offering the facilities to locally
               | solve the issues described in the original article
               | (locality of reference mostly), they do not practically
               | allow these solutions to become widespread or common
               | because they are not part of the core, and as a result
               | you lose all benefits on library boundaries.
        
           | theamk wrote:
           | The code you to actually use it is pretty dangerous - you
           | need to keep the index and "main pointer" in separate
           | variables, but always use them together. And indexes are all
           | plain untyped integers, so no compiler will warn you if you
           | use wrong index variable. And GC is no longer thing, so
           | "write after free" and friends come back.
           | 
           | Going to column arrays in high-level languages brings back
           | many of the C bugs that we really hoped to forget.
        
         | speedplane wrote:
         | > Instead, we should be using game-style "data oriented
         | programming" a.k.a. "column databases" for a much higher
         | performance.
         | 
         | This makes logical sense, but I don't buy it in practice. Most
         | of the heavy data reads are handled by databases, which do
         | optimize for this stuff. I just doubt that, in most software, a
         | significant amount of software performance issues are a result
         | of poor memory alignment of data structures.
        
           | gumby wrote:
           | When your datasets are quite large a sql database becomes an
           | absurd bottleneck. Think of weather or aerodynamics
           | simulations.
        
           | nine_k wrote:
           | Cache misses can lead to _major_ slowdowns.
           | 
           | Everything depends on the access patterns in critical loops.
           | If you need most of the fields of a struct in each iteration,
           | the classic way is beneficial. If you need a narrow subset (a
           | "column") of fields in a large collection of objects,
           | splitting objects into these columns speeds things up.
        
         | aparsons wrote:
         | What do you mean by game-style here? I'm very intrigued. Any
         | place to read more about these?
        
           | foota wrote:
           | The keywords here are things like data oriented or
           | "structures of arrays". Some examples are unity ECS or
           | amethyst and bevy in Rust language.
        
           | astrange wrote:
           | It's called entity-component systems in games. Note that OOP
           | was designed for simulations but games, the only kind of
           | simulation people like using, don't use OOP.
        
             | scambier wrote:
             | It's "Entity Component System". The "system" is part of the
             | pattern name.
        
             | astrobe_ wrote:
             | I think the more accurate references is "Parallel arrays",
             | aka "Structure of Arrays" (SoA) [1]
             | 
             | The amusing thing is that SoA is something you could see in
             | old languages that didn't have data structures, or the code
             | of newbies that don't know data structures.
             | 
             | [1] https://en.wikipedia.org/wiki/Parallel_array
        
           | CodeArtisan wrote:
           | https://gameprogrammingpatterns.com/component.html
           | 
           | https://gameprogrammingpatterns.com/data-locality.html
        
           | elcapitan wrote:
           | Look for "Data oriented design" or "Structs of arrays".
           | 
           | Here's a talk on the former by Mike Acton:
           | https://www.youtube.com/watch?v=rX0ItVEVjHc
           | 
           | On the latter, there's Jai, a new programming language for
           | games (WIP, unpublished) by Jonathan Blow, who has been very
           | public in documenting the process creating it, and which is
           | centered around such concepts. There is some unofficial
           | documentation of the ideas on SoA vs AoS and how the language
           | can help switching between the two, e.g. here: https://pixeld
           | roid.com/jailang/overview/Features/SOA/#/overv... and here: h
           | ttps://github.com/BSVino/JaiPrimer/blob/master/JaiPrimer.md..
           | .
           | 
           | Edit: Just realized that the articale even mentions Jai
           | towards the end.
        
           | loopz wrote:
           | Gaming has always been pushing boundaries of what's possible
           | performance-wise. For high-throuhput data loops, there's
           | already established patterns used by gamedevs that maximize
           | use of cache and cpu/gpu.
        
         | jhgb wrote:
         | > So we should not be using traditional struct/class-like
         | programming model, where we put all properties of an object
         | next to each other. Instead, we should be using game-style
         | "data oriented programming" a.k.a. "column databases" for a
         | much higher performance.
         | 
         | Something like DataDraw (http://datadraw.sourceforge.net/) does
         | with C?
         | 
         | > However, most modern languages (C, C++, Python, Java, etc..)
         | are not making it easy
         | 
         | Funnily enough, it was yesterday when I though about how cool
         | it would be to make an implementation of Oberon-07
         | (http://oberon07.com ,
         | https://people.inf.ethz.ch/wirth/ProjectOberon/index.html - a
         | simple-enough language to experiment with in various ways) that
         | would use something like this as its in-memory data
         | representation.
         | 
         | It came up as a consequence of me wanting to have an
         | implementation of Oberon-07 for WebAssembly, and realizing that
         | the necessity of "restarting" the whole binary at any code
         | change (necessitated by how WebAssembly works) also allows for
         | some liberties in data representation (for example running out
         | of compressed IDs for instances of a type could be handled in
         | exactly the same way as changing the code of the running
         | program - by generating a new binary with expanded space for
         | these IDs and restarting it using the serialized old heap).
        
       | elcapitan wrote:
       | > The programming languages listed above are all over 20 years
       | old and their initial design decisions, like Python's global
       | interpreter lock or Java's everything is an object mindset, no
       | longer make any sense today
       | 
       | I wonder what general purpose languages (as the mentioned Jai is
       | meant for games primarily) will be the best fit for modern
       | architectures, besides obvious things like doing it in C and just
       | doing everything manually to fit the machine nicely.
        
         | beagle3 wrote:
         | It's the APL/J/K/Fortran model. It fits modern CPU and memory
         | architectures, it fits GPUs, and slow external storage.
         | 
         | And rather surprisingly, it always has, even when memory
         | latency was 1 cycle and there was no prefetch (for which you
         | have to go back to the late 70s). The first APL implementation
         | is from 1962 or so, first Fortran is 1955 or so.
         | 
         | R and Numpy use it to deliver their speed while still providing
         | high level semantics.
        
         | NohatCoder wrote:
         | I don't see any particular reason why Jai shouldn't be usable
         | for other stuff than games, there are no baked-in assumption
         | about any gamey nature of programs.
        
       | mvanaltvorst wrote:
       | "Programming languages are old, therefore they will never take
       | advantage of our hardware." Have you looked at how compilers have
       | changed the past 30 years? How they take advantage of SIMD? AVX?
       | The LLVM-revolution? It's delusional to think compilers will
       | never be smart enough to vectorise object-oriented code, which is
       | the main gripe of this article.
        
         | astrange wrote:
         | This article is about transforming memory layouts, which
         | compilers don't do. GCC has some really really basic support
         | for it which never works.
         | 
         | Autovectorizing doesn't transform memory layouts, which is part
         | of the reason it doesn't work that well either.
        
           | moonchild wrote:
           | The sad part is chris lattner (llvm author)'s phd
           | dissertation was on that exact topic. And yet nothing seems
           | to have come of it since despite llvm's hegemony.
           | (https://llvm.org/pubs/2005-05-04-LattnerPHDThesis.html)
        
         | SuchAnonMuchWow wrote:
         | No, the main gripe of this article is data locality, not
         | vectorization. Those two are completely orthogonal and
         | complementary: you can have data locality without vectorization
         | and vectorization without data locality, and both bring
         | performance improvements. But vectorization bring a constant
         | speedup (from x2 to maybe x16 depending on the ISA of the
         | processor), but data locality can improve performance much more
         | because memory accesses are thousands of times slower than
         | processor cycles.
         | 
         | But if the semantic of your programming languages doesn't allow
         | your compiler to change the layout of your data in memory, then
         | there is almost nothing the compiler can do to improve data
         | locality in your application.
        
           | barrkel wrote:
           | Struct of arrays layouts does put you in a good position to
           | apply vectorization techniques though. The article is rather
           | more about columnar vs row formats for program entities than
           | it is about locality generally. Columnar databases often use
           | vectorized evaluation strategies because it's low-hanging
           | fruit.
        
             | zaphar wrote:
             | The impetus for using a columnar format is precisely to get
             | data locality. It is absolutely about data locality
             | generally. You wouldn't take his approach at all if you
             | didn't get the locality with it's associated performance
             | improvements. It would be strictly more work for no gain.
             | Locality is the entire point.
        
       | beagle3 wrote:
       | > So far, the only programming language I know of that supports
       | this type of crazy data transformations is JAI,
       | 
       | As far as I know JAI is indeed the only language that explicitly
       | lets you switch between AoS and SoA with one bit, but the APL
       | family of languages - (APL, J, K, Shakti, and a couple more) has
       | basically - for 60 years no - taken the "data oriented" SoA
       | approach for storage, and provides the language support that
       | makes it as easy to use as the "object oriented approach" AoS.
       | (For some definition of "as easy as" - the languages themselves
       | are generally not considered easy to use, but within the
       | language, treating things in either way is straightforward, at
       | most a "flip" away, but usually not even that is needed).
       | 
       | Additionally, Nim macros (and I suspect Rust and D as well) allow
       | this to be a library-level thing as well. Lisp does too, of
       | course -- but Nim/Rust/D are much closer to the Algol family-and-
       | friends list given in the article.
        
         | celrod wrote:
         | ISPC also has first class support for "(array of) structure of
         | arrays", see: https://ispc.github.io/ispc.html#structure-of-
         | array-types
         | 
         | For example:                 soa<8> Point pts[...];
         | 
         | declares an array of struct of arrays, where each inner struct
         | array contains 8 elements. This would have the advantage of
         | playing well with the streaming prefetcher if you're working
         | with all fields of the `Point` type, and allowing the compiler
         | to use and increment a single pointer for loading/storing,
         | accessing each field with a small compile time offset (that can
         | get folded into addressing calculations, making them
         | essentially free).
         | 
         | Julia's StructArrays package is also very convenient:
         | https://github.com/JuliaArrays/StructArrays.jl
        
         | Tyr42 wrote:
         | I also think that it's a shame he didn't bring up NumPy &
         | Pandas, or R. He's just created a data frame and then
         | complained that there's no functions to sort it, but we do have
         | those. They are here.                   ants = pd.DataFrame({
         | "name": ["bob", "alice", "carol"],           "color":["red",
         | "blue", "red"],            "age":[1.1, 0.5, 1.2],
         | "warrior":[True, False, True]})         # Or read_csv to get
         | the data in.              # Count number of warriors.  True =>
         | 1, False => 0         ants['warrior'].sum() # Returns 2
         | # Count old red ants         ((ants.color == "red") & (ants.age
         | > 1.0)).sum() # 2 again              ants.sort_values(by="age")
         | 
         | I think Pandas and NumPy should be up there as making the
         | language easy to use in a SoA way.
        
       | jamesbrock wrote:
       | > What if a programming language would provide us with a
       | structure that would act like an array of structs, but internally
       | it would really behave like a struct of arrays? We could program
       | in the typical object oriented way that is convenient for humans,
       | while still enjoying in great performance due to playing nice
       | with the hardware.
       | 
       | > So far, the only programming language I know of that supports
       | this type of crazy data transformations is JAI,
       | 
       | Haskell supports this type of data transformation.
       | 
       | http://hackage.haskell.org/package/vector-0.12.3.0/docs/Data...
       | 
       | > unboxed vectors of pairs are represented as pairs of unboxed
       | vectors.
        
         | CyberDildonics wrote:
         | Haskell uses linked lists everywhere, so any transformation
         | like this likely does nothing for speed.
        
           | tom_mellior wrote:
           | The given link describes unboxed vectors. This is Haskell-
           | speak for "actual array of actual machine numbers".
        
       | dhsysusbsjsi wrote:
       | It's true that compilers won't optimise memory layout but I would
       | argue this is an issue for only a subset of the software
       | engineering industry. More important is robust bug-free code with
       | separation of concerns which these tools allow. Remember the
       | first rule of optimisation: "don't". The second rule of
       | optimisation: "Don't...yet".
        
       | jefftk wrote:
       | It's not a fun language to work in, but SQL does a lot of what
       | the author is asking for.
        
       | 0-_-0 wrote:
       | There is a Nim library inspired by this blog post that simulates
       | array-of-structs with struct-of-arrays (implemented through
       | macros):
       | 
       | https://github.com/liquidev/datarray
        
       | gumby wrote:
       | > What if a programming language would provide us with a
       | structure that would act like an array of structs, but internally
       | it would really behave like a struct of arrays?
       | 
       | I do this all the time (well, it's not uncommon) in C++. The nice
       | thing is I can mix the two with a bit of manual glue so callers
       | don't even have to know (though if you are doing this it's often
       | worth letting some callers know for the reasons described in this
       | post.
        
       | Const-me wrote:
       | Disagree about garbage collected runtimes.
       | 
       | A lot of widely used software is written in C or C++ and uses
       | malloc/free extensively (C++ new/delete is mostly a wrapper
       | around it). This results in memory layout worse than an
       | equivalent managed heap would be. Happens because the memory
       | allocated on C heap is immovable, while garbage collectors may
       | move data around to defragment the heap.
        
         | moonchild wrote:
         | Actually...https://arxiv.org/pdf/1902.04738.pdf
         | 
         | On a more serious note, most c programs I see don't do very
         | much of that, preferring to allocate fixed-size buffers on the
         | stack. Additionally, most GC'd languages that aren't java have
         | crappy GCs, and those GC'd languages that _are_ java lack value
         | types. This harms spatial locality quite a bit, and compaction
         | can 't fully compensate for that.
         | 
         | They are working on adding value types to the jvm, though; it
         | will be very interesting to see how performance changes. (I
         | expect not very much in practice--those projects for which
         | performance was important were already using whatever tricks
         | they needed to--but perhaps it will improve the ergonomics of
         | performant java.)
        
           | Const-me wrote:
           | > preferring to allocate fixed-size buffers on the stack
           | 
           | When working set is measured in megabytes, it fits in L3
           | cache of modern CPUs. Memory layout is not too important for
           | these programs.
           | 
           | > most GC'd languages that aren't java have crappy GCs
           | 
           | C# is good too. It also has value types, native memory spans,
           | SIMD intrinsics, and stackalloc.
        
             | PixelOfDeath wrote:
             | Don't forget that good cache locality also can cause data
             | being pulled into cache that the prefetcher did know
             | nothing about.
             | 
             | I can create you a shitty linked list that fits perfectly
             | in L3, but still has terrible cold cache performance
             | because each individual cacheline has to be pulled in one
             | by one.
        
               | Const-me wrote:
               | Indeed, there're edge cases. Still, how many people using
               | linked lists on the stack?
        
       | TazeTSchnitzel wrote:
       | The metaprogramming available in languages like Rust and C++
       | could maybe be leveraged to make the struct-of-arrays model
       | easier. Wouldn't be the same as native support, though.
        
         | adgjlsfhk1 wrote:
         | Julia has a package that provides incredibility easy to use
         | SoA. https://github.com/JuliaArrays/StructArrays.jl Julia's
         | metaprogramming and multiple dispatch mean that it can use user
         | structs fully transparently. Everything just works.
        
         | mhh__ wrote:
         | I've seen this done in D fairly easily, almost definitely
         | closed source unfortunately.
        
         | PixelOfDeath wrote:
         | There is some, lets call them experimental, C++ libraries for
         | that. But C++ not having static reflection is the main show
         | stopper on that frontier.
        
       | nine_k wrote:
       | I can remember the columnar approach used in Fortran back in the
       | day. Fortran did not have records, only scalar arrays, so a
       | natural way to represent a bunch of "objects" was to have a bunch
       | of arrays for each property value.
       | 
       | IDK if such prior art is any helpful today, though :)
        
         | disgruntledphd2 wrote:
         | This approach is _still_ used (conceptually, at least) in
         | statistical computing. NumPy and R are good examples of this
         | approach, and also have solutions for the indexing problems
         | outlined in the article.
         | 
         | That being said, statistical computing is at the mercy of the
         | high cost of matrix multiplication.
         | 
         | I think that you can alter the order of arrays in numpy, and
         | examine the performance difference.
        
           | grayclhn wrote:
           | I think the desired functionality is closer to Pandas than
           | Numpy -- you want coordinated arrays of different data types,
           | not higher-dimension arrays of the same data type.
           | 
           | R and Pandas aren't exactly known for their outstanding
           | native performance (they're generally fast _after_ converting
           | dataframes to arrays and outsourcing to C or Fortran) so
           | they're great demonstrations of the syntax for some of these
           | indexing problems but leave a lot to be desired.
        
             | cozzyd wrote:
             | ROOT TTrees will "split" class members to effectively
             | transform them into AoS although this is mostly for IO
             | efficiency.
        
       | jmull wrote:
       | There's a nice introduction to a data-oriented approach in here,
       | but it's buried in some junk.
       | 
       | E.g., I don't think a lot of developers believe compilers are
       | magic optimizers. And programming languages, including the ones
       | referenced, have changed quite a bit over the last 20 years. A
       | strong enough case for general data-oriented features would cause
       | at least some of them to support it.
       | 
       | Also, a great amount of code should be written for
       | maintainability -- that is, to be read and easily understood by a
       | future developer without special insight -- rather the
       | performance. Talk of "software cultists" is silly and shows how
       | poorly the author understands the issues driving software
       | development approaches.
       | 
       | Not to mention, a performance analysis of software systems that
       | stops at main memory is about only a subset of software (one that
       | has a pretty thin slice of overlap with "enterprise" software,
       | where your bottlenecks are overwhelmingly IO and DB).
        
         | jolux wrote:
         | To me, the goal of language implementations should be to make
         | the most maintainable code also the fastest code. I shouldn't
         | have to worry much about the performance characteristics of my
         | software outside of general concerns about algorithmic
         | complexity.
        
           | tirrex wrote:
           | > I shouldn't have to worry much about the performance
           | characteristics of my software outside of general concerns
           | about algorithmic complexity.
           | 
           | That's the point of the article. Algorithmic complexity
           | without hardware knowledge almost always give you bad
           | performance. If there is a language, only complexity analysis
           | is enough with it, be sure that language is slow as hell.
           | 
           | I think this kind of "free performance" can't be provided by
           | a language. Only thing a language can do, it can hide less,
           | let the developer reason about generated assembly. e.g : C.
        
           | secondcoming wrote:
           | That all depends on what type of software you write. I don't
           | think there'll ever be a language that ticks every
           | requirement box.
        
       | lordnacho wrote:
       | Good article. My own thinking when coding performance is also
       | towards data oriented approaches. For example Bevy in Rust. If
       | stuff needs to be fast, it needs to be in cache. To do that, have
       | everything nicely packed so you only ask for a chunk as often as
       | you need. Then when you need it, it's already there.
       | 
       | Thing about old fashioned OO is it's often fine enough for your
       | run-of-the-mill CRUD app. If you look at the latency chart he
       | provides, there's another level to it: going to a database
       | server. If you're gonna do that, you're not really in the speed
       | game anyway, so why not make it simple? Just write it in the most
       | uncomplicated way the language allows, and that's it. Waiting for
       | the DB will take way longer than filtering the ants, forget all
       | the optimizations.
       | 
       | Edit:
       | 
       | Forgot to mention with ECS, you have a different organizing
       | principle. It can be quite enlightening to work with "systems
       | that operate on data" as opposed to OO where you have the data
       | declared next to the functions that mutate it. It can add clarity
       | for certain problems. Somehow over the years I've found that the
       | animals/cars analogies given in OO tutorials are one of the few
       | places that fit well with the model.
       | 
       | If you imagine a game that has characters, relationships, items,
       | spells, and so on, it's often not that easy to model as
       | encapsulated objects. Does each character have a list of other
       | characters that they have a relationship with? What happens when
       | you want to change the relationship, or a character casts a spell
       | that temporarily changes the relationship? Can easily end up a
       | mess, because it's not obvious where such things should live in
       | an OO model.
        
         | dragonwriter wrote:
         | > If you imagine a game that has characters, relationships,
         | items, spells, and so on, it's often not that easy to model as
         | encapsulated objects.
         | 
         | I...disagree. ECS definitely has efficiency advantages for
         | games in terms of support performant implementations, which for
         | games is often overwhelmingly critical, but there's well
         | understand OO ways to address the modelling issue tou raise.
         | 
         | > Does each character have a list of other characters that they
         | have a relationship with?
         | 
         | It probably _acts like it does_ , but it probably doesn't
         | maintain it, instead deferring to a separate object that acts
         | as a repository of relationships, which are themselves objects,
         | which allows you to modify relationships without touching the
         | characters at each side (which may be more than two for
         | multilateral relationships).
         | 
         | > What happens when you want to change the relationship, or a
         | character casts a spell that temporarily changes the
         | relationship?
         | 
         | You retrieve and act on the relationship object, which knows
         | how to handle modifications.
        
           | megameter wrote:
           | That doesn't really encapsulate anything, then. You end up
           | with global data wrapped in classes.
        
         | kstenerud wrote:
         | It's mostly a question of hierarchy of data and
         | responsibilities. Character objects that administer changing
         | relationships are doing too much.
         | 
         | For example, relationships involving "characters",
         | "relationships", "items", "spells" etc can be handled by
         | objects like "party" and "inventory" and "spell book"
         | (glorified lists supporting a few custom named operations that
         | basically do the same things) which contain the links to other
         | objects. A spell that temporarily replaces your equipment
         | simply swaps out your "character's" "inventory" object with
         | some magical temporary one, and then swaps the real one back
         | again when the "effect" from that "spell's" "invocation" ends.
         | After this, adding support for something else like a spell that
         | swaps inventory with the victim or steals their equipment would
         | be trivial.
         | 
         | Convert this to functional terms and you've basically got a
         | hierarchy of data that you operate functions on and generate
         | new data to put back into the hierarchy, matching the morphing
         | environment. It's all about the data in the end, regardless of
         | your source point of view.
        
         | grayclhn wrote:
         | > If you look at the latency chart he provides, there's another
         | level to it: going to a database server. If you're gonna do
         | that, you're not really in the speed game anyway, so why not
         | make it simple?
         | 
         | OTOH, this is where I can see the biggest benefits from
         | building a language/system around the article's approach. You'd
         | really like to execute a lot of the logic on the DB (or some
         | other distributed system) which needs to be deeply integrated
         | with the DB to be as convenient and expressive as pulling the
         | data and operating locally.
        
         | tgaj wrote:
         | > If you imagine a game that has characters, relationships,
         | items, spells, and so on, it's often not that easy to model as
         | encapsulated objects. Does each character have a list of other
         | characters that they have a relationship with? What happens
         | when you want to change the relationship, or a character casts
         | a spell that temporarily changes the relationship? Can easily
         | end up a mess, because it's not obvious where such things
         | should live in >an OO model.
         | 
         | It's not really that hard. You just have one
         | RelationshipManager that every Character have access to via
         | simple public methods. OO is not so bad, especially when you
         | forget about inheritance and use composition.
        
           | lordnacho wrote:
           | But splitting it out into another class is halfway there
           | already, no? The relations object doesn't correspond to a
           | real domain object anymore. Next step is just to split out
           | all the other components.
        
             | dragonwriter wrote:
             | > The relations object doesn't correspond to a real domain
             | object anymore.
             | 
             | "relationship" is a noun describing a real feature of the
             | domain, and thus is a real domain object.
        
               | lordnacho wrote:
               | You could say that about anything you construct in your
               | model though? Stops being OO if it's not somehow similar
               | to an admittedly vague concept of what an object is,
               | surely?
               | 
               | I could write the whole thing as an ECS and call all the
               | objects systems or components, and then they are nouns in
               | the domain?
        
               | dragonwriter wrote:
               | > You could say that about anything you construct in your
               | model though?
               | 
               | That's kind of the point, yes, model -> nouns -> objects.
               | 
               | > Stops being OO if it's not somehow similar to an
               | admittedly vague concept of what an object is, surely?
               | 
               | There's nothing particularly vague about "noun in the
               | domain"; linguistic-based modelling certainly isn't the
               | be-all and end-all of OOP modelling, but its one of the
               | traditional approaches dating back to the early 80s and
               | its more sophisticated than thr kind of naive tangible-
               | item approach that seems to be set up as a strawman here.
               | 
               | > I could write the whole thing as an ECS and call all
               | the objects systems or components, and then they are
               | nouns in the domain?
               | 
               | A relationship is a real thing in the domain being
               | modelled, "systems" and "components" are (in the sense
               | you are using them) not, unless the thing you are
               | modelling is a an ECS implementation of a domain. There
               | might be some utility for such a second-order model in
               | OOP (if the OOP is for a code generator, for instance),
               | but it wouldn't be a model of the domain addressed by the
               | ECS system, but of the ECS system itself.
        
           | mmis1000 wrote:
           | That sounds like a poor man's component if you already
           | separated it into another class...? And that data is no
           | longer belonging to the instance now.
        
             | dragonwriter wrote:
             | > And that data is no longer belonging to the instance now.
             | 
             | It belongs to the _correct_ instance. The noun in the
             | domain to which the information logically uniquely belongs
             | is a relationship, not a character (of which there is
             | typically more than one with different roles in a
             | relationship, the number and roles varying by type of
             | relationship.)
             | 
             | One problem that textbook OOP examples produce is that they
             | tend to favor a heirarchy of physical entities as examples,
             | which has the virtue of familiarity but really obscures the
             | analysis of nouns in the domain, the most important of
             | which in most real domains are the nouns describing
             | relationships, activities, and events.
        
           | beagle3 wrote:
           | Ah ... here we are again.
           | 
           | http://www.paulgraham.com/reesoo.html
           | 
           | OO is not so bad because OO is not well defined.
        
         | beagle3 wrote:
         | > Somehow over the years I've found that the animals/cars
         | analogies given in OO tutorials are one of the few places that
         | fit well with the model.
         | 
         | Yes, and I've never actually needed to implement a cat or a cow
         | in any project :)
         | 
         | The other thing for which OO works much better than plain data
         | is GUIs - and I think it is not a coincidence that OO
         | popularity exploded together with the the coming-of-age of
         | GUIs.
         | 
         | The other canonical example of OO - "Shapes" - doesn't actually
         | work well at all; It doesn't work better with "plain data", but
         | it exposes the fallacies of trying to use OO inheritance to
         | model the real world. Every square is a rectangle, so square
         | should inherit from rectangle ... but, you can't stretch width
         | and height independently in a square, so it's not _really_ a
         | rectangle, etc. etc.
        
           | dragonwriter wrote:
           | > The other canonical example of OO - "Shapes" - doesn't
           | actually work well at all.
           | 
           | Shapes work quite well with OO if you design the heirarchy
           | right using is-a relationships, the typically cited problem
           | involves applying is-a relationships which apply to immutable
           | geometric concept of shapes to mutable representations whose
           | mutation contracts don't support the is-a relationship of the
           | immutable shapes. This would actually be fine if the
           | contracts were written in a way which respects the it's-a
           | relationships properly. E.g., the common Circle-Ellipse
           | problem goes away if the contract for the mutator for, say,
           | the major axis of an Ellipse doesn't specify what the effect
           | is on the minor axis. For the base class, the two can then be
           | independent with the Circle subclass having them invariably
           | equal under mutation.
           | 
           | Alternatively, its not a problem with a Smalltalk like
           | "become", in which case an unequally stretched Circle
           | _becomes_ an Ellipse with appropriate attributes.
           | 
           | Or, the mutable elements arr restricted to things thst don't
           | change the shape like scale and location, and shape
           | transforms return a new Shape; if Ellipse's StretchMajorAxis
           | returns an Ellipse and so does Circle's, there's no problem,
           | either.
        
             | beagle3 wrote:
             | I should have added "in C / C++ / C#".
             | 
             | Everything OOP works well in Smalltalk (and most of it does
             | in Python), but you pay dearly for that in efficiency -
             | which is what this article as about.
        
               | dragonwriter wrote:
               | > I should have added "in C / C++ / C#".
               | 
               | The only part of my response that would have effected was
               | the aside about Smalltalk-style "become", not the main
               | point about the problem being one of constructing an
               | inheritance heirarchy based on relations of immutable
               | entity and using it for mutable entities with mutation
               | contracts that don't observe the same is-a heirarchy.
        
           | divs1210 wrote:
           | > The other thing for which OO works much better than plain
           | data is GUIs
           | 
           | This cannot be further from my experience. Switching from
           | Java/AWT/Swing to ClojureScript/Reagent/Re-frame has been
           | suoer liberating.
           | 
           | Every GUI programmer must fiddle with reagent once:
           | 
           | https://reagent-project.github.io/
        
             | iforgotpassword wrote:
             | Well, you really picked the worst representative with
             | awt/swing. I'd say Qt or even classic VB6/Delphi are great
             | examples for OOP gui development.
        
               | MaxBarraclough wrote:
               | How about JavaFX? Swing has been old hat for years now.
        
           | slifin wrote:
           | OO is a tree and html (UI) on a page is a tree
           | 
           | The problem is when you want to go to page two and want to
           | display the same data as page 1 but in a different
           | configuration
           | 
           | OO is suddenly terrible because the data is the wrong shape,
           | you should hold your data as a graph then derive trees out of
           | it to satisfy views
           | 
           | OO is not good for UIs unless you have one page and the
           | contents on that page is static and doesn't change it's
           | placement outside the statically defined tree shape of OO
        
             | beagle3 wrote:
             | I Was referring to the lower level GUI world, that in which
             | you implement "Button" and "TextField" - OO was, and still
             | is, a great win there. The Win32 / Cocoa / Xt level.
             | 
             | But even inside the browser, the fact that even
             | div/span/thing is an object, to which you can attach
             | listeners, and otherwise apply methods, works rather well -
             | I'm not familiar with a better alternative for
             | implementation and dynamic control of _this_ layer
        
             | dragonwriter wrote:
             | > OO is a tree
             | 
             | Inheritance heirarchies in single-inheritance languages are
             | trees. In MI they are DAGs. But neither of those constrains
             | _data_ representations, OO can support arbitrary data
             | graphs.
             | 
             | > OO is suddenly terrible because the data is the wrong
             | shape, you should hold your data as a graph then derive
             | trees out of it to satisfy views
             | 
             | How does this make OO terrible? What you describe is
             | exactly standard OO GUI practice (specifically, in
             | MV<whatever> architecture, the model layer is an arbitrary
             | graph representing the data, from which the view is
             | derived.)
        
           | [deleted]
        
           | acje wrote:
           | I'm on thin ice here. A systems complexity can be measured in
           | the number of tops it has. As in different ways to see it as
           | hierarchical.
           | 
           | Could it be that OO favors systems with one top? The tomato
           | example comes to mind. Is it a fruit or vegetables? According
           | to American law it was classified as vegetable at some point
           | to meet a political need to limit imports. Of course
           | botanically it's a fruit but usually perceived as a
           | vegetable. So three tops identified just in this example.
           | Depends on context; import, science or consumers.
        
         | rhn_mk1 wrote:
         | To elaborate on Bevy: the data-oriented approach used is called
         | an Entity Component System.
        
       | jupp0r wrote:
       | The implicit premise of the article seems to be that all software
       | has to be heavily optimized. This is completely wrong. All the
       | languages mentioned in the article are still around because it
       | doesn't matter how performant 99% of code is. For the 1%, we can
       | think about cache misses, SIMD and data parallel approaches. In
       | my experience this is totally possible and not too hard, but has
       | the enormous downside of making that code hard to reason about.
       | You are also optimizing for today's hardware, which means that it
       | might not run that great on new hardware in 20 years. This means
       | that somebody has to go in and rewrite that hard to understand
       | optimized code. It's really a tradeoff between the cost of that
       | and the benefits from optimization.
       | 
       | Some of the future costs can be mitigated by using a few
       | techniques from the start. One good idea is to keep an vanilla
       | unoptimized version of the part that you are optimizing around as
       | a reference implementation. This will help understanding what the
       | optimized code does and can be used as a stopgap to support new
       | hardware. It can also be a nice baseline to compare optimizations
       | against.
       | 
       | The beauty of compilers is that they do a decent job of
       | optimizing the 99% of code that would otherwise never be
       | optimized, at 0 cost. This is a different sweet spot on the cost
       | benefit curve.
        
         | esailija wrote:
         | I disagree. Almost all software I use these days is absurdly
         | slower than it should be and probably a massive drain on
         | collective productivity. I have ssd, fast internet, 64gb ddr4
         | etc, almost everything I do should be impercetibly instant but
         | instead takes seconds and even minutes.
         | 
         | Yeah of course most of that slowness is not even because of
         | ignorance of the low level stuff but complete disregard of
         | performance aspect of software. If it's even slightly more
         | convenient for a programmer to do something that is very stupid
         | performance wise, they will do it.
         | 
         | Sure developer time costs, but this cannot actually be the
         | reason. Because tooling is also ridiculously slow in exactly
         | same way and wastes countless of hours of that costly developer
         | time.
        
           | einpoklum wrote:
           | You're both right - but about different kinds of software.
           | 
           | I believe the GP post was thinking an application which has
           | some heavy computational kernel to it, where most of the
           | processor time (and other resources) are spent - with a lot
           | of other code for UI, configuration, some parsing etc.
           | 
           | And you seem to be talking about everyday desktop
           | applications: Browser, mail client, word processor, instant
           | messaging client, and maybe even your IDE as a programmer.
           | Those kind of apps don't have that kernel of hard
           | computational work - their work is much more diffuse.
        
       | smasher164 wrote:
       | Impedance mismatch. Languages don't give the compiler enough
       | semantic information to work with, and some require heavy
       | runtimes. Compilers, on top of having to infer semantic
       | information about the above, aren't able to sufficiently utilize
       | the hardware, either due to constraints on the types of analyses
       | done, or lack of control. The hardware exposes an API too high-
       | level or too unfamiliar to existing compilers.
       | 
       | IMO, the future is bright, because all these layers are being re-
       | thought. However, phrasing this as a debate over programming
       | paradigms is a red herring. If coupling data and behavior is a
       | useful mental model, we ought to accommodate that while giving
       | the compiler more to work with.
        
       | tester756 wrote:
       | >The hardware has improved, but the languages didn't
       | 
       | I kinda disagree with author, because """languages""" improved
       | 
       | Let's take a look at recent features in C#:
       | 
       | Spans and CPU Intrinsics
        
       ___________________________________________________________________
       (page generated 2021-05-02 23:01 UTC)