Full Width [alt+shift+f] Shortcuts [alt+shift+k]
Sign Up [alt+shift+s] Log In [alt+shift+l]
42

A metastasis in America

from Don Melton [alt+shift+b] in programming

America is sick. And I don’t just mean with COVID-19. The bad news is that removing the ugly, orange tumor in the White House next week will not be enough to affect a cure. The malignancy has spread. It didn’t even start with the presidency. We’ve been brewing and self-dosing a toxic carcinogen for centuries. White supremacy is, of course, the not-so-secret ingredient. While responsibility for this has changed hands over the years, the modern Republican Party is obviously ensuring racism persists and even flourishes here in America. And for nothing more than electoral advantage. The GOP cannot maintain power if everyone is allowed to vote. We all know this. But even disenfranchising non-white, non-native and otherwise different people is not enough for them now. And their new strategy only starts by overturning election results they don’t like, achieved by lying to their lesser number of voters that they’ve been cheated. The violent insurrectionists at the Capitol last week weren’t really interested in elections, fair or otherwise. They wanted to install a dictator. And many in the GOP—federal, state and local officials as well as their voters—were encouraging that. It’s clear that Republicanism is no longer compatible with democracy. So we have a big problem. The reason so many of our fellow citizens are angry and demanding autocracy is more complicated than simple political disagreements. There’s something elemental going on here. An embrace of conspiracy theories, ignorance and grievance. And on a scale I’ve never seen. I’ll be honest that I have no idea how to cure this disease. I’m not even sure where to begin. But our nation needs some powerful medicine. Or the body will die.
15th Jan 2021

Stay updated

Get a weekly newsletter with the top 5 articles worth reading every week.

More from Don Melton

Sorry, we’re closed

For reasons that will soon become obvious, I’m shutting the doors on this website. Everything will remain online for now, but I don’t plan on returning to write anything new here. Not that I’ve added any content in almost two years anyway. I still have a passion for making observations, telling stories and recording my thoughts as they happen. I’ll just be doing it elsewhere. Thank you for reading.

19th Jun 2023 • 103 votes
Happy twentieth to Safari and WebKit

Safari and WebKit aren’t teenagers anymore. I just want to make note of that. To quote a previous post: On June 25, 2001, I arrived at Apple Computer to lead the effort in building a new Web browser. It was also Ken Kocienda’s first day on the job, both at Apple and on that same project with me. For that reason, Ken and I have always considered our start date to be when Safari and WebKit were born. Not any other position on the calendar. Only June 25, 2001. We were there. We should know. That was 20 years ago today. Twenty years! Of course, it’s been over nine years since I retired from Apple. Obviously, I’m not a teenager anymore either. But I still remember that first day clearly. So, happy birthday to Safari and WebKit and the team now tasked with their adult supervision.

25th Jun 2021 • 57 votes
That bleeping kerfuffle

After I posted that link to my latest podcast with Rene Ritchie, several folks alerted me via Twitter that all my colorful metaphors had been “bleeped” on the audio. I didn’t realize that because I hadn’t listened to the recording myself. And I don’t normally listen to my own podcasts because… that’s just sort of creepy, isn’t it? Obviously, that means I don’t mix the audio either. I don’t do that because 1) I don’t have relevant experience at it, 2) I’m really lazy and 3) fine folks elsewhere do all the hard work for me. My apologies if you didn’t get the whole “Melton” experience you were expecting. Rene tells me that episode was an accident and our next podcast won’t be censored. “Let Melton be Melton,” as he likes to say. Plus, we might just release an explicit version of the current show. Has everyone calmed the fuck down now?1 OK, here’s the thing—I was not upset at all about being censored. The show might be called “Melton” but that’s only because 1) Rene Ritchie is a generous man, 2) I’m vain and 3) we couldn’t think of a better name after we recorded the first episode. I consider the whole enterprise as something Rene and I do together. It’s our show. Not my show. If anything, I’m the co-host. This is exactly why I call Rene (and Kelly Guimont for our “Westworld” podcast) “boss.” I’m not trying to be funny, ironic or insult them. I’m reminding myself who really is in charge. And who does all the hard work. Seriously, I just talk into a microphone, folks. And it’s a microphone that Rene gave me! A really nice Røde Podcaster model, too. Talking is easy and I continue to be amazed that anyone out there cares about listening to what I have to say. I’m honored that all these nice people enable me to broadcast my various musings, opinions and rants. So if Rene and anyone else at iMore—or Jason Snell and anyone else at The Incomparable—decide to censor my many and frequent vulgarities, it’s their call. They’re the publishers. And being censored won’t damage my “brand”—whatever the hell that means. (Actually, it scares me thinking about what that means.) Yes, words matter. Exact words even. But the truth is that some people—whether they admit being offended or not—have difficulty listening to vulgarities. Especially at the pace I spew them. A friend of mine told me he’s sad that he can’t listen to my podcasts in his car anymore now that he has kids. I get it. There are valid reasons to hit the buzzer. As anyone who’s adventurous enough to follow me on Twitter knows, I’m saltier than most sailors. I don’t plan on changing that there or on this website. But if someone needs to filter me a bit elsewhere, I’m fine with that. A tired catchphrase which really needs retirement. I should know. ↩

