Haiku: Where to innovate?

Ah, I missed this. It seems it’s only for EHCI (USB2) though, and the commit it was added in says it’s “unfinished but working.” XHCI (USB3) doesn’t have it at all.

Most PCs still have a few USB2 ports at least… My KGPE-D16 doesn’t even have USB3 I guess that is due to it’s serverish design.

There is also a multi packet variation of isochronous transfers on xHCI which means multiple packets within 125usec apparently…

1 Like

Haiku was written in C++ when most OS were at the time written in C. This has been a differentiator.

Is there any value in becoming a reference implementation for how to do things in C++17?

Obviously this would break the compatibility of every api in the core OS. What I’m thinking of here is perhaps writing certain parts of it in the latest c++ standard, ideally leaving API untouched (for ABI reasons, if nothing else). E.g. Write a server or kernel module internals in c++17.

I have no particular axe to grind on the subject, although reading into the internals of a fairly large project like Paladin, I long for some features of c++11 I used on my last big project.

I just think it would be nice for newcomers to c++ today to have something as complex as Haiku providing an example of how things could/should be done with modern language features. Most libraries out there are too simple, or have a mishmash of c++ code from different versions, to be used as exemplars.

1 Like

You need experts in the new c++ standards. As i said in other post, i only began to use c++11 features recently and in the libraries I use for almost everything i dont think i have ever seen something more recent that c++11 as requirement.

So maybe you have c++14 libs now, but i dont think you will find c++17 experienced coders yet.

That’s not a differenciating factor on its own.

C++ is a tool. It is a great tool and it’s nice to see it put to use in this project.

We may need C++17 in some applications or servers at some point. I see no problem with that. I would not force myself to use C++17, and I don’t think it would be a differenciator. Just use it when you find some construct is convenient and solves the particular problem you’re trying to solve.

The differenciating factor in the way Haiku is written is rather using a single language for everything - something few languages besides C++ allow, but the number is growing these days - and keeping things simple and obvious. I read a book named “writing good code”, and something that I remembered about it is “good code is when everything does exactly what you expect”.

C++ makes it easy to write functions which operate at a single level of abstraction, making it possible to achieve this. When you use C, it is a lot of work to set up all data structures, parameter passing, memory allocation, etc that you need to achieve this. So it is very tempting to take shortcuts and write “bad code” because writing things the right way would be too much effort and complexity.

In C++, you can have the abstraction and usually not pay a lot in performance (thanks to templates, inlining, const / constexpr, etc). C++ is only an enabler to keep our code clean all while having reasonable maintenance costs.

I would say consistency also helps readability so if it is done in one place it should also be looked into if it can be applied across the codebase easily.

Your quote also parallels the principle of least astonishment. https://en.wikipedia.org/wiki/Principle_of_least_astonishment

Although I almost agree with this approach / strategy, this is not the whole truth. In the area of programming languages that speak to hardware, each single piece of convenience pays the price in terms of performance. There are no exceptions. When I say “convenience”, I mean the modality of reducing the code duplication and increase the level of abstraction.

Assembler can be said to be the “native” language of the hardware (it translates to machine code one-by-one and for this reason, it is single language that can be truly decompiled from machine executable code).

C is being said “portable Assembler” as it has human readable syntax and the same performance as Assembler. This is not entirely true. C code can be written to have the same performance as Assembler code, but this means to write it so low-level, it will look exactly as Assembler does. It means, C code can only be on pair with Assembler code in terms of performance only when you give up all the convenience C has over Assembler.

C++ is being said as “C with OOP”. Also it is being said that C++ is on pair with C in terms of performance. This is also not entirely true. C++ code may be written to have the same performance as C code, when you give up all its convenience over C.

I can continue with so-called dynamic languages, but I stop here, as Haiku is written in C++. My point is: each and every convenient language construction has the performance penalty. Of course, the common trade-off is to find the balance between easy-to-program and easy-to-maintain code on the one hand and performance on the other hand.

1 Like

This just isn’t true at all. Most of the convenience over C, at least in how we use it in the Haiku kernel, comes from things like RAII, scope-based destruction, virtual methods, etc. all of which have almost identical performance (or better, in the case of some things related to vtables), and often identical assembly, to their C counterparts.

