[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)