2025-06-17

An orange ring for git submodules

At work we have a common library, call it libThing, that underlies several of the products we produce for one of our big customers. We bring it into the projects that use it with git submodules. And that's a bit of a problem.

Let me explain.

You see, the design of the submodules facility is clearly predicated on an understanding that the submodule is a separate thing, and is not edited in situ by a programmer working on the super-project. If the sub-project needs updating, it is assumed, you will send the maintainer a well defined change request, wait for it to happen, and point your super-project at the updated version and go about your business.

Which is what you would expect if the sub-project belongs to someone else.

And some of our changes look like that: "Folks, we have a note from the customer. There's a new file format for specifying widgets. Someone needs to update the WidgetLoader in libThing to handle it. Joe, you've worked in that module recently, can you get to it this week?". Fine. Joe updates libThing, pushes to the reference repository and the next release of each of our project can manage the new Widget files. Nice. And exactly as Linus envisioned it.

On the other hand a lot of times we find out that libThing needs updates because we're in the course of making changes to one of the project that use it. By working both side together we can work through the trade offs dynamically. Its more natural, and probably faster, to just work on them together. Even though submodules doesn't encourage it.

Buuuut ... if you're not careful, you'll make one or more commits to the sub-project in a detached head state.1

The rest of this post is a recipe getting safely back to a happy state after you make this mistake.

Advancing the branch your aren't on

