I tried doing the z-buffer post-process fog trick in hardware, but it ended up being slower there and the alpha issue existed there too.
String comparisons. Here's a trick for you.
An awful lot of these come from looking up commands and cvars in their respective lists. Vanilla Quake is awful for this. If something needs to set a cvar directly (through Cmd_ExecuteString rather than through Cvar_Set) it first does a linear walk through the entire list of commands, finds that it's not there, then does a linear walk through the entire list of aliases, finds that it's not there, then does a linear walk through the entire list of cvars (and hopefully finds it). If you've added extra cvars or commands yourself it only adds to it.
When you've finished registering all cvars and adding all commands (somewhere at the end of Host_Init is good) build another list of all cvars, commands and aliases. This list should contain a struct, one member is the name, another member is a pointer to the original. Sort that list by name. When a new alias gets created or an existing one is removed destory, rebuild, resort. In Cmd_ExecuteString do a binary search instead. Retrieve the pointer to the original and push that through to the correct handling function.
Cvar_Command does two linear walks through the list - one in Cvar_FindVar and one in Cvar_Set. You can remove the second - even if you don't do the binary search thing - by making a Cvar_SetDirect that takes a pointer to a cvar_t as it's first arg instead of a string. If you do the binary search then both go away. Cvar_SetDirect is also a good replacement for anywhere else in engine code that issues a Cvar_Set.
The list for binary searching is also great to use for autocomplete partial matching, so you get a double win from this.
Like the fifth day of playing 24-hour Scrabble when you don't want to use any letters because each one means a world to you because you're so deranged.