Further, some languages that look very little like C in terms of memory model (like Rust) actually have better performance in naive implementations of algorithms, due to the constraints they place on the programmer that allow the compiler to more aggressively optimize memory accesses.

So the “easy to maintain” vs “performance” tradeoff is not so simple as you would like to make it out.

2 Likes

Also with a greater complexity on the readability of the syntax for the source code, and more headaches with the borrow checker, before getting code compiled. Safety, but after a whole process of thinking & coding.

C++ includes everything C does, and includes more tools. With more tools, I can do more things.

I’ve been forced to write complex embedded, real-time applications in C (paid job…). I ended up adding a manual vtable system, macros for managing linked lists, trees, whatever. Most of the time, the language was too low level for what I wanted to do, and as a result my C implementation is both less flexible and slower than what I could achieve a lot more quickly with C++.

The cost for most of C++ features is either something that you will end up implementing in C anyways, or a cost that you pay at compile time. And as I mentionned, using C++ allows to keep the abstraction levels appropriate, and thus makes the code easier to understand and maintain (when the abstraction levels are not mixed, it’s easy to replace the underlying mechanism while keeping the upper levels the same, for example).

3 Likes

That’s the thing though…readability is sometimes an illusion, that tricks you into writing code that is broken. The borrow checker in Rust is there because we are human… and having it raises the average quality of code written in rust, and because the code is better, its easier and better to use and reuse because there are fewer bugs.

You can spend your effort in different places, either debugging why you have a memory leak or spend it up front writing better code.

1 Like

We are still human, yes. But if you are following patterns/techniques in c++, like the mentioned RAII and reusing your already battle-tested modules, you shouldnt have problems. That’s why we have test code too (asuming you could cover most of the cases tho). Also, you can get tools like Valgrind to lead you to the leak sources, plus callgrind to see where you spend most calls (where valgrind is available and is not messing things tho).

In any case, i dislike c++syntax for complex calls/structures but the basic rust one is not goodlooking for me (having seen and developed in quite a lot of languages). The slow compilation time for it didnt help either (although last time i read about, Steve Klabnik said that they were improving that).

Not going to do the re-do-in-rust joke, but would Haiku gain something with part(s) being done in rust? I have not seen the code fail, if the system reaches the desktop state.

Been there too. STM32, some old olimex devices with a table sized flashing board…

“RAII only works for resources acquired and released (directly or indirectly) by stack-allocated objects, where there is a well-defined static object lifetime. Heap-allocated objects which themselves acquire and release resources are common in many languages, including C++.” (Resource acquisition is initialization - Wikipedia). That means, the price of using RAII is giving up using containers, such as std::{list, map, vector, etc.}, all of them having heap allocation. Scope-based destruction is closely related to RAII and have the same limitations. Virtual methods also pay their price (see e.g. What is the performance cost of having a virtual method in a C++ class? - Stack Overflow). The templates pay the time price during compilation and space price on compiled code, unlike generics in Java, which pay the time price during execution and virtually no space price. Lambdas pay enormous price and still are very limited in creation of closures. Assume Haiku doesn’t use them.

That is the whole my point :slight_smile:

I don’t know what the point of this comment is, but there are quite a lot of factual inaccuracies in it. Clang is now more advanced and easier to work with than GCC and Binutils in just about every respect except optimizations, and they are very quickly catching up here. There has already been a lot of talk about switching Haiku to be compiled with and include Clang by default for some time now.

Further, again, you can talk all you want about how C++ or Rust are slower than C, but the fact of the matter is they are usually faster – especially for things like vtables, as the C equivalents (for vtables, structs with function pointers) are slower. The more constructs the compiler understands, the more it can optimize them; to a certain point anyway.

Firefox is already compiled with Clang on Windows as Clang was faster in the generated binary performance on average even compared against GCC+PGO. I think windows does Clang+LTO+PGO these days, and all the mozilla nightly builds have LTO enabled for all platforms.

https://glandium.org/blog/?p=3888

2 Likes