If you had done this right, you'd have put the subproject on the branch before you started editing and the branch would have advanced as you made commits. We want to get the repository into the state it would have had.

  1. Determine the name of the branch you're suppose to be on by looking in the .gitmodules file of the super-project. Remember that for later.
  2. Give the current state of your work in the submodule a temporary name with git checkout -b life-preserver2
  3. Get on the right branch with a git checkout to the branch you found in step 1.
  4. Fast-forward with git merge life-preserver (and it should be a fast-forward merge; if not you should probably take stock at this point.)
  5. Check that nothing is missing by examining the commit tree. I like gitk --all.
  6. Assuming all is well, dispose of the evidence with git branch --delete life-preserver.
  7. Detach the head again (at least if you're done) git checkout --detach HEAD.
  8. Pretend you're the kind of coder who never misses the little details.

1 If you don't work with git, then this post probably isn't much use to you, but short-short version:

  • In git a "branch" is a name that refers to some remembered state of the project. A project can have a lot of branches, and you can start with one state and make different changes to the project and have both of the remembered as different branches. That is, they can split off like the, well, branches of a tree.* They can also join up which is called a "merge".
  • You can tell git to remember a new state of the project. That's called a making a "commit" and each commit knows about the one(s) it came from, so the software can navigate back in time.
  • A commit can be the target of zero, one, or more branches.
  • You can be "on a branch" meaning that (a) you are working on the state of the project remembered by that branch name and (b) git has a record of which branch you're "on".
  • When you're on a branch and you make a commit, git changes the association of branch name to the new commit. Remember that commits know what came before, so you can still go back, but the name now refers to the new state of the project.
  • If you're not on a branch you are in a "detached head state" which means git knows which version of the project you start with but doesn't know a branch. Any commits you make in this state are nameless because there is no branch name to move forward.
  • * In computerese it's actually a directed acyclic graph (DAG), but that's neither here nor there.

2 Yes, I have a name I use for this. Not that it happens often or anything.

2025-05-14

Getting to do grown-up things

We took the seven year-old to an art museum yesterday so that Mommy and Daddy could see a traveling exhibit focused on a single artist.

And not only was it not a disaster, after a while she disengaged from the iPad and took an active interest in the art. She has a favorite among the pieces we saw.1 She remarked on how interesting it was to see the studies the painter did toward another large canvas.

Victory!

Well, at least a conditional win. We didn't stay to see any of the permanent exhibits because she was clearly done at that point. But still.


1 It's one of my top ten, too!

2025-05-13

Maybe another example of my favorite weakness of LLMs?

We've flown out to stay with my in-laws for the best part of a week. Because reasons.

I brought my work computer in case I need to respond to some crises or just find some time to do useful work rather than burning PTO (which is always in short supply). Unfortunately, my work computer is currently an eight-pound, desktop-replacement beast. For which I really want to bring a spare display (and I have fifteen-inch, USB HD panel for that purpose). With all the weight and volume devoted to work computing I opted for the barest minimum of person hardware: my Raspberry Pi 400, its mouse and, associated cabling to talk to that same panel.

Now, the 400 is a surprisingly good computer for what I paid for it, but it's also quite limited. In particular it has only 4GB of RAM, and while the CPU supports NEON SIMD there is no dedicated graphics processor. It's completely unsuited to running LLMs without add-on hardware. But I got bored, so I decided to try anyway.

I was looking for relatively modern models that will fit in RAM and found Llama3.2:1b (7 months old) and codegemma:2B (9 months old). One conversation and one code support. Nice.

I've been speculating about down-quantizing some slightly bigger models, but in the mean time I started playing around with the baby llama. I didn't want to challenge it with my usual questions for probing the weakness of larger and more powerful models, so I started by just asking it to tell me about the band Rush.

As models do, it then produced a bunch of plausible sounding text. Some of it was incomplete and some was simply wrong. For some reason it thinks Rush won more major award than they did. It seems to have selected exclusively praising text to reproduce (which is fine by me: I like the band and don't find most complaints directed at them to be at all convincing), but there is a non-trivial amount of criticism and none of it is represented or even acknowledged by the answer.

Now, let's be frank: there is a limit to how much factual detail you can expect to be encoded in what is, after all, a somewhat niche cultural subject when you have only two billion parameters available to try to represent a full cross section of English language knowledge. No general purpose model of this size is going to get everything right on a query of that kind. So, I'm not saying that the fact of errors is surprising or even interesting. But I am wondering what we can learn from the nature of the errors.

So let's look more closely at some of the errors. In its "Discography" section the model lists

  • Start with School Days (1974)
  • Caress of Steel (1975)
  • Fly by Night (1975)
  • 2112 (1976)
  • Two-Side Streets (1978)
  • Permanent Waves (1980)
  • Moving Pictures (1981)
  • Hold Your Fire (1987)
  • Presto (1997)
  • Test for Echo (2004)
  • Snakes & Arrows (2007)
  • Clockwork Angels (2012)

which has both omissions and errors.

I haven't the faintest clue what to make of the title mistake for the first album (which was 1974 but was self-titled). It's not the title of any Rush song or album that I'm aware of and a casual search of the web doesn't turn up any song or album by that name at all. The search does turn up a reasonable number of hits on Stanley Clarke's 1976 album School Days and a number of suggestions that people just beginning to explore Mr. Clarke's music should "Start with" that very same album. Interesting, but not terribly enlightening.

Nor do I have any theories concerning which albums are omitted. It doesn't look to me like the list is either the commercial hits or the fan favorites, and beyond that I don't know what patterns to look for.

But I do want to talk about the 1978 entry in the model's list. The proper album title for that year is Hemispheres.The thing that strikes me here is proper title and the substitute text share a conceptual relationship to the number two (two halves; two ways). My (admittedly wild) guess is that we're seeing a side affect of the model's attention system identifying "two" as an important concept to use when trying to infer tokens.

If true that would be interesting, because the attention system is one of the significant ways in which LLMs differ from Markov Chain generators. But it may also be responsible for the models difficulty in know what is a quotation and what is commentary which I've already discussed in the context of scientific papers.

2025-04-27

Why did all the numbers move to the back of my credit card?

One of my credit cards has a feature that baffles me: all the numbers are on the the same side1 of the card.

Now, credit cards present an interesting trade-off problem between security and convenience. Bruce Schneier spends some time on the matter in one of his books. Maybe Liars and Outliers. A certain level of loss to fraud is accepted to ensure that the system is convenient and ubiquitous; and the history of the technology is an epic tale of continual re-tuning of the risks.

In the very early days the system was surprisingly simply, relying on people and paper. Really. By the time I came on the scene, the cards had raised numbers on the front to impress multiple copies of the transaction record on carbon papers. The "chunk, chunk" of the cashier making the impression was the sound track of eighties retail. Later we got smart cards and now touchless payment.2

Hey, Ma! Look at me! I'm living in the future!

And somewhere along there (late '90s) the "card security code" was added to the back. It didn't show up on the mechanical impressions (still in use then in the US, if not in really advanced parts of the world) or in a single-sided xerox of the card, so it made it slightly harder for bad actors to capture enough information to create fraudulent charges. Not really hard, mind you. Just hard enough. That's one of the surprising things about this story.

But now I have this card that has the CSC printed right next to the main card number and expiration date.3 Huh? Is that Okay because merchants are now using billing zip code as an additional (if very weak) authenticator? Or is it something else?


1 The side with the magnetic strip and opposite the conductive pads which I would describe as the back, but it's the fact that they're all on the same side that bothers me.

2 The touchless systems marks one of the first moments I started to feel like technology was leaving me behind. Another unanticipated milestone.

3 It also has the main numbers printed flush on the surface of the card: they're not raised.

2025-04-16

This hard programming problem keeps butting its head into my workflow!

The design (sometimes architecture) of a non-trivial program's source code and build is replete with problems. Multiple books are written on the matter every year. The code goes into many different files which are stashed in multiple folders. If you're paying attention the folders represent elements of a modular build. Mostly.

Architecture level problems come in lots of kinds, but the ones I want to focus on today are excessive interconnectedness of logical units, and excessive interconnectedness of build-time units. The former makes the code hard to reason about because when I'm look at a bit of code here I may not know if some bit of code in a different folder is going to reach in and change something when I least expect it. The latter driving up build times. Both can make changes that looked initially confined to a single file cause adjustments in many other files.

So now we have two things to keep in mind:

  1. The organization of files in the file-system should parallel the build organization.
  2. File should know as little as possible about other code units and especially about units in other build modules.

So, when one bit of code needs to know a lot about another bit of code it goes nearby. Maybe in the same file, and almost certainly in the same build module. Fine.

Now consider the "extract function" feature of your programming environment.1 It's a shining example of good tooling as it encourages and helps with refactoring. But it usually crashes me right out of the zone. Because if I'm creating a new "piece" of code and I have to find it a home. And that can be hard.

Some cases are easy.

  1. If it's coming out of object's instance function (AKA method), and it needs to access instance state then is must live in the object.
    1. If it does not respect the class invariants then it must be private. Except that if it is virtual then you may want it to be protected2
    2. Otherwise it can be public. But start with it private until you know of a use case: every API you expose is one you have to maintain.
  2. If it is reaching into another class's object, then consider moving it to that class.

But after that it starts getting tricky. I mean, it can be a free function or a class static, but where should it live?!? If it is highly specific, perhaps keep it in the file it's in (and perhaps even private), just with a name now. If it represents a specific behavior within the domain modeled by this module, make it it accessible throughout the module (new files, class static, who knows?). If it's very general purpose, first look to see if you missed it in the standard library or any frameworks you're using, and if not put it in your project wide utility code. Maybe?

In any case, having to make these decisions often knocks me out of my flow. And since it doesn't have a name that I know of I haven't been able to google advice.


1 Obviously you don't actually need a tool for this. I got on fine with cut-n-paste for decades. Still do when certain IDEs (no names, Qt Creator you .... you ... wonderful program) just refuse to have it enabled for some mysterious reason. But I will sure as heck use the tool if present.

2 I'm largely using the C++ nomenclature here because that's the sea I swim in daily, but I think the considerations apply more broadly.

JSON Update

A followup to a recent rant.

First an admission. At least one thing I complained about was not a feature of JSON per se but of the library (nlohman::json AKA JSON for Modern C++) that we're using. In particular the behavior of serializing a floating point IEEE-754 special to null, and then throwing when trying to deserialize a null into a floating-point variable is library specific. And they stand by it. Grrrr!

Second, by defining a strong-typing wrapper I was able to (de)serialize those values from-and-to strings. I even provided multiple acceptable spellings on the deserialize path. Then by writing explicit to_json and from_json routines for my objects (rather than relying on the handy-dandy macros in the library) I was able to apply the strong-types only at the point of (de)serialization reducing what initially looked like a major intrusion into the code. Yeah.

It's not a complete win, however, because I have a std::variant in the code-base. The usual advice for deserialize a variant with the library is to detect the json-type of the value1 and use that to know which member type to set. Only the number can give rise to a string value. So I had to explicitly (de)serialize the current-type, too. Bletch!

Long story short. We're going ahead with this and I may replace ny custom interchange format after all. Just because other users will stand a better chance of decoding the JSON.

But the lack of infinity and not-a-number is still a bug and still renders the format poorly suited for use in numeric computing.


1 JSON for Modern C++ uses a domain-model object as an intermediary, so this is relatively easy.

2025-04-11

No really. Should I use that library function that I shouldn't use?

The world would make more sense had it happened this way:

Shoot in grainy black and white.

Scene: a sprawling cellar lit by torches and candelabras. Brick archways lead in multiple direction. All the spaces we see are filled with abandoned experiments, strange equipment, and untidy storage. Some of the bits spark. Others bubble. Here and there tables are covered with books, notes, and abandoned comestibles.

Richiestein: "Igor, the world degenerates. Many programmers loose their edge, hiding every day behind strong guarantees from safe environments.1 We must bring LIFE back to the demons and dragons of yesteryear!"

Igor: "Yes Master"

Richiestein: "I needs a way, Igor; a way to trap programmers. To make it easy for them to err; easy to use memory they do not intend."

Igor: "Oh, master. Igor knows. Igor is sure master. You must use sentinel terminated strings, master!"

Richiestein: "Will that help?"

Igor: "Oh yes, master!"

Richiestein: "Will they not see that it is a bad design?"

Igor: "Master must tell them it is for simplicity. And show them correct code saying that it is elegant and basic. Maybe write a book?"

Richiestein: "I suppose."

Richiestein: "But can I get them to overrun their buffers that way?

Igor: "Oh yes master, let Igor show you!"

Igor hurries to a crowded corner of the room and digs frantically through trunks and shelves before returning triumphantly to show the good doctor what he has found.

Igor: "See here, master, I have gets!"

The doctor squints at the jagged and rusty artifact before looking sharply at his assistant.

Richiestein: "Would anyone accept something so obviously cursed as this? Surely not?"

Igor: "They will, Master! They will! You need only put it in the Standard Library master!"2

Richiestein: "Hmmm. We'll try it. But we also need something more subtle. Something that looks like it would work. Maybe something that only brings disaster occasionally. Do we have that, too?"

Igor: "We do, Master!"

Igor roots through another corner of the cellar before bringing a many-geared contraption of only slightly tarnished brass.

Igor: "See master: snprintf. A powerful tool, but if it runs out of space no sentinel is placed, making their string a trap!"

Richiestein: "Oh, that one is better. It almost works."

Exuant. Laughing.

Sadly it's so much more likely there were making only what they really, really needed (and would fit in the very small machine they had on-hand) and intended to come back and fix it later. That's the way these things usually happen.

I do know the danger

The standard library function snprintf3 guarantees to not overwrite the buffer (assuming of course that the programmer passed the right length). Which looks so promising, but they don't guarantee to write a terminal '\0'. They only add that if there is room left in the buffer.4

So, if you use snprintf to fill in a buffer and run out of room without noticing (always get the young man's name and address check the return value!), and are later so incautious as to try to measure the length (with strlen) there'll be no terminator and you read off into random memory until you encounter a zero byte. Or a seg-fault. Or nasal demons, of course.

And it can get even worse.5 Say you think you want to perform an in-place tokenization with strtok.6 Now you can be writing past the end of the buffer.

And, yet, I still use the function

And yet, when I exhibited my nifty "safe" string builder for c, I used vsnprintf. Twice. Even though it's a trap. What gives?

grug quite satisfied when complexity demon trapped properly in crystal, is best feeling to trap mortal enemy!

The answer, of course, is that by using it very carefully in just one place I intend to relieve future programmers (including my future self) from the need of using it repeatedly elsewhere.

I also put prominent comments in, not so much to convince future readers as to make sure that I was paying attention, though they should serve as a alert for future readers too. They'll have reason to read the docs with "how long" and null termination in mind. I hope.

When is it a good idea, again?

So ... it's sometimes okay to mess with the magic lamp? Really?

Honestly, that function is dangerous, and the comments don't fix it. If I were developing a non-trivial code in c as a green field project, I'd give real consideration to not using big parts of the c standard library starting with string.h. But the use case that drove me to thinking about it in the first place was maintenance on a statically-linked launcher for a C++ project.7


1 Okay, so strong guarantees were few and far between in the early 1970s. but, this is Hollywood! Work with me here.

2 And don't call ne Shirley!

3 Or its variadac sibling vsnprintf.

4 Cue Admiral Ackbar.

5 As far as the standard is concerned this is exactly as bad as before—undefined behavior—but from a practical point of view it is more immediately destructive.

6 You probably don't, really. For several reasons, but take it arguendo.

7 It is harder to write a C++ program you know will "just work" on a target machine, then to do the same with a C program. Especially on windows. So we have a little program written in c that should "just work". It is suppose to detect the environment, set up the prerequisites for the big C++ code, produce a clear error message if it can't, and then launch the main app. It does a lot of string manipulation. But it's been there a long time and it works; there is no way I could justify switching string libraries wholesale.

2025-03-28

Actually, if I had it to do over again I still wouldn't use JSON

When I started with my current employer they asked me to do a "story-board version" of a tool they were pitching to a customer by way of a warm up project. When the customer picked it up they kept me on as the lead designer and coder.1 This was the first time I'd ever been put in charge of the basic decision making for a shared effort or of something intended as "a product", so unsurprisingly, I made some mistakes. There are facts about the codebase that I am not proud of.2 In a couple of cases I cringe inside when I have to explain them to newcomers.

Interchange format

Plain text is the way

We run a significant amount of computation in separate OS processes.3 Honestly, to an old Unix hand like me that feels completely natural and it's not much harder than threads except for one thing: you can't just hand the worker a binary config object, but have to invoke some kind of interprocess communication. I choose to pass a serialied config object.

Now, a wise developer would have picked an existing serialization library. Obviously. But I didn't understand any of the existing options and saw only obstacles, so I rolled my own.4

After all, I had very simple needs, so I had it done in an afternoon. Tests included. Then time passed, requirements changed, and the format grew more elaborate. The code got longer and spawed more templates and more specializations. Writing the tests got harder. We began using it for a save-current-configuration format. I learned how to use the type_traits header badly. Then better. That heloped with the tests, but only a little. Still more requirements loomed and the code became a no-go area for everyone but myself and my best junior dev (now really a mid-career dev).

For several years I've been telling people that if I had it to do over again we'd be using JSON.

Little did I know.

Careful what you wish for

A couple of months ago we found out that a subset of the tool is being used as a component in a multi-step chain by a different division at the customer's organization. Last month we got funding to support that effort. This month they asked us to replace the custom interchange format with JSON.

We picked a library and went to town. Progress is being made, but it's not quite all sunshine and butterflies.

Why JSON is a really bad choice for this application

The thing to understand up-front is that this is an application in physical modeling. We compute an approximation to the behavior of the real world in a highly specialized domain and we do it fast and with a friendly front end. We don't need "just so" fidelity, but it does need to be reality based.

That means numbers. Almost always in floating point representations.

Nearly all modern platforms support IEEE-754 floating point numbers, which I have mentioned before. A feature of that standard is the ability to represent several special cases without demanding extra space. The available special cases go by three names: "infinity", "negative infinity", and "not-a-number" (AKA NaN). The infnities are generated when you do things like divide a finite value by zero or take the logarithm of zero (which you'd expect from real math) but also when you do things "close" to the real ones like dividing a big enough number by a small enough one. NaNs come out of weird operations like dividing zero by zero, or taking the arcsine of two.

Programmers have limited control over the users and what kind of input they generate so these kind of values pop up from time to time, and we have to decide how to deal with them. It's one of the annoyances of the job. Sometimes you want to do one thing with infinities something else with NaNs.

My custom serialization format handles those values gracefully, but JSON does not:5 it serializes all of them as null.

Not only is this ambiguous, but readers are allowed to simply fail when they encounter null where they were expecting a number. In particular, the library we'd started using throws an exception in this case. Really. This may or may not be reasonable in some domains but it is clearly an error in scientific computing.

Now what?

The thing is, it's not going to get fixed. You'd have to add new tokens to the grammar which would break many (probably billions) of deploy instances. When one of the selling points of the format has been stability. Total nonstarter.

I'd been planning on deprecating the old interchange format: stop generating it (maybe even remove the generation code) but keep the reading code around for a while. Becuase of the save files, naturally.

Now I'm not sure I want to switch. Maybe I just want to support JSON as an alternative. And support extra code for the foreseeable future. Sigh. Or I can live with not being able to pass well defined specials from component to component. Bigger sigh.


1 Actually, at first (and from time to time since then) I was the only person on the project. But still.

2 And some I am proud of. And a couple I'm ambivalent about because they may be necessary, but they are complex and hard for newbs to wrap their heads around. Well, they were hard to figure out in the first place, too. At least I wrote design documents for the features, so I have an answer when they ask "What were you thinking?"

3 A legacy library we rely on for one of the main computationally bound features of the application is single-threaded and not re-entrant; to let our customers take advantage of their beefy, many-cored, analytic workstations we have to get the OS to isolate instances.

4 Seems to be a habit, doesn't it? Though, in my defnense the logger has been a success. It has ridden out expanding needs with minimal maintenance and without growing out of hand.

5 Interestingly, JSON is a strict subset of JavaScript which does support the specials. To judge from the few things I've seen from the creator, it's likely they were sacrificed on the alter of simplicity. Perhaps a lamb too far, that.

2025-03-02

Yeah, why doesn't she?

One of my habits1 is singing along to whatver music is playing in the car. Today I was belting out The Warning's2 When I'm Alone while driving my daughter to gymnastics. She's usually heads-down over her tablet in the car, so I have to make my own entertainment.

As the song wrapped up she asked me what the singer meant by the "hole in [her] soul".

These are the kinds of moments that make parenting a minefield, but also so worth it.

The song is about maintaining outward calm in the face of inner turmoil in a inescapable situation,3 but I don't think4 my seven year old really has a reference for that. So I tried to explain that in this case "soul" means how you're really feeling inside and told her somthing like "The whole song is about making the people around you think you're OK, while you're really, really sad on the inside". That seemed to satisfy her at first.

Then, after a few moments of quiet, she asked me "Why doesn't she just tell them?"

Stonkered for the second time in as many minutes. Alas, I suppose that she will understand some day.


1 Good? Bad? Fine when I'm alone but annoying otherwise?

2 I'll try to keep the pathetic fan-boying to a minimum here, but if have any interest in rock and don't know this band it's worth a little of your time to check them out. In addition to the song noted above consider Choke, Evolve, Disciple, or Hell you call a Dream as introducers.

3 Written by a homesick fifteen year-old and a better expression of that state of being than I've managed in more than fifty years of being at least a little messed up. Go figure.

4 Hard to know with absolute certainty: once you send them to school you don't know everything about what's going on in their life unless someone tells you. I feel pretty confident that either the child or a teacher would have clued us in but there is still room, however small, for doubt.

2025-02-28

Header-implementation duality?

While, in principle,1 C++20 marks the beginning of the end of our long, collective, inclusion nightmare the painful truth is that many C++ developers are going to be dealing with header files for years to come. And I've heard of no plans to bring a modules system to plain C at all.

But at least we have decades of experience from which to derive best practices and better tools to automate or enforce those practices. Right?

Some of the best practices:

  1. Every header should have include guards.2
  2. Don't #include files unless you need declarations or definitions found therein. Where possible prefer forward declarations or forward declaring headers (like iosfwd).
  3. Explicitly include headers to cover everything you need. Do not rely on transitive includes.

None of this is particularly controversial (leaving out the holy war over how to protect files from multiple inclusion), but I find that I have a minor interpretational difference with include-what-you-use (AKA IWYU: one of the leading tools for automating these rules). You see IWYU asks me to include in my implementation file things I already included in my the associated header file. Now, that is a strict interpretation of rule (3), so why does it bother me? Apparently I think of the header and implementation as two parts of a single logical unit rather than two independent things. I mean, those things should be maintained together so the danger of relying on the transitive inclusion is mitigated.

Of course, if I was implementing the tool I'd at the very least start with the current behavior. Simply because it's going to be very hard for the tool to know that this header really and truly does go with that implementation. I mean usual if they have the same stem that's a good clue, and in project where headers and implementations can sit in the same directories that is also helpful. But some layouts separate the interface files, so then what?


1 By now a non-trivial fraction of C++ coders are able to use the 2020 standard, but that doesn't mean that they are able to use modules. Support has been pretty slow even in compilers and build tools maintainers are stuggling with some real problems inherent in the pure flexibility of the module standard in C++.

2 Yes, I know, in the Windows world many people strongly advocate for #pragma once instead, and avoiding the possibility of name collisions is a big advantage in my book. But it remains non-standard which is why some major style guides and the core guideline still insist on classic approach.

2025-02-16

I think the Stones left that bit out

I had occasion to exchange a moment of shared culture with a cashier over the Rolling Stones' You can't always get what you want, and my daugter wanted to know if we'd learned it at school like she did

So, later, I played her the song. And she wanted to know when they would sing the "you get what you get and don't throw a fit" part.

2025-02-11

Irony writ large

This is a rant that's been bugging me for a long time and has just recently boiled over.

My work computer runs Windows. I wouldn't have chosen that, but there are good reasons for it. I'm told. Whatever. I'm a technology pro, and I take my daily dose of copium so I can deal. But there is a fact of life about my working environment that amuses and frustrates me in equal measure: certain applications are just really bad at window placement on returning from sleep mode.

Now, this is actually a hard problem for three reasons. First, I work for a contracting firm so I actually sit at three sites (home, company office, and client facility) and due to hot-desking at the client site maybe seven different desks.1 Second, I'm a programmer and I prefer to work with multiple monitors. Five of those seats supply an external monitor to pair with my laptop's screen, one of them gives my two externals, and then there is the "sad" seat. Finally, I'm an older programmer and modern displays have lots-n-lots (tm) of itty-bitty-teeny-weeny pixels, so I use auto scaling to make everything easier to read.

The good news is that modern external display buses give the OS enough information that it can "remember" setups and restore all your settings. Yeah! But still, my machine is repeatedly going to sleep with one setup and waking up to find itself connected to another. How operating systems applications should respond to a changing display environment like that is far from trivial, but I feel like things have finally settled out a bit in recent years.

Still not every application is good at this, and my work environment features a few regular offenders.

In third place is that dancing-to-a-different-drummer perennial, Emacs. Particularly Emacs running from a WSL Linux (running from msys it seems to be fine). Every time you wake the computer at a new display location you can expect WSL Emacs windows to migrate to the virtual desktop that was active when you put the machine to sleep. And sometime to unnecessarily switch to the main display as well. Sigh.

Second place goes to Microsoft Teams which seems to jump virtual desktops on an intermittent basis. Often when you've missed an appointment. Maybe? But it also occasional does that when you're just working normally, so it's not clear that it relates to sleeping or to changing display environments at all.

And the biggest irony is Microsoft Edge. Which just can't seem to recall where it was or how big it was. More or less ever. At all. I mean, I re-size and reposition this POS a dozen times a day. What the #&|!, Microsoft?!? Honestly, if a major Microsoft product team can't figure out Microsoft's own interfaces for this stuff why would you expect anyone to get it right? And what does it say that many other application do a pretty good job none-the-less?


1 At least it's just one physical computer, which limits the issue of file synchronization to the usual domains of software repositories and shared drives.

2025-02-08

Deepseek-R1 and a few more thoughts on LLMs

Not so long ago Deepseek-R1 dropped to much wittering and gnashing of teeth.

Im my desultory way, I eventually got around to downloading several of the medium scale models to run locally. The 70B parameter model achieves a token or two a second on my framework laptop which is good enough for testing even if I wouldn't try to use it in a productive work flow. My comments here are based mostly on local interactions with that model, though I have poked at it's slightly small cousins. I haven't interacted with the 600B+ parameter variant at all.

My methodology (if you can will accept such a formal sounding word) continues to be pretty ad hoc: I repeatedly fire up the model in ollama, present the model with a single prompt or a short series of related prompts, read the answers then shut down the model before going onto the next prompt(set). As before my prompts are mostly intended to probe the edges of what I expect these models to do well on by choosing subject where I expect one of the following to apply:

  • The topic is sparesly represented on the internet.
  • The topic is a combination of two or more well covered ideas, but the combination is sparsely represented.
  • The topic is a highly specific example taken from a very broad field.
I have added another movie related query that I expect models to do well on. I ask them to explain how the rooftop confrontation in Blade Runner makes Roy Batty a sympathetic and human figure; this is ground that many a fawning essay has covered in detail, and large enough models often write very well on the subject.

But the idea is mostly to stress the generation process by one way or anther.

Something I'd noticed even before I began playing with deepseek, but hadn't mentioned yet is that these models seem to be very bad at keeping track or what was in specific sources (say a single paper I asked about) and what they only found in the halo of secondary-source words written about the thing I asked after. This shows up consistently in how they handle the questions about physics paper with most models drawing in material that was probably written to explain the essays to less technical audiences and attributing it to the paper itself.

Thoughts on Deepseek-R1

At least in the models I've been working with it's good (even for the size), but it's not great. It's really, pointedly not great.

It has a reasoning model, which gets it partly over a hurdle I saw in my earlier post. It was able to actually do something with the weight and volume limits I suggested for my day-hike survival kit. It can math. Yeah!

Mind you, the inputs it was using for the math were still values it picked out of it's training set without any obvious understanding of the context. Like several other models, it keeps insisting that the first-aid kit I should carry will be just a few ounces, which isn't a bad number for something to put in your purse or satchel, but the one I actually pack for a hike is the best part of a pound because it is meant to cover a wider and more serious set of problems.

It's writing style is clunky and repetitive, and it takes great pains to show off the presence of that reasoning model, often sounding like a under-prepared student trying desperately to stretch a cursory understanding into an assigned page target.1 This stands in contrast to the slow but notable progress I've been seeing as new models come out from the established players. Lamma3.3, for instance, produces much more fluent and readable text for a lot of prompts.

Is the alarmed response justified

Well, OpenAI's annoyance is mostly amusing. To the extent that it is not amusing it's a warning that [ spunky up-n-comers | tendrils of evil, acquisitive foreign powers ] don't always play be the rules that [ their staid elders | the defenders truth freedom and unrestrained capitalism ] proclaim.2

Leaving that aside, the claimed cost and speed of this work is impressive and should probably worry the big players. I mean they still have a better product, but the price-performance situation means that the new guy probably looks really attractive for a lot of applications.


1 Particularly annoying with the slow production speed. I keep wanting to shout "You said that already, think of something new or give it a rest!".

2 As a minor irony, I'll note that the young United States was criticized by various European powers for failing to respect even the weak international intellectual property regime then extant. This is a thing with historical precedents even if we don't like it.

2025-01-25

Little Surprises #9: too helpful edition

In Qt5, this #include <QString> #include <algorithm> #include <vector> int main() { std::vector<int> v{0, 1, 1, 2, 3, 5, 8}; QString qs("target"); // Search the vector for the string std::find(v.begin(), v.end(), qs); } will build (compile and link). There are very few circumstances where you mean what it will actually do (in this case it's a very expensive no-op), but it is valid code.

This works because of a chain of "helpful" features:

  • You can construct a QChar from an int: QChar(int rc) noexcept
  • .
  • There is a equality test between QChar and QString: bool operator==(QChar lhs, const QString &rhs) noexcept
  • .
  • The find template uses operator== between the contents of the vector and the target.

Each of the behaviors is desireable in isolation, so we don't want to remove any of these options entirely. What causes the surprise is the implicit nature of the type conversion.1 Core guidelines C46 and C164 discuss the uses of explicit annotation to prevent this kind of thing.

BTW: the above code will not compile in QT6 which is a small but significant improvement.


1 Shout out to all the strong typing fans out there.

2025-01-21

When I grow up I want to be...

... organized, detail-oriented, and thorough.

I imagine that will be happening ::checks watch:: any day now. Right? Right?!?

2025-01-11

Dates are important

If you're putting a technical article on the web please, please make sure it has an easy to find date of production assocaiated with it. Seriously, technical stuff changes and sometimes changes fast. I need to know if your advice is a few years old because that might be enough to make it comprehensively wrong.

That is all.