proj-oot-ootNotes21

" The ubiquitous understanding that templates and variadic templates will produce much faster assembly because the C++ code will automagically vanish during compilation cannot be corroborated by my personal experience in decades of HFT and gaming, by no reputable peer-reviewed publications nor by anyone with some knowledge of compiler optimization. This is an ongoing fallacy. In fact, it is the opposite: smaller bits of code can be compiled, debugged and optimized by looking at the generated assembly much more efficiently than with templates and variadics.

Compilation times

The next stumbling block refers to compiling time. Templates and variadics notoriously increase the compilation time by orders of magnitude due to recompilation of a much higher number of files involved. While a simple, traditional C++ class will only be recompiled if the header files directly included by it are changed, in a Modern C++ setup one simple change will very often trigger a global recompilation. It is not rare to see Modern C++ applications taking 10 minutes to compile. With traditional C++, this number is counted in low seconds for a simple change. "

jupp0r 1 day ago

In my experience, the opposite of what the author claims is true: modern C++ leads to code that's easier to understand, performs better and is easier to maintain.

As an example, replacing boost::bind with lambdas allowed the compiler to inline functor calls and avoided virtual function calls in a large code base I've been working with, improving performance.

Move semantics also boosted performance. Designing APIs with lambdas in mind allowed us to get rid of tons of callback interfaces, reducing boilerplate and code duplication.

I also found compilation times to be unaffected by using modern C++ features. The main problem is the preprocessor including hundreds of thousands of lines for a single compilation unit. This has been a problem in C and C++ forever and will only be resolved with C++ modules in C++2x (hopefully).

I encourage the author to try pasting some of his code into https://gcc.godbolt.org/ and to look at the generated assembly. Following the C++ core guidelines (http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines) is also a good way to avoid shooting yourself in the foot (which is surprisingly easy with C++, unfortunately).

reply

vinkelhake 1 day ago

Fully agree with what you're saying. Just a nitpick about bind (which I'm sure you're aware of, just for the sake of others).