My point is not to compare different implementations of some language standard (GCC vs Clang). My point is not to compare different reaincarnations of mostly the same language standard (C vs Rust) in terms of better / worse algorithms or memory management primitives. My point is not to compare the usual programming styles imposed by different programming paradigms due to completely different approach to the problem solving.

All my point is about the expressiveness of different formal languages. This theory is absolutely precise science with repeatable and measurable results. Each programming language has syntactic limitations of what constructions can be effectively implemented in it. Different generations of the same language add new constructions / primitives, that otherwise can be overcome at much higher performance penalty. Here are some examples:

  1. C has no OOP in terms of C++ (which started as “C with classes”). So in C structures there is no way to implement access permissions, inheritance or polymorphism, which C++ classes provide.
  2. C has no templates functions of C++.
  3. C has no try-catch construction of C++.
  4. C++ prior to C++11 has no way to use lambdas.
  5. C++ standard proposes closures for C++20 standard.
  6. C++ standard proposes introspection for C++20 standard.

None of this constructions can be implemented in a library if the language lacks them. Their implementation would involve definition of new language over the existing one. Naturally, the performance penalty of the later approach is huge. My point is that each new construction also adds some penalty to a language when is used for convenience and not for strict necessity.

Now, scientific part. Take 2 completely different and unrelated languages, A and B, with their concrete implementations Ai and Bi. Both are Turing complete, so it is possible to write almost any program in both, including the compiler for another language. Then we do the following:

  1. Take Ai and write in it compiler for B (name it BAi) and take Bi and write in it a compiler for A (name it ABi).
  2. Now we have 2 compilers for A: Ai and ABi and 2 compilers for B: Bi and BAi, where ABi is also a program written in B and BAi is also a program written in A.
  3. Having the program ABi written in B and B compiler BAi, we can compile the program with this compiler, obtain ABAi, which is also the compiler for A. Do the same with program BAi written in A and A compiler ABi, obtain BABi, which is also compiler for B.
  4. Now we have 2 compilers for A written in A: Ai and ABAi and 2 compilers for B written in B: Bi and BABi. Naturally, ABAi has some performance penalty over Ai and BABi over Bi.
  5. Take a problem, which features some principle, some feature of programming or of some language, or some technique. Given 2 very talented programmers, first in A and the second in B write 2 programs for this problem, one in A and another in B.
  6. Compile program in A with Ai and ABAi, compile the program in B with Bi and BABi. Measure the relative penalty of the first program compiled with ABAi over the same program compiled with Ai (name it PB). Measure the relative penalty of the second program compiled with BABi over the same program compiled with Bi (name it PA).
  7. If the PB > PA, then the language A is less expressive than B with respect to given feature / principle / technique, if PB < PA then the language A is more expressive then B in with this respect. And the relative expressiveness of B and A can be measured just as PB/PA.

The scientific part of this approach is that given concrete feature and concrete language standards A and B, the relative expressiveness doesn’t depend much on concrete compilers, on the problem chosen, on the program written for this problem and on the programmer’s skills. This is a measure of the languages themselves. Namely, if this feature is missing in B and present at syntactic level in A, the relative penalty PB/PA may be of orders of magnitude, if this feature is present in both languages, the relative penalty is a measure of how much better it is implemented in B than in A. Of course, you can take A = B and compare the relative penalty of AAi over Ai. If this penalty is large, the language A is not quite expressive with respect to given feature.

The conclusion is: if the performance is involved and if some language “feature” is not strictly necessary to be used, don’t use it just for the sake of fancy programming.

(continuation of previous post)

The problem of extra features performance can be illustrated in real process of bootstrapping of the new language or new version of the some language. Each language compiler, if it is self-hosted, is a triplet X/Y/Z, where X is certain standard of a language, Y is the compiler for the language X and also a program, written in some language Z.

Suppose, we have the language A/A/A and just released specification of its extension A+. There is no compiler for A+ yet. In order to provide one, we need the 3 stage process:

  1. Write compiler for A+ as program in in A and compile it with A/A/A. The result is A+/A/A.
  2. Write another compiler for A+, this time in A+ and compile it with A+/A/A. The result is A+/A+/A.
  3. Using the same compiler for A+ as in previous case, compile it with A+/A+/A. The result is A+/A+/A+.