18th Jun 2017 • 50 votes

More in programming

An Update on Orion for Linux and Windows

Kagi is ending development of Orion for Linux and Windows and open-sourcing both so the community can carry them forward. Our small team will now focus fully on making Orion for macOS and iOS faster, more stable, and more capable.

11 hours ago • 1 votes
Clip of me singing Despard in Ruddigore in 2013

A clip of me singing a funny song from Gilbert and Sullivan’s Ruddigore back in 2013

17 hours ago • 1 votes
How and Why fork() Uses Copy-on-Write

In this video, we look at why fork() needs copy-on-write, how it works inside the kernel, and a memory usage problem that Instagram encountered with Python.

23 hours ago • 1 votes
What we lost when we lost comments

Comments require commitment, but they’re worth it.

yesterday • 1 votes
Two-Stack Sliding-Window Aggregation

An aggregation is some kind of summary of a set of data. This can be the sum, length, minimum, etc. It is quite common to want to calculate such a summary repeatedly, e.g. “the maximum noise level in dB for the past 30 seconds” for a nuisance detector. In such a case we say there is a sliding window over our data, and we want to aggregate over our window. If our aggregation is a binary operator with an inverse, like integer sums, there is a very easy solution using a double-ended queue: from collections import deque class SlidingWindowSum: def __init__(self): self.sum = 0 self.elems = deque() def push(self, x): self.sum += x self.elems.append(x) def pop(self): self.sum -= self.elems.popleft() def eval(self): return self.sum But what if our operator has no inverse? This is actually the case for most interesting summaries such as minimum, quantile, approximate unique count (for example using HyperLogLog), etc. In fact, even something as simple as a floating-point sum suffers from the fact that floating-point addition is not invertible. For example, if you ever have a NaN in your input data with the above naive algorithm your sum will forever remain NaN, even long after the bad value has left your window. Six years ago I came up with an algorithm for maintaining just the minimum/maximum in a sliding window and posted it to cs.stackexchange. I now consider this algorithm pointless, because it turns out there is a simple and efficient algorithm that solves this problem for a very wide class of aggregations. I’m writing this blog post to spread the word, because I feel it should be more widely known. Folklore I came across this algorithm while reading a far more advanced paper, Low-Latency Sliding-Window Aggregation in Worst-Case Constant Time by Tangwongsan et al. Why is this paper titled low-latency? Because it does the same as what I’m about to describe, but in O(1) time for each step. However, in it they also described a “two-stack” algorithm, which does it in amortized O(1), and is far, far simpler. Amortized O(1) means that across many operations the total amount of work per element is constant, but an individual operation can take much longer. This is almost always fine, unless you absolutely need a low upper bound on latency. Funnily enough that paper attributes this algorithm to “adamax” from a 2011 Stack Overflow post. They in turn credit a 2001 lecture note by D. Sleator for the inspiration. However, this lecture note does not describe a sliding window aggregate, it describes the classical two-stack algorithm for implementing a FIFO queue and does amortized analysis on it. Ultimately I would not be surprised to find that this algorithm was already described in an obscure paper from the 1970s, seeing how simple and brilliant it is. Two stacks Like the authors of the paper, I will generalize the two-stack algorithm to arbitrary associative aggregation functions. By abstracting the aggregation as a set of functions, empty(), unit(x), combine(x, y) and finalize(x), you can describe many possible aggregations, for example a mean: empty = lambda: (0, 0) unit = lambda x: (x, 1) combine = lambda x, y: (x[0] + y[0], x[1] + y[1]) finalize = lambda x: x[0] / x[1] if x[1] else None I’d like to note here that these functions have the following signatures: fn empty() -> Agg; fn unit(x: Value) -> Agg; fn combine(x: Agg, y: Agg) -> Agg; fn finalize(x: Agg) -> Out; I’m making a distinction here between Value, Agg and Out because while they seem superficially similar for something like an integer sum, for an approximate unique count on strings you would have (Value, Agg, Out) = (String, HyperLogLogSketch, u64), three wildly different types. Without further ado, the algorithm: class TwoStackAgg: def __init__(self): self.values = [] self.values_agg = empty() self.cum_aggs = [] def push(self, x): self.values.append(x) self.values_agg = combine(self.values_agg, unit(x)) def pop(self): if not self.cum_aggs: cum_agg = empty() while self.values: cum_agg = combine(unit(self.values.pop()), cum_agg) self.cum_aggs.append(cum_agg) self.values_agg = empty() self.cum_aggs.pop() def eval(self): return finalize( combine(self.cum_aggs[-1], self.values_agg) if self.cum_aggs else self.values_agg ) That’s it, the entire algorithm. There’s two stacks (values and cum_aggs) and one more aggregate, values_agg. At any point in time values_agg holds the aggregate of values, and cum_aggs contains the cumulative aggregates of all values in our window that aren’t in values, in reverse order. From this we can get the aggregate over our entire window in constant time by by combining the last value of cum_aggs with values_agg. The neat part is that (assuming w is our window size) every wth operation we drain all of values and maintain a running aggregate while pushing the partial cumulative aggregates onto cum_aggs. This is what makes it amortized O(1), doing O(w) internal operations every wth pop bounds the total amount of work per element to O(1), even though a singular operation might not be constant time. I think this is best visualized. Suppose we sum [1, 2, ..., 10] with a fixed-size sliding window of four elements, then the state on each eval() call would look like this (values_agg not shown as it is simply the aggregate of the values): cum_aggs values out [] [] = 0 [] [1] = 1 [] [1, 2] = 1 + 2 [] [1, 2, 3] = 1 + 2 + 3 [] [1, 2, 3, 4] = 1 + 2 + 3 + 4 [4, 3 + 4, 2 + 3 + 4] [5] = 2 + 3 + 4 + 5 [4, 3 + 4] [5, 6] = 3 + 4 + 5 + 6 [4] [5, 6, 7] = 4 + 5 + 6 + 7 [] [5, 6, 7, 8] = 5 + 6 + 7 + 8 [8, 7 + 8, 6 + 7 + 8] [9] = 6 + 7 + 8 + 9 [8, 7 + 8] [9, 10] = 7 + 8 + 9 + 10 [8] [9, 10] = 8 + 9 + 10 [] [9, 10] = 9 + 10 [10] [] = 10 [] [] = 0 In total the memory usage is O(w), where w is your maximum window size. Note that for simplicity of analysis and the example I assumed a fixed-size window w, but there is nothing about the two-stack algorithm that requires this. You can call push(x) and pop() as many times as you’d like between each eval(), growing and shrinking the window size as needed. Floating-point non-associativity Note that we required above that our aggregate combine is associative, meaning: combine(combine(x, y), z) = combine(x, combine(y, z)) Technically speaking, floating-point addition doesn’t respect this. Nevertheless, the above algorithm is still very useful because the results closely match the expected outcome, even more so if you use a compensated summation algorithm like Kahan summation. Another neat thing about the two-stack algorithm is that it doesn’t require commutativity, if you follow the above implementation precisely. The order of operands is maintained, which can matter for things like string concatenation. However, there is a second very useful property of the above algorithm. Each aggregate is strictly a combination of the elements in the window, and none outside the window. This means if your window contains a NaN or infinity (or some other outlier), that value only poisons the windows that contain it rather than the rest of your computation. But even without NaN or infinity it is useful, due to not propagating errors endlessly. E.g. if your sliding window starts with [1e20, 1], this is what would happen with a naive rolling sum: >>> 1e20 + 1 - 1e20 - 1 -1.0 Compensated summation will reduce these effects, but not making your result depend on values outside of the window will eliminate long-term error accumulation entirely.

yesterday • 1 votes
📚 BoredReading

You seem to be enjoying this.

Join free to unlock everything.

Create free account

Already have an account? Sign in