The return type of {boost,std}::bind is unspecified (it's basically just a "callable"). This means that bind doesn't have to do type erasure. On the other hand, {boost,std}::function has to do type erasure, which can boil down to a virtual function call. But that's orthogonal to where the callable came from.

Another thing to keep in mind is that if you're writing a function like `copy_if` which takes a callback, but doesn't have to store it for later use, it's much better to take the callback as a template type rather than going through the type-erasing {boost,std}::function. Doing the latter makes the compiler's job a lot harder.

reply

plorkyeran 1 day ago

It's not type erasure per se, but it's somewhat functionally equivalent. When you pass a function pointer (or pointer to member function) to bind(), it has to store that pointer in a member variable and perform an indirect call in operator(). In theory this is something that a compiler should be able to optimize away, but in practice they very rarely actually do, so using bind() as the argument for a STL algorithm typically does not result in the whole thing getting inlined out of existence. Lambdas, OTOH, are super easy to inline out of existence and compilers actually do so most of the time.

reply

---

catnaroek 1 day ago [-]

I'm not so sure. In C++, most certainly a mainstream language, two copies of the complex number `2 + 3i` are equal to each other. In JavaScript?, they're different. (Assuming you make two objects with fields `realPart` and `imaginaryPart`.) How can you do functional programming on a foundation where not even the most basic property of equality holds (every value is equal to itself)?

Remember that functions are mappings from values from a domain, into values from a codomain. A language whose treatment of compound values is as feeble as JavaScript?'s can't possibly constitute the right foundation for doing functional programming.

reply

---

catnaroek 13 hours ago [-]

Haskell's actual problem isn't the lack of a comprehensive standard library, but rather the presence of core language features that actively hinder large-scale modular programming. Type classes, type families, orphan instances and flexible instances all conspire to make as difficult as possible to determine whether two modules can be safely linked. Making things worse, whenever two alternatives are available for achieving roughly the same thing (say, type families and functional dependencies), the Haskell community consistently picks the worse one (in this case, type families, because, you know, why not punch a big hole on parametricity and free theorems?).

Thanks to GHC's extensions, Haskell has become a ridiculously powerful language in exactly the same way C++ has: by sacrificing elegance. The principled approach would've been to admit that, while type classes are good for a few use cases, (say, overloading numeric literals, string literals and sequences), they have unacceptable limitations as a large-scale program structuring construct. And instead use an ML-style module system for that purpose. But it's already too late to do that.

reply

---

https://www.microsoft.com/en-us/research/wp-content/uploads/2007/01/appsem-tcs.pdf

describes how a theoretical framework called 'full abstraction' or 'fully abstract compilation' relates to security holes of the form where source language invariants can fail to hold if the attacker can write linked code in the object language. 6 examples are given in C# (where C# is the source language and the CLR IL is the object language):

the first three of these were corrected:

random note on the common implementation of booleans: "most logical operations on bools interpret zero as false and non-zero as true, and hence are not affected by the possibility of values other than 0 or 1"

suggestions are given for the other problems:

some other security flaws:

" Nonetheless, (aiming for) full abstraction is just a start. Languages inevitably contain weaknesses, C ] included, and these weaknesses lead to security holes.

For example, the mutability of arrays is a common cause of security bugs in libraries for both Java and C#. (Typically, a programmer marks an array-type field or property readonly but forgets that the elements of the array can be mutated.)

The ability to apply checked downcasts can lead to holes too; one naive ‘solution’ to the mutability of arrays is to pass the array at a supertype that prevents mutation ( System.IEnumerable ); this fails because the array type can be recovered through downcasting. As the semantics community knows, the right way to think about such issues is by studying observational equivalence.

...

The complexities of industrial programming languages and platforms can make questions such as full abstraction and even type safety hard to pin down. For example, .NET, like Java, has a reflection capability that destroys any sensible notion of contextual equivalence: programs can reflect on the number of meth- ods in their implementation, or inspect the current call-stack. To be of any use at all, a definition of contextual equivalence has to ignore these capabilities (which, incidentally, are not available to untrusted components, i.e. the sort that we have been considering as potential ‘attacker’ contexts). "

---

((in a thread arguing about how cool Rust is:))

dikaiosune 8 minutes ago [-]

> But most likely - you simply do not need a language without a GC.

Absolutely. That doesn't mean I can't want predictable performance or deterministic destruction. I also think it's a shame that we waste so much electricity and rare earth minerals on keeping ourselves from screwing up (i.e. on the overhead of managed runtimes and GCs). Before, I'd have argued that it was just necessary. Having spent a bunch of time with Rust, I don't think so any more, and I'm really excited to see non-GC languages build on Rust's ideas in the future.

---

"Go says it can achieve 10ms max pause time using 20% of your CPU cores provided you give it 100% extra memory. In other words, memory utilisation must be kept below 50%."

[1]

---

" Note though that a lot of the problems with GC in crystal can be worked around by replacing classes with structs. The latter are passed by value and allocated on the stack. There is also access to pointers and manual allocation if that should be needed (though that will end up with roughly the same lack of memory safety as in C) to optimize a hotspot. " [2]

---

i guess i'm still feeling fairly eager to get-implementin' on an Oot Bytecode interpreter, even though there are many unanswered questions at the Oot Core level, and even though i haven't learned all of the other contender languages yet. What else is likely to make such a big difference to Oot Bytecode that beginning implementation now would be a waste of time? All i can think of is that i should:

---

 bluetomcat 16 hours ago | parent [-] | on: Crystal: Fast as C, Slick as Ruby

All of these "fast as C" claims about modern, high-level Python-like languages (be they statically typed and natively compiled) are missing the point. It is mostly the minimalistic and terse programming style that C encourages that makes C programs performant. You avoid allocations wherever possible, you write your own custom allocators and memory pools for frequently allocated objects, you avoid copying stuff as much as possible. You craft your own data structures suited for the problem at hand, rather than using the standard "one size fits all" ones. Compare that to the "new this, new that" style of programming that's prevalent today.

---

bluetomcat 16 days ago

parent [-]on: Memory management in C programs (2014)

> In C++, the use of RAII means that exceptions can be made to free any dynamically allocated objects whose owners went out of scope as a result of the exception. In C, we don't easily have that option available to us, and those objects are just going to stay allocated.

In GCC and Clang, there is the "cleanup" attribute which runs a user-supplied function once a variable goes out of scope, so you can have scope-based destructors:

    static void do_cleanup(char **str) {
        free(*str);
    }
    void f(void) {
        char *s __attribute__((__cleanup__(do_cleanup))) = malloc(20);
        // s gets freed once the function exits
    }

pjmlp 16 days ago [-]

Which is inherently not portable across other C compilers.

---

bluetomcat 209 days ago

parent [-]on: Why I love Rust

The whole C++ language is indeed a monstrosity designed by a committee that resulted from years of piling up "requested features", but with "modern" C++14 they try to narrow-down the idiomatic constructs to a smaller, more manageable set.

---

bluetomcat 189 days ago

parent [-]on: Undefined Behavior in LLVM [pdf]

On some architectures, an uninitialized value may contain either a trap representation or a valid value, so enforcing any consistent implementation defined behavior seems impossible.

---

to achieve deterministic builds, must not generate 'build identifiers' with the build time. But then what is the build identifier? It must depend only on the actual content of the build, therefore it must be a hash, like with the 'content-addressable' stuff.

---

i've said before that Oot needs a sigil that means 'platform native' (and mb this should be mixed with the 'inexact' sigil, eg ~, eg when doing arithmetic, for saying 'just use the platform-native arithmetic, even if it doesn't quite conform to Oot's arithmetic semantics).