No further compilations are necessary for the sake of performance and we can safely expect that the program written in A and compiled with A/A/A and A+/A+/A+ will perform almost equally well. But why is it necessary the 3rd step? Isn’t is just repetition of compiling the same program written in the same language? No, in the 2nd case the extra-features of A+ were provided by less capable compiler A, which implemented these features as a work-around, because it lacks thereof, which affects the resulting compiler performance. In the 3rd case the extra-features were provided by more capable compiler A+, which implements them as built-in capabilities.

Still, the new features in A+ over A were implemented as additional code, with additional runtime checks and routines. So, these new features will affect the performance of programs in A+ compiled with A+/A+/A+ when are used. It may happen that even if they are not used (i.e. the program is written in A), its performance still is worse when compiled with A+/A+/A+ then when compiled with A/A/A for the very same reason.

I don’t know if you have some computer science background, but in any case I will try to explain the technical view of the point also for other people that may be reading. When handling with complexity in theoretical informatics we proceed defining a single unit of time (or space), which we assume to be the smallest effort the CPU has to take to execute an operation. So, for example, an operation may be fetching memory and since it takes some CPU cycles more you may say it takes 6 units of time to execute. Suppose you do just a jmp, then it takes 1 unit (I am not going to extend on the branching problems, this is just an example). The point is that not all instructions take the same time. So when you are going to evaluate the true algorithmic complexity of a piece of software, it is indeed true that a C program may be slightly better, but it is also true that normally when going higher level with compiled languages you are not going to add very expensive operations to the code. So, to be more clear, generally expensive instructions are going to be the mass that really counts on top of the scale. When you write the same algorithm in C++ vs C, you are generally not going to get all that much difference. What matters is the theoretical complexity of the algorithm you are using. If someone implement a random sort instead of a bubble sort, when sorting an array, it doesn’t matter what language you use, you can certainly write it in hand-written assembly, but the few instructions you gain are not going to be incisive in any way.

Said that, I am not a big fan of the languages debate. And I will not take the topic of bytecode languages, since this is really another situation. But I think Haiku should provide some high level language for building modern user interfaces.

3 Likes

May I suggest QML (Qt Modeling Language) for that purpose? It conforms to the ECMAScript standard (same as JS), so people who are used to programming JavaScript won’t have a hard time picking it up. It can be compiled to native C++ binaries using the Qt Quick Compiler and has caching available for automatically generating compiled binaries to speed up subsequent startups. The logic can be made using JS and C++, depending on the developer’s preference.

It could be useful for UI/UX designers who are used to web development. It’s similar enough that there won’t be a large learning curve for them. At the same time, it is generally faster than JS due to caching and/or the ability to generate compiled native code, in addition to the ability of exposing C++ classes to it.

Eh… I’d rather have something much simpler and more maintainable than that.

Big companies build complex products so you have to pay for them to support them… when a small elegant tool would work just as well.

Perhaps YAML would work though… for on disk layout format for a UI editor? It’s certainly used on the web (being a superset of JSON), perhaps enhance the layout kit to be able to load such layouts (similar to what WPF does with XML) probably building them into the applications as resources. Auto generating “codebehind” for the UI would be quite nice also… VS does this and generates mostly readable auto generated code for the boilerplate.

A lot of what brings mom and pop applications, Industrial applications etc… that need to be written quickly at low cost and run reliably for decades is, the amount of automation that developers are afforded on other platforms. I mean there is no reason a developer should spend time writing boilerplate when he should be designing business logic or algorithms of his application. I can jump immediately from laying out my UI, to writing the application code itself…

Also the OS should inform applications what type of OS they are running on, such as Desktop/Tablet/Phone etc… and any layout model should be able to adapt. Desktop apps would never switch to a phone type UI, always just adding scrollbars if something doesn’t fit within the constraints of the layout. while handheld devices might rearrange the layout more freely… etc… also I’m not proposing “Metro” on Haiku by any means, just that it should be possible to implement a layout model that is very accommodating and flexible. ALM probably does a lot of that… but also isn’t familiar to developers at large.

Alot of the ALM and ALE ideas seem sound… I remember someone saying ALE is broken why is that?