The Windows malloc() implementation from MSVCRT is slow(erikmcclure.com) |
The Windows malloc() implementation from MSVCRT is slow(erikmcclure.com) |
MSVCRT basically just exists for backwards compatibility. It's impossible to improve this library at this point.
Glibc very much is better.
There cannot be more than a single version of a (tightly coupled) ld.so+libc.so pair in a given address space any more than there can be more than a single KERNEL32 version in a given address space, and given that some system services are exclusively accessible via dynamic linking (accelerated graphics, for one), this forces essentially everything on a conventional Linux desktop to use a single version of a single C runtime (whether Glibc or Musl). No need to guess whether you need CRTDLL or MSVCRT or MSVCR70 or MSVCR71 or ... or UCRT (plus which compiler-version-specific overlay?): you need a sufficiently recent libc.so.6.
I cannot say I like this design (in some respects I actually like the Windows one more), but it does force Glibc to have very strong back-compat mechanisms and guarantees; e.g. it includes a bug-compatible version of memcpy() for old binaries that depended on it working byte by byte. As far as I’m aware, this applies from the point GNU stopped pretending Linux did not exist and the “Linux libc” fork died, that is 1998 or thereabouts.
(There are a myriad reasons why old Linux binaries won’t run on a new machine, but Glibc isn’t one of them.)
This is not to say that Glibc is perfect. Actually building a backwards-compatible binary that references old symbol versions is a gigantic pain: you either have to use a whole old environment, apply symbol versioning hacks on top of a new one and pray the resulting chimera works, or patch antique Glibc sources for new compilers and build a cross toolchain. But if you already have an old binary, it is better.
I remember some funny problems with Glibc, like, 20 years ago, but it's been invisible to me (as a user) since then. You get a new Glibc, old binaries still work, it's fine.
On Unix systems (and Linux) this sort of thing can only ever happen if you have a statically-linked application that is linked with libdl and then it dlopen()s some ELF -- then you need the application's C library to be truly heroic. (Solaris stopped supporting that insanity in Solaris 10, though glibc apparently still supports it, I think?)
because developing for windows is cancer that people fall back to LLVM and others
it's all on microsoft for not cleaning their mess
even apple offers a better story than the platform for "developers, developers, developers, developers"
a shame, a well deserved shame, a hall of shame
It's true that you cannot just happily pass pointers around and expect someone else to be able to safely delete your pointer - but that is why any serious library with a C interface provides its own function to free objects you obtained from the library. Saying that this is impossible without MSVCRT implies that every software needs to be built with it, which is not even remotely the case. If I wanted, I could build all the C libraries I use with LLVM and still link against them in my application compiled with the latest MSVC runtime or UCRT.
The much bigger problem is mixing C++ runtimes in the same piece of software, there you effectively must guarantee that each library uses the same runtime, or chaos ensues.
MSVCRT.DLL isn't the library "everyone" uses; just Microsoft programs, and some misguided freeware built with MinGW.
Even if ADVAPI32.DLL uses MSVCRT.DLL, it's not going to mistakenly call the malloc that you provide in your application; Windows DLL's don't even have that sort of global symbol resolution power.
I would be very surprised if any public API in ADVAPI32 returns a pointer that the application is required to directly free, or accept a pointer that the application must malloc. If that were the case, you'd have to attach to MSVCRT.DLL with LoadLibrary, look up those functions with GetProcAddress and call them that way.
Windows has non-malloc allocators for sharing memory that way among DLL's: the "Heap API" in KERNEL32. One component can HeapAlloc something which another can HeapFree: they have to agree on the same heap handle, though. You can use GetProcessHeap to get the default heap for the process.
It may be that the MSVCRT.DLL malloc uses this; or else it's based on VirtualAlloc directly.
It was in the past. At first msvcrt.dll was the runtime library used up to Visual C++ 6. Later, VC++ moved to their own separate dlls, but you could still link with system msvcrt.dll using corresponding DDK/WDK up to Windows 7.
I'm also not sure that this is just ancient library left for compatibility, some system components still link to it, and msvcrt.dll itself seems to link with UCRT libraries.
At that time it was already a big mess, because at first it was the runtime library of Visual C++ 4 in fact! The gory details are here: https://devblogs.microsoft.com/oldnewthing/20140411-00/?p=12...
> some system components still link to it
Some system components themselves are very much ancient and unmaintained and only exist for backwards compatibility as well.
"Windows is not a Microsoft Visual C/C++ Run-Time delivery channel" https://devblogs.microsoft.com/oldnewthing/20140411-00/
In that environment I can imagine nobody wants to be on the hook for messing with something fundamental like malloc().
The complete trash fire that is O365 and Teams—for some reason the new Outlook kicks you out to a web app just to manage your todos—suggests to me that Microsoft may be suffering from a development culture that’s more focused on people protecting fiefdoms than delivering the best product. I saw this with Nortel before it went under. It was so sclerotic that they would outsource software development for their own products to third party development shops because there was too much internal politics to execute them in house.
The C runtime is doing exactly that, except it adds a bit of bookkeeping on top of it IIRC. And in debug builds it adds support for tracking allocations.
There's also CoTaskMemAlloc (aka IMalloc::Alloc). And COM automation has a bunch of methods which allocate memory for dynamically sized data, which could be abused for memory allocation - SafeArrayCreate, SysAllocString.
My understanding is mimalloc is basically a one-person project[1] from an MSR researcher in support of his research programming languages. It sounds like it's pretty nice, but I also wouldn't expect it to be somehow pushed as the default choice for Windows allocators.
[1]: https://github.com/microsoft/mimalloc/graphs/contributors
https://developercommunity.visualstudio.com/t/mallocfree-dra...
It's poorly documented, so I can't find a reference explaining what it is on MSDN save for a snippet on the page about the app manifests[1]. There's some better third-party "documentation"[2] that gets into some specifics of how it works, but even that is light on the real-world operational details that would be helpful here.
Chrome tried it out and found[3] it to be less than suitable due to its increased CPU cost, which might presage what Erik would see if they enabled it.
[1] https://docs.microsoft.com/en-us/windows/win32/sbscs/applica...
[2] (PDF warning) https://www.blackhat.com/docs/us-16/materials/us-16-Yason-Wi...
[3] https://bugs.chromium.org/p/chromium/issues/detail?id=110228...
https://blogs.windows.com/windowsexperience/2020/05/27/whats...
It mentions that the segment heap is used by default for UWP apps and reduces memory usage of Edge.
I mean, why exactly is the malloc of the compatibility msvcrt so slow compared to newer allocators? What is it doing?
An analysis of that would have been some actual content of interest.
So much so that beating it with a custom allocator is a real challenge.
Control flow is not a DAG.
I guess they're just trying to say that LLVM's control-flow graph is implemented as individually heap-allocated objects for nodes, and pointers for edges. (I haven't looked at the LLVM code, but that sounds plausible).
Even if those allocations are fast on Linux/Mac, I wonder whether there are other downsides of that representation, for example in terms of performance issues from cache misses when walking the graph. Could you do better, e.g. with a bump allocator instead of malloc? But who knows, maybe graph algorithms are just inherently cache-unfriendly, no matter the representation.
The halting problem is also not considered unsolved (though P=NP is unsolved).
Where do people get their opinions from? It seems like opinions now spread like memes - someone you respect/has done something in the world says it, you repeat it without verifying any of their points. It seems like gamedev has the highest "C++ bad and we should all program in C" commmunity out there.
If you want a good malloc impl just use tcmalloc or jemalloc and be done with it
Of course, there is a semantic point here. macOS nominally really is UNIX, except for when someone finds out it's not actually POSIX compliant due to a bug somewhere every year or so. Still, it IS UNIX. But what people mostly run with that capability, is stuff that mostly targets Linux. So... yeah.
Of course it is true that some people really think macOS is actually Linux, but that misunderstanding is quite old by this point.
addendum: I feel like I haven't really done a good job putting my point across. What I'm really saying is, I believe most developers targeting macOS or Linux today only care about POSIX or UNIX insofar as they result in similarities between macOS and Linux. That macOS is truly UNIX makes little difference; if it happened to differ in some way, developers would happily adjust to handle it, just like they do for Linux which definitely isn't UNIX.
UNIXes are the only OSes were there is an overlapping between OS APIs and libc due to C's origin.
An over-dependence on malloc is one of the first places I look when optimizing old C++ codebases, even on Linux and Darwin. Degradation on Linux + macOS is still there, but more insidious because the default is so good that simple apps don't see it.
...which is about two orders of magnitude bigger than the applications I write, so that's already a huge DO NOT WANT. Don't even get me started on the retarded UCRT mess that newer VS seems to force you to use...
Microsoft refer to the modern libraries as the "Microsoft C and C++ (MSVC) runtime libraries", so shortening that to MSVCRT doesn't seem unreasonable.
> it's statically linking with the CRT shipped with Visual Studio
that is, unless you know for a fact that they're wrong and clang uses the msvcrt from system32. in which case this seems like clang's fault?
The C runtime shipped in Windows is called MSVCRT.DLL and the ones shipped in Visual Studio are called MSVCRxxx.DLL, where xxx is Visual Studio's version number. If he statically linked to MSVCRxxx.DLL (MSVCRxxx.LIB actually) then what version did he link to? The performance of malloc() differs between versions.
Clang doesn't ship its own C/C++ runtime and certainly can link to MSVCRT.DLL. That is how legacy applications are built.
I think that PowerShell story was how old MS worked, back in the days of stack ranking, hatred of Linux and the Longhorn fiasco. things inside the company are a lot more functional now. I saw internal politics drama at my first position, but once I moved everything was chill, and experimentation and contributing across team boundaries was actively encouraged and rewarded.
I suspect Office suffers from a ton of technical debt, along with being architecturally amorphous and dating from a pre-cloud era. as for Windows, the amount of breakage I see in the betas suggests they're not afraid of making deep changes, it's probably that MSVCRT is a living fossil and has to support old programs monkeypatching the guts of malloc or something.
why can't i simply scroll up in my own conversations? let alone search them. the sticky sludge of communication in something as simple as chat has cost me hours since i was forced to use teams. outlook search is so superior to teams i'd easily prefer to have lync back. this one thing absolutely cripples communication. there are a list of other very basic issues that make communicating code blocks frustrating. i see new app features here and there, i saw some feature added the other day which won't help anyone. i just don't understand the prioritization of issues
i don't expect a direct answer to this, although i hope to read an explanation one day
EDIT: i removed content from this comment that was missing the point
The rate PMs change, how they lack understanding that we are fed up with rewrites, how they seem to believe we would still care and hope that in a couple of years WinUI 3.0 will actually be usable, how bugs in public repos accumulate,....
CMD badly needed replacing. MS needed a new shell language. A functional company would connect people with a passion for X with the resources to achieve X, if X has a chance of helping the company.
Windows Terminal and WSL show how far MS has come from the PS days.
(Disclaimer: I work for MS)
Apparently you have never worked at any company with more than 50 employees because that's literally how every large company works. Career-obsessed managers who can't see the forest for the trees not giving a single shit about the overall product as long as their goals were met. They're off to the next promotion before all hell breaks loose.
But it's not safe to do anything with unbounded time on a realtime thread, and malloc takes unbounded time. You should also mlock() any large pieces of memory you're using, or at least touch them first, to avoid swapins.
>If you want a good malloc impl just use tcmalloc or jemalloc and be done with it
This wasn't applicable until relatively recently.
... it still does ? I had a case a year or so ago (on then-latest Linux / GCC / etc.) where a very sporadic allocation of 40-something bytes (very exactly, inserting a couple of int64 in an unordered_map at the wrong time) in a real-time thread was enough to go from "ok" to "unuseable"
I did, in fact, call him out on that. I did not know exactly how those plugins worked then(though I have a much better idea now) but I already knew that it couldn't be so easy. The actual VST devs I shared it with concurred.
But it looks like he's simply learned more ways of blaming his tools since then.
It's interesting that LLVM is suffering so horrifically using default malloc. I really wish the author did a deeper investigation into why exactly.
Benchmarking Malloc with Doom 3 - https://news.ycombinator.com/item?id=31631352 - June 2022 (30 comments)
Then we have the surviving mainframes and embedded RTOS, which aren't UNIXes either.
There's a third set of users: programs built with old enough versions of the Microsoft compiler. Before Microsoft decided that every single version of its C compiler should use a different C runtime (and much later they changed their mind again), all Microsoft C and C++ compilers linked their output with MSVCRT.DLL. In fact, that's probably the reason MinGW chose to use MSVCRT.DLL: to increase compatibility with the Microsoft compilers, by using the same C runtime.
It is... not as simple as that[1]. MSVCRT itself is the fifth version of a Win32 C runtime, after CRTDLL (used to ship with the OS but no longer), MSVCRT10, MSVCRT20, and MSVCRT40. It’s just that both the toolchains linking with MSVCRT and the OSes shipping it (and nothing newer) endured for a very long time while Microsoft went on a collective .NET piligrimage.
Of course, “NT OS/2” started the same year ANSI C was ratified (and Win32 only a couple of years later), so some degree of flailing around was inevitable. But the MSVCRT Garden of Eden story is not true.
(this is not so much an issue with linux but with your threading library)
I'm completely in agreement that you shouldn't be mallocing, that was kind of my point - if you just got a key change from the cable stream and you can't get it decoded within your small number of millisecond window before the on-the-wire crypto changes you're screwed (I chased one of these once that only happened once a month when you paid your cable bill .....)
If your threading library isn't capable of handling priority inheritance then it's probably Linux's fault for making it not easy enough to do that. This is a serious issue on AMP (aka big.little) processors, if everything has waits on the slow cores with no inheritance then everything will be slow.
I think this isn't quite right - I think some distributions are actually certified as UNIX.
> You get a new Glibc, old binaries still work, it's fine.
Indeed, but when you need to build for an older glibc it's not so simple. This is a common use case, since e.g. AWS's environments are on glibc 2.26.
Ideally you'd like to build for all targets, including older systems, from a single, modern environment (this is trivial in Windows) -- and you can do some gymnastics to make that happen[1] -- but in practice it's easier to just manage different build environments for different targets. This is partly why building Linux wheels is so convoluted for Python[2].
Hardly a world-ending problem, but my point is simply that C runtimes are a pain everywhere.
1. https://stackoverflow.com/questions/2856438/how-can-i-link-t...
https://github.com/sjmulder/netwake does what you're talking about, but it does a lot of gymnastics to make it work, and it also needs to use MinGW rather than MSVC for that to be the case.
modern engines generally have a memory handler, which means that mallocs are usually coached in some type of asset management. you are also discouraged from extending working memory of the scene suddenly. When I was doing gamedev, even then, there was no reason to big malloc because everything was already done for you with good guardrails
But the UCRT is proprietary, so there are often legal issues with doing so.
So you have two competing copies, which leads to race conditions (different "global" locks!) and nice cashes, until you can analyze it with DrMemory or the kernel debugger. Nobody does that.
It also means that C++ or other runtimes don't pollute your ABI and make it annoyingly hard to access features from any random code.
According to: https://kb.iu.edu/d/agat "To use the Unix trademark, an operating system vendor must pay a licensing fee and annual trademark royalties to The Open Group. Officially licensed Unix operating systems (and their vendors) include macOS (Apple), Solaris (Oracle), AIX (IBM), IRIX (SGI), and HP-UX (Hewlett-Packard). Operating systems that behave like Unix systems and provide similar utilities but do not conform to Unix specification or are not licensed by The Open Group are commonly known as Unix-like systems."
Many will include the *BSDs as a Unix, because their code does directly descend from the original Unix code. But Linux distros generally do not meet either definition of "Unix".
(It also seems odd from my perspective to call exactly only those two Linux distributions "UNIX" unless you're essentially using it as a legal qualification and not a practical one)
No one is claiming it can't be a Unix. But as you noted, Linux distributions normally do not meet the legal criteria, nor are they descended from one that did.
Legally Unix is a trademark and has a very specific legal meaning. If you don't mean that legal meaning, then it is clearer if you use another term. The usual term is "Unux-like"; that is the widely-used term and it has been for decades.
A rose by any other name may smell as sweet, but calling it a different word risks confusion.
Another part is the trademark and certification fee. https://www.opengroup.org/openbrand/Brandfees.htm
Linux is as much Unix as WSL1 was Linux - i.e. not at all, just clones.
https://web.archive.org/web/20160410051513/https://blogs.msd...
in my opinion, the team or leader who is responsible for prioritizing issues in Teams needs a major adjustment. their flaws are brazen and affecting all of us who are forced to use Teams for communication.
outrageously slowly, and i'm not talking about a couple HTTP requests and a database query slow
> and search
search is not practical in my opinion. i'd go as far as saying it's unusable. i can _find individual messages_, but there are times (often, i might add!) where context or jumping to that message aren't even options. context is often the reason you search for messages in the first place. if i'm alone here, sure, PEBKAC
I think I understand why it works this way too. If you search _channels_, then the search will show the messages with that word but link you to the entire conversation that message appears in. But for chats, each individual message is basically treated like it's own conversation and thus search only displays the single message with no context.
A quick Google suggests I'm not alone in this criticism, and it's been a problem for even longer than I've been using Teams (a year or two): https://techcommunity.microsoft.com/t5/microsoft-teams/teams...
CMD is nasty but there are lots of little ways in which it could have been improved. For example, provide an option to disable that useless "Terminate batch job (Y/N)?" prompt.
I wish Microsoft would open-source CMD.EXE. I dislike how slow PowerShell is (especially its startup). Maybe nobody at Microsoft cares about CMD.EXE enough to fix those long-standing little annoyances like the above, but if it was open-source other people out there would.
Also, I wonder why nobody ever seemed to have thought of integrating CSCRIPT into CMD, so that you could have seamlessly mixed VBScript (or other WSH languages) into batch files.
In terms of being a programming language, I always regret when I use it. I recently had a minor heart attack when I realized that our CI was green when tests were failing; deep down in some shell script, someone forgot to turn on "pipefail" and the shell linter to check for that was misconfigured, and so the CI command that piped output to a logfile failed, but the log file was written OK, so "exit 0".
In terms of interactive poking around at a computer, I never really liked the UNIX philosophy here. I run a terminal emulator, which connects to a remote server over SSH, which is running a terminal multiplexer, which is running bash, which then runs my programs. None of these things know anything about each other. The UNIX way!!! The end result is total jank and some useful features are completely impossible to implement. The various shells running under the multiplexer overwrite each other's history. The terminal multiplexer can't give the terminal emulator its scrollback buffer. The shell history is specific to the machine that the shell is running on, not the machine that the terminal is running on. Echoing the character you just typed involves two TCP round trips! It's so bad, guys.
For that reason, I totally see the desire to re-engineer this space. There are a lot of improvements to be mad. Powershell is an interesting attempt. It doesn't solve any of my problems, though, and I personally don't enjoy using it. It's verbose and unergonomic, and still not a good programming language for making stuff happen. Windows is missing the glue ecosystem of things like "grep", "sed", "curl", etc., which make matters worse. (Powershell provides some of those things as cmdlets, but the "curl" one opens up IE to make you click something, and weird stuff like that.) It's nice that someone tried to make a new thing. I personally think it's worse than everything else out there.
TL;DR: "we've always used bash" leaves a lot to be desired. It's fine. But if someone says they can do better, I completely agree.
GitHub seems to have umpteen different variations on "store the shell history in a SQLite database": https://github.com/thenewwazoo/bash-history-sqlite https://github.com/trengrj/recent https://github.com/andmarios/bashistdb https://github.com/digitalist/bash_database_history https://github.com/quitesimpleorg/hs9001 https://github.com/hinzundcode/termmon https://github.com/fvichot/hisql https://github.com/bcsgh/bash_history_db https://github.com/jcsalterego/historian
This seems a popular enough idea, I wonder why it hasn't just added to Readline/libedit/etc? (Would the maintainers of those packages agree to add such an idea to them?)
> The shell history is specific to the machine that the shell is running on, not the machine that the terminal is running on
Once you've got the idea of shell history in sqlite – why not have a (per-user) daemon which exposes that history, some RPC protocol over a Unix domain socket, with the name of the socket in an environment variable? Then you can forward that socket over SSH? Again, something that could go in readline/libedit/etc, if their maintainers could agree to it.
> The terminal multiplexer can't give the terminal emulator its scrollback buffer.
Someone needs to come up with a new protocol here, instead of just pretending we are all still using physical VT series terminals over serial lines? The biggest problem with introducing any such protocol would be getting everyone to agree to adopt it. One could make patches for a few different terminal multiplexers and emulators, but would the maintainers accept them?
In hindsight, this particular one may be the best thing since sliced bread. But that's survivorship bias.
Thanks to symbol versioning I never know if a binary will work unless I can dismiss it early by versioning causing it to not link at all.
And given that glibc backward compatibility for all practical purposes is shorter than 10 years, having to support centos can get you a lot of those issues
As far as I’m able to tell[1], Glibc has never removed a versioned symbol as long as symbol versioning has existed, so the intended backwards compatibility is “forever”. No binary built against a(n unpatched) Glibc should fail to load on a later Glibc due to an absent Glibc symbol version. (Of course, if you play non-version-aware dynamic linker and dlsym() not dlvsym() things in libc, you lose; if you bundle an “updated” part of libc with the application[2], you lose; etc., so this may be true but still not entirely foolproof. And given how many things there are in libc, if you’re going to lose it’s probably going to be there.)
[1] E.g. https://sourceware.org/git/?p=glibc.git;a=history;f=stdlib/V...
(Hacks such as https://github.com/wheybags/glibc_version_header -- which apparently does work very well, but still feels like an annoying hoop that should be unnecessary to jump through. I wish glibc's shipped headers actually could support this out of the box so you could set a preprocessor define like `-DGLIBC_TARGET_ABI=2.12` and it would just work.)
... Duh?
(I’m not an expert in VSCode DRM, to put it mildly, so I might be misinterpreting this discussion, but that’s what it looks like to me. Also, isn’t it referencing GLIBCXX i.e. libstdc++, which not even a part of Glibc?)
That's an odd restriction, and it is related to the fact that all symbols live in one global namespace in a process. It's annoying if you are trying to build something like a plugin system, or if you are using a dynamic language which by definition loads all libraries dynamically. This is also the reason that you cannot mix Glib (GTK+) versions in a process.
I think you should be able to dlopen some library, or heck just load some machine code, and be able to run it, just take care to only pass POD over the boundary and never `free` stuff you didn't `malloc`.
This is the example I gave downthread. It's an everyday problem for a lot of developers targeting Linux, whereas targeting decades-old Windows versions is a breeze.
Glibc isn't better, it just has different problems.
(Not to say malloc is a perfect API, it’s definitely oversimplified, but they probably didn’t solve any of its problems.)
HeapAlloc (and legacy routines GlobalAlloc and LocalAlloc which wrap it) is mostly a relic of 16-bit Windows.
VirtualAlloc is the one that matters for language runtimes on Windows since Win32 API in the 90s, and it's designed to allocate slabs which are suballocated by more sophisticated code.
Yes, this unfortunately isn't the reality MSVCRT is in, but it is quite a reasonable expectation.
Multiplication is not a great argument... There's a long history of hardware that doesn't have multipliers. Would I complain about that hardware being bad? No, because I'd take a step back and ask what their priorities were and accept that different hardware has different priorities so I should be prepared to not depend on them. Same thing with standard libraries. You can't always assume the default allocator smiles kindly on your application.
Even on hardware without a multiplier you'd do a shift-based version, with log_2(max_value) iterations. What's unreasonable is "for (int i = 0; i < y; i++) res+= x;". If there truly were no way to do a shift, then, sure, I'd accept the loop; but I definitely would be pretty mad at a compiler if it generated a loop for multiplication on x86_64. And I think it's reasonable to be mad at the stdlib being purposefully outdated too (even if there is a (bad) reason for it).
To any capacity? That's insane.
It's not a reference implementation to show you what the correct results should be. It's the standard. The default.
So I donno, perhaps it's something funky with our Teams deployment, but this is such a basic feature that I have a hard time understanding how it could ever be implemented this way. Certainly no other chat service I've used has had this kind of problem.
WinRT hasn't died, that is what WinUI 3.0/WinAppSDK is all about, making that COM infrastructure available on the Win32 side, even though their progress is quite slow.
I think it will take them 2 years still to reach feature parity with UWP features.
Nowadays there are two WinRT models, the original underlying UWP that grew out of the UAP / WinRT evolution introduced in Windows 8.1, to simplify what was originally split across phones, tablets and desktop.
And now the WinRT implementation on top of Win32, started as Project Reunion, rebranded as WinAppSDK alongside WinUI 3.0.
Windows does have malloc as a C api for programs using the C runtime library. Same as everywhere else.
Then, at the OS api level, there are indeed several memory management functions. But you usually don’t need them. Except if you are writing a custom memory allocator for instance. Also same as everywhere else.
So saying Windows has X memory management functions instead of malloc is incorrect.
You're right, so not an example of a glibc failure, but rather another standard library. Thanks!
As you know, WinRT predates UWP. UWP as tech isn’t strictly defined but it includes things that are out of the scope of WinRT itself and aren’t available via WinAppSDK even now that UWP is finally, officially dead.
I will say that one should be wary of combining 3d graphics and font rendering and designing a programming language in the same project, at least if you want to get it done in less than 10 years. (I did talk myself out of writing a text editor, at least. Not touching that one.)
Like I said, very backgroundy. I don't have a good programming language spec. I do have text and UI elements that can be rendered at 360fps. (Yup, I have a 360Hz monitor. If you do things right, the lack of latency is frightening. It's like that "perfectly level" floor from Rick & Morty. But it is hard work to render a UI 360 times a second, especially when you're not using C++. Sigh!)
I have the exact same thought. I've been working on it, on-and-off, for years. I remember starting out when our son was a baby, and he's 9 now, and I still haven't got very far. Plus, now we've got two kids, and they are older, I have far less time to muck around with this stuff than I used to.
What I've realised after a while – "burn it all down" is unlikely to produce much results. I mean, if it is all just for fun (which is kind of all it is for me now), it doesn't really matter. But if one is hoping to make an impact in the real world, small incremental improvements building on pre-existing work are far more likely to do that than starting it all over from scratch, as tempting as that is.
> I will say that one should be wary of combining 3d graphics and font rendering and designing a programming language in the same project, at least if you want to get it done in less than 10 years. (I did talk myself out of writing a text editor, at least. Not touching that one.)
My priorities are somewhat different from yours. Own programming language? Yup, been through a few of those (every now and again I get fed up with it all and restart from scratch). 3D graphics? Well, I planned on having 2D graphics, never got very far with that, didn't even think of 3D. Mostly have just stuck to text mode, at one point I had my own incomplete clone of curses written in a mixture of my own programming language and Java (this was before I decided to abandon the JVM as a substrate and rewrite my own language in C–I started redoing my curses clone in C–why didn't I just use ncurses or whatever?–but it is very unfinished, never got anywhere near as far as my Java attempt did), also HTML to render in the browser.
But a text editor? Yeah, did that. Also, somewhat bizarrely, the text editor I wrote was a line editor (as in ed, ex, edlin, etc). Never actually used it that much for editing text. I meant to write a full-screen interface for it too (retracing the historical evolution from ex to vi), just have never got around to it.
> I don't have a good programming language spec
One thing I learned years ago – unless you love writing specs, they are a bit of a waste of time for this kind of stuff. Write your language first, create the spec later. Even for serious languages like Java, that's actually what happened (from what I understand)–the implementation of the language and the runtime was already several years old before they started writing the specs for them.
I would assume that quite a few people actually trying to write fast code would just assume that malloc, being provided to you by your OS, would be in the best position to know how to be fast. Certainly microsoft has the resources to optimize the two most frequently invoked functions in most C/C++ codebases, at least more than you yourself would.
MSVCRT being stuck with the current extremely slow thing, even if there are truly good reasonable reasons, is still a horrible situation to be in.
Not even then. You can just use an addition instead of a shift.
This will also make your interface accessible to other languages, if the need arises, since the C ABI is a de-facto stable ABI on all platforms (disregarding library objects and issues).
Another alternative if you want to stick with C++: make up your own lightweight COM! I've done this successfully in my own code at work, it works great, and has been working for 8 years now.
This method allows people to write C++ components against my app without using COM and without having the exact same compiler versions and having symbol problems. It may seem like a lot of work but it really isn't.
1) Expose a pure virtual interface IFoo, that doesn't include any implementation. Once it is released, never touch it, only create new versions which inherit from it, IFoo2, IFoo3 etc.
2) Expose an extern "C" CreateFoo() that will return FooImpl : IFoo. Also a matching DeleteFoo(IFoo).
3) For all structs / classes you need to receive/send from IFoo methods, create another interface. This includes all C++ classes, including std::string, hide it behind IMyString or something. This is a minor inconvenience but it sidesteps all ABI incompatibilities.
4) Have all interfaces inherit from some IBaseXX which has a GetVersion() method A component which uses this component could call this, and if it returns e.g. 2, then it can safely cast IFoo to IFoo2* and use IFoo2 methods. Else it can return an error message or use something from IFoo*
This relies on the fact that C++ vtable layout is essentially an ABI that will never change, at least under Windows, since the whole of COM relies on this and MS will never change it. Anything other than vtable layout in an object is subject to change, so the trick is to only have pure virtual interfaces.
I have no idea if this trick will also work on Linux, I don't know how stable GCC / Clang's vtable layout is from version to version, but I suspect it will.
This was taken from a CodeProject article I read a few years back, but I can't find it anymore... the closest I can find is [0] (DynObj - C++ Cross Platform Plugin Objects), but it is more complicated than what I read back then. I didn't use it, I wrote my own as I outlined above. Like I said, it isn't really that complicated.
[0] https://www.codeproject.com/Articles/20648/DynObj-C-Cross-Pl...
Or you can combine both: use a pure C interface which returns COM objects. That way, you can keep using COM without having to register anything with regsvr32 or similar.
> I have no idea if this trick will also work on Linux, I don't know how stable GCC / Clang's vtable layout is from version to version
Recent (as in, since around the turn of the millennium) GCC and clang on Linux use a standard vtable layout, defined by the "Itanium ABI" (originally created for Intel's IA-64, but like UEFI and GPT, became a standard across all architectures), so it's also very stable.
Were starting to convert my app from a Windows-only app to a cross platform one, so this makes me happy that my "COM lite" scheme would also be stable on linux.
As for returning pure COM objects through a C interface: Yeah, I've considered that, but like I wrote in my comment below to pjmlp, I don't like COM personally, and I want to simplify writing the components' code as much as possible, since the team which writes them isn't a team of software developers but rather in a supporting role. They do know basic C++/C# but don't know COM.
Also getting rid of COM would be the first thing done for cross platform support, anyway.
Registration free COM exists for several years now.
However in my experience it was always hard to get working properly, and it was always poorly documented. I'm talking primarily about the XML assembly manifest, which is very easy to get wrong.
In fact I remember vaguely the only complete documentation & example I could find at the time was an article in MSDN magazine, but now I can't find it, only bits and pieces of information scattered around, nothing in MSDN except a vague article. Most references I can find are for consuming dotnet COM objects and I also need to consume components written in C++. So the situation has gotten worse, documentation-wise.
Another couple of points:
1) Personally I don't want to use COM at all, I think it's too complex. I think it's really a brilliant idea but wrapped with over-engineered, idiosyncratic idioms and API. I tried to distill it to the minimum required by me for my "COM lite" above.
2) I'm not the one creating those components, I'm in charge of the consuming application. The people creating those components (in house) are essentially my support people, they're not really professional developers, and get confused with anything slightly more complex than plain C++ code. None of them has ever learned anything about COM, I'm the only one who knows all about it. Meaning I have to support them when things go wrong with COM registration or when they compile their code and it doesn't work. So I'm on a mission to get rid of all COM dependencies in my application, and replace them either with plain DLLs with C API, or (in one specific case) with my "COM-Lite" I outlined above.