In Clojure, i think '.' is used to indicate Java classes/methods?

another thing we need is 'tell me which underlying platform object instance is being used to implement Oot object x'. For example, if an Oot thread is created on an Erlang platform, then the Oot program should be able to do some 'reflection' on Oot threads in terms of asking the following questions of the implementation:

---

"You can learn TLA+ in a couple of weeks and start writing specifications in a month. There's a fairly slim tome called, Programming in the 1990s that demonstrates a practical technique for proofs that is digestible by programmers."

---

alanning 11 hours ago [-]

Thanks for sharing this. A few questions from someone interested in learning how to use BEAM-based systems in production:

I know Erlang in Anger [1] is kind of written just for this but it feels pretty intense to me as a newbie. I don't know enough to tell whether the issues there are only things I'd need to worry about at larger scale but I found it pretty intimidating, to the point where I have delayed actually designing/deploying a Erlang solution. Designing for Scalability with Erlang/OTP [2] has a chapter on monitoring that I'm looking forward to reading. Wondering if there's some resources you could recommend to get started running a prod Erlang system re: debugging, monitoring, etc.

1. https://www.erlang-in-anger.com/

2. http://shop.oreilly.com/product/0636920024149.do

reply

rdtsc 11 hours ago [-]

Good question. So first I noticed in metrics dashboard (so it is important to have metrics) the receiver never seemed to have gotten the expected number of messages.

Then noticed the count of messages was being reset. Suspected something restarted the node. Browsed through metrics, noticed both memory usage was spiking too high and node was indeed restarting (uptime kept going up and down).

Focused on memory usage. We have a small function which returns top N memory hungry processes. Notice a particular one. Noticed its mailbox was tens of gigabytes.

