Embracing My Inner Blub Programmer
I always feel a little bit guilty about Lisp. I am supposed to appreciate Lisp. It’s a fine wine and as a member of respectable society I should hold it in high esteem. But, I’m not quite there. I’m not opposed to Lisp, per se, I just never end up using it for very much.
This week is no exception. I am not writing any Lisp this week. Quite the opposite - for the first time in a while, I am writing big chunks of C++.
The Bad
It’s very obvious when you write C++ after using languages like Python or JavaScript for a while that C++ has problems. Ancient problems that I remember Java fixing back in the 90’s. The problems with C++ are so glaring, I run into programmers who are surprised that such things are even possible.
-
When you write a new class you have to split logic into
.h
and.cpp
files, half repeating yourself. -
If you accidentally access of the end of an array, your program will simply crash with no error message reported.
-
If you forget to initialize a member variable, your program will often set those variables to zero, but sometimes it will just fill them with quasi random data.
-
You either have to manually free memory that you allocate, or use solutions like
unique_ptr
that are more complicated than any reasonable language offers.
The Not So Bad
At least C++ is getting better. The last time I wrote a lot of C++ was
working for Google, 2005-2009, and I don’t think we had even fully
adopted C++98. So I’m still a decade behind the times, learning the
various C++11 things. unique_ptr
and shared_ptr
are good; they let you
mostly avoid remembering to delete your own pointers. mutex
and
condition_variable
are good as well. And in general with GitHub, cmake
and
meson
there are a decent amount of libraries out there that do
common things. Not like Python or JavaScript where you have solutions
for everything, but it’s a lot better than nothing.
The Actually Good
So why C++ at all? Well, this is my favorite API of the week:
cublasCgemm3mStridedBatched(cublasHandle_t handle,
cublasOperation_t transA, cublasOperation_t transB,
int M, int N, int K,
const T* alpha,
const T* A, int ldA, int strideA,
const T* B, int ldB, int strideB,
const T* beta,
T* C, int ldC, int strideC,
int batchCount)
It’s a shortcut to run code equivalent to these nested loops, on a GPU…
for (int p = 0; p < batchCount; ++p) {
for (int m = 0; m < M; ++m) {
for (int n = 0; n < N; ++n) {
T c_mnp = 0;
for (int k = 0; k < K, ++k)
c_mnp += A[m + k*ldA + p*strideA] * B[k + n*ldB + p*strideB];
C[m + n*ldC + p*strideC] =
(*alpha)*c_mnp + (*beta)*C[m + n*ldC + p*strideC];
}
}
}
…but you can also conjugate-transpose A
before running this matrix
operation. I was pretty excited, I literally wanted a matrix to be
conjugated and transposed, and I thought this was the sort of abstract
mathematical fun that I had given up long ago with the transition to
computer science.
See this Nvidia blog post if you’re curious for more detail, but basically this code is multiplying sequences of matrices that you’ve stored in some regular way in memory.
Blub
I feel like this is why I always end up developing in Blub nowadays. I’m never choosing programming languages based on the most powerful generalist language. I always have some narrow task - from GPU matrix operations to making a responsive website where you can drag stuff around - and having a library that’s well suited to the task ends up being more important to me than having macros.
It’s not the same but at least I sort of get to use Lisp macros while
developing in Emacs. I’ll take another look at VS Code once they
implement C-x-(
, C-x-)
, and C-x-e
.