3 ms·
> Macros can be easily replaced by other means, that is what Java and .NET do via annotations and compiler plugins. There are cases where text substitution is
by jstimpfle 8y ago
> Macros can be easily replaced by other means, that is what Java and .NET do via annotations and compiler plugins.
There are cases where text substitution is the right thing to do. How do you do the things I mentioned, for instance? Java in particular is an infamous example, requiring lots of hard to maintainable boilerplate. Tooling helps in some cases to write it, but can't help reading it, right?
Some examples from my current code
#define MAKE(x, y, z) [x] = { #x, y, z }
#define MSG_AT_EXPR(...) _msg_at_expr(__FILE__, __LINE__, __VA_ARGS__)
#define PARSE_LOG() \
if (doDebug) \
MSG_AT(lvl_debug, currentFile, currentOffset, \
"%s()\n", __func__);
#define BUF_RESERVE(buf, alloc, cnt) \
_buf_reserve((void**)(buf), (alloc), (cnt), sizeof *(buf), 0, \
__FILE__, __LINE__);
#define CLEAR(x) mem_fill(&(x), 0, sizeof (x))
#define SORT(a, n, cmp) sort_array(a, n, sizeof *(a), cmp)
#define RESIZE_GLOBAL_BUFFER(bufname, nelems) \
_resize_global_buffer(BUFFER_##bufname, (nelems), 0)
In Delphi I've had to manually write all these expansions, resulting in less maintainable code. Go look in the linux kernel, I'm sure there are tons of examples that you'd be hard pressed to replace by a cleaner or safer specialized means.
> I don't get where that 100x - 1000x factor comes from, most compiled languages aren't that slower than C, specially if you restrict to ISO C.
It's not so much about the language, but what you do with it and how you structure your code. Or, actually, how you structure the data. OOP culture is bad for performance.
If I were to chose the single best resource for this kind of argument, that would be Mike Acton's talk from CppCon 2014 on youtube. If you want to watch that. Note that I'm not his fanboy. These are experiences I've made on my own to a large degree. And the arguments apply just as well to maintainability if you ask me.
> The big difference is that I am only doing that in that function or package that is required to run like a 1500cc motorbike at full throttle to win the benchmark game, while everything else in the code is happy to run under allowed speed limits.
And so the rebuttal is: No. If your code is full of OOP objects you can micro-optimize a certain function like crazy, but the data layout and the order of processing are still wrong.
To give another anecdata, for my bachelor's thesis I had to write a SAT solver for clauses of length <= 3 in Java. I modeled clauses as POD objects holding 3 integers (the 2nd and 3rd of which could be -1). My program could do about 10M clauses before memory was getting tight and it was doing only GC for at least a minute before it would finally die. Note that all objects are GC'ed reference types in Java (as you probably know).
I then converted it to highly unidiomatic Java by allocating 3 columns of unboxed integers of length #clauses, instead of allocating #clauses POD objects. The object overhead went away, so I could do about twice as many clauses before memory was used up. And when it was used up, since there was basically no GC overhead, the program died immediately (after a few seconds of processing, without a minute of GC). The downside was that maintainability was drastically worse since I was using only the most primitive building blocks of Java, and none of its "features".
If that had been in C, I could have just stayed with the first approach, since C has only "value types". It would have been performant from the start. C would have yielded a more maintainable program since I would not have had to fight the language. I could also have chosen the second approach, and it would have been easier to write and read than the Java code (which required tons of boilerplate).
- pjmlp 8y agoYou know that Mike Acton is now working on Unity's new C# compiler team, right? And yes he is also having a role on the new ECS stack, which is just another form of OOP, for anyone that bothers to read the respective CS literature. Had you implemented your SAT solver in a GC language like Oberon, Modula-3, Component Pascal, Eiffel or D, among many other possible examples, then you wouldn't need such tricks as they also use value types by default, just like C.
- jstimpfle 8y agoI know and as far as I know he's trying to improve performance there. If you actually bother to watch the video you will find him ranting hard against mindless OOP practices. ECS as I understand it is pretty much not OOP. My idea of it I would call Aspect-oriented, i.e. extracting features from artifacts and stuffing them in global tables, which of course separate data by shape. If you look on wikipedia, the first association you will find is also Data-oriented programming (the term from the talk; it is about tables-shaped and cache-aware programming and I believe it was also coined by Mike Acton). Data-oriented programming stands particularly opposed to OOP which the games industry has found to scale miserably.
- pjmlp 8y agoYes, I did watch that talk back when he did it. I always follow CppCon talks. Then you should also watch the talks he did later at Unite, after joining Unity. As I mentioned regarding ECS, on CS literature. For example, "Component Software: Beyond Object-Oriented Programming" https://www.amazon.com/Component-Software-Object-Oriented-Programming-Addison-wesley/dp/032175302X https://www.amazon.com/Component-Software-Object-Oriented-Pr... First edition (1997) used Component Pascal, C++ and Java, while the 2nd edition replaced Component Pascal with C#. "Component-Based Software Engineering: Putting the Pieces Together" https://www.amazon.com/Component-Based-Software-Engineering-Putting-Together/dp/0201704854/ref=pd_rhf_dp_p_img_3 https://www.amazon.com/Component-Based-Software-Engineering-... ECS and Data-oriented programming aren't the same thing.
- 8y ago