Then used recon_trace to trace that process to see what it was doing. recon_trace is written by author of Erlang In Anger. So did something like:

    recon_trace:calls[{my_module, '_', '_'}, 100, [{scope, local}].

dbg module is built-in and can use that, but it doesn't have rate limiting so it can kill a busy node if you trace the wrong thing (it floods you with messages).

So noticed what it was doing and noticed that a particular operation was taking way too long. It was because it was doing an O(n) operation instead of O(1) on each message. On a smaller scale it wasn't noticeable but when got to 1M plus instances it was bringing everything down.

After solving the problem to test it, I compiled a .beam file locally, scp-ed to test machine, hot-patched it (code:load_abs("/tmp/my_module")) and noticed that everything was working fine.

On whether to pick a message queue vs regular processes. It depends. They are very different. Regular processes are easier, simpler and cheap. But they are not persistent. So perhaps if your messages are like "add $1M to my account" and sender wants to just send the messages and not worry about acknowledging it. Then you'd want something with very good persistence guarantees.

reply

---

critium 12 hours ago [-]

I would add to the list;

EDIT: Side note. IMO, JMX extensions are one of the most under-appreciated things that java and jvm devs have but keep forgetting about but its so powerful.

reply

AdieuToLogic? 10 hours ago [-]

  IMO, JMX extensions are one of the most
  under-appreciated things that java and
  jvm devs have but keep forgetting about
  but its so powerful.

This is an excellent point which I cannot agree with more. When doing distributed systems using JVM's, I almost always reach for the excellent Metrics[0] library. It provides substantial functionality "out of the box" (gauges, timers, histograms, etc.) as well as exposing each metric to JMX. It also integrates with external measuring servers, such as Graphite[1], though that's not germane to this post.

0 - http://metrics.dropwizard.io/3.1.0/

1 - http://graphiteapp.org/

reply

---

honkhonkpants 11 hours ago [-]

On backpressure: "Common versions include dropping new messages on the floor (and incrementing a metric) if the system’s resources are already over-scheduled."

What I would add here is that "and incrementing a metric" must use fewer resources than would have been spent serving the request. I once worked on a system that would latch up into an overload state because in overload it would log errors at a severity high enough to force the log file to be flushed on every line. This was counterproductive to say they least. From that I learned that a system's flow control / pushback mechanism must be dirt cheap.

reply

---

marcus_holmes 13 hours ago [-]

I still miss COM. It had its problems, sure, but it worked really well.

I haven't seen anything since that allowed such decoupled development

reply

mike_hearn 8 hours ago [-]

I think you have rose tinted classes on.

COM is/was a rats nest of confusing and frequently duplicated APIs with insanely complicated rules that by the end really only Don Box understood. CoMarshalInterThreadInterfaceInStream? was one of the simpler ones, iirc. COM attempted to abstract object language, location, thread safety, types, and then the layers on top tried to add serialisation and documented embedding too, except that the separation wasn't really clean because document embedding had come first.

Even just implementing IUnknown was riddled with sharp edges and the total lack of any kind of tooling meant people frequently screwed it up:

https://blogs.msdn.microsoft.com/oldnewthing/20040326-00/?p=...

The modern equivalent of COM is the JVM and it works wildly better, even if you look at the messy neglected bits (like serialisation and RPC).

reply

asveikau 3 hours ago [-]

I think the good ideas from COM are: IUnknown, consistent error handling through HRESULT, the coding style that emerges from being clear about method inputs and outputs.

Some things done not as well as these core ideas: registration done globally in the registry, anything to do with threading, serialization, IDispatch.

I think in many situations you can take lessons from the good parts and try to avoid the bad.

I don't see how pointing out common bugs helps your argument though. You can write bugs in any paradigm.

reply

marcus_holmes 13 hours ago [-]

I still miss COM. It had its problems, sure, but it worked really well.

I haven't seen anything since that allowed such decoupled development

reply

mike_hearn 8 hours ago [-]

I think you have rose tinted classes on.

COM is/was a rats nest of confusing and frequently duplicated APIs with insanely complicated rules that by the end really only Don Box understood. CoMarshalInterThreadInterfaceInStream? was one of the simpler ones, iirc. COM attempted to abstract object language, location, thread safety, types, and then the layers on top tried to add serialisation and documented embedding too, except that the separation wasn't really clean because document embedding had come first.

Even just implementing IUnknown was riddled with sharp edges and the total lack of any kind of tooling meant people frequently screwed it up:

https://blogs.msdn.microsoft.com/oldnewthing/20040326-00/?p=...

The modern equivalent of COM is the JVM and it works wildly better, even if you look at the messy neglected bits (like serialisation and RPC).

reply

asveikau 3 hours ago [-]

I think the good ideas from COM are: IUnknown, consistent error handling through HRESULT, the coding style that emerges from being clear about method inputs and outputs.

Some things done not as well as these core ideas: registration done globally in the registry, anything to do with threading, serialization, IDispatch.

I think in many situations you can take lessons from the good parts and try to avoid the bad.

I don't see how pointing out common bugs helps your argument though. You can write bugs in any paradigm.

reply

marcus_holmes 13 hours ago [-]

I still miss COM. It had its problems, sure, but it worked really well.

I haven't seen anything since that allowed such decoupled development

reply

mike_hearn 8 hours ago [-]

I think you have rose tinted classes on.

COM is/was a rats nest of confusing and frequently duplicated APIs with insanely complicated rules that by the end really only Don Box understood. CoMarshalInterThreadInterfaceInStream? was one of the simpler ones, iirc. COM attempted to abstract object language, location, thread safety, types, and then the layers on top tried to add serialisation and documented embedding too, except that the separation wasn't really clean because document embedding had come first.

Even just implementing IUnknown was riddled with sharp edges and the total lack of any kind of tooling meant people frequently screwed it up:

https://blogs.msdn.microsoft.com/oldnewthing/20040326-00/?p=...

The modern equivalent of COM is the JVM and it works wildly better, even if you look at the messy neglected bits (like serialisation and RPC).

reply

asveikau 3 hours ago [-]

I think the good ideas from COM are: IUnknown, consistent error handling through HRESULT, the coding style that emerges from being clear about method inputs and outputs.

Some things done not as well as these core ideas: registration done globally in the registry, anything to do with threading, serialization, IDispatch.

I think in many situations you can take lessons from the good parts and try to avoid the bad.

I don't see how pointing out common bugs helps your argument though. You can write bugs in any paradigm.

reply

pjmlp 4 hours ago [-]

> The modern equivalent of COM is the JVM and it works wildly better, even if you look at the messy neglected bits (like serialisation and RPC).

Actually it is the WinRT? introduced in Windows 8.

reply

flukus 12 hours ago [-]

It came back with a vengeance in windows 10.

reply

marcus_holmes 8 hours ago [-]

really? I must have missed that... I stopped doing windows dev around Vista, because Vista.

Any details?

reply

pjmlp 4 hours ago [-]

The UWP programming model, which was introduced in Windows 8 as WinRT? is COM.

Basically it is the original idea of .NET, which as called COM+ Runtime, before they decided to create the CLR.

WinRT? is nothing more than COM+ Runtime, but with .NET metadata instead of COM type libraries.

Also since Vista, the majority of new Windows native APIs are COM based, not plain C like ones.

reply

douche 13 hours ago [-]

Do you remember DLL hell? Pepperidge Farms remembers /s

reply

asveikau 3 hours ago [-]

Library ABIs are hard. Same is true with shared libraries if you're not careful. I don't think it's necessarily the fault of the tooling.

reply

mcguire 2 hours ago [-]

The tooling didn't include version numbers.

reply

---

random somewhat interesting things, i already skimmed, no need to read them:

https://en.wikipedia.org/wiki/Restrict

https://devblogs.nvidia.com/parallelforall/six-ways-saxpy/

---

some random C and Fortran code with compiler pragmas:

" 2. OpenACC? SAXPY

If you have been following Parallel Forall, you are already familiar with OpenACC?. OpenACC? is an open standard that defines compiler directives for parallel computing on GPUs (see my previous posts on the subject). We can add a single line to the above example to produce an OpenACC?