10 comments

[ 2.3 ms ] story [ 31.8 ms ] thread
Oh, it’s Wolfgang. In computational math, he has a focus on research software that few others are able to do, he (the deal.ii team more generally) got an award for it last SIAMCSE. Generally a great writer, looking forward to reading this.
I would like to see a comparison between modules and precompiled headers. I have a suspicion that using precompiled headers could provide the same build time gains with much less work.
Thanks to author for doing some solid work in providing data points for modules. For those like me looking for the headline metric, here it is in the conclusion

  While the evidence shown above is pretty clear that building a software package as a module provides the claimed benefits in terms of compile time (a reduction by around 10%, see Section 5.1.1) and perhaps better code structure (Section 5.1.4), the data shown in Section 5.1.2 also make clear that the effect on compile time of downstream projects is at best unclear. 
So, alas, underwhelming in this iteration and perhaps speaks to 'module-fication' of existing source code (deal.II, dates from the '90s I believe), rather than doing it from scratch. More work might be needed in structuring the source code into modules as I have known good speedup with just pch, forward decls etc. (more than 10%). Good data point and rich analysis, nevertheless.
To be fair, C++’s modules make no sense, just like their namespaces that span multiple translation units.

It’s just more heavy clunky abstractions for the sake of abstractions.

Modules are an attempt to make part of the language what currently requires a convention:

- A component is a collection of related code.

- The component has an interface and an implementation.

- The interface is a header file (e.g. *.h) that is included (but at most once!) using a preprocessor directive in each dependent component.

- The header file contains only declarations, templates, and explicitly inline definitions.

- The implementation is one or more source files (e.g. *.cpp) that provide the definitions for what is declared in the header, and other unexposed implementation details.

- Component implementations are compiled separately (usually).

- The linker finds compiled definitions for everything a component depends upon, transitively, to produce the resulting program/dll.

So much can go wrong! If only there were a notion of components in the language itself. This way we could just write what we mean ("this is a component, here is what it exports, here are the definitions, here is what it imports"). Then compiler toolchains could implement it however they like, and hopefully optimize it.

It makes lots of sense to anyone used to large scale software development.

It is no accident that Ada, Java, .NET, and oldies like Delphi, Eiffel, Modula-2 and Modula-3 have similar approaches.

Even the way D and Python modules and packages work, or the whole crates and modules approach in Rust.

Naturally folks not used to Web scale don't get these kind of features.

The code block styling is less than ideal.
A few points

1) modules only really help address time spent parsing stuff, not time spent doing codegen. Actually they can negatively impact codegen performance because they can make more definitions available for inlining/global opts, even in non-lto builds. For this reason it's likely best to compare using thin-lto in both cases.

2) when your dependencies aren't yet modularized you tend to get pretty big global module fragments, inflating both the size of your BMIs and the parsing time. Header units are supposed to partially address this but right now they are not supported in any build systems properly (except perhaps msbuild?). Also clang is pretty bad at pruning the global module fragment of unused data, which makes this worse again.

I really wonder whether LLMs are helpful in this case. This kind of task should be the forte of LLMs: well-defined syntax and requirements, abundant training material available, and outputs that are verifiable and validatable.

Perhaps we should use LLMs to convert all the legacy programs written in Fortran or COBOL into modern languages.

You are far from the first person to have this very, very bad idea.

No, LLMs are not good at refactoring.