Workflows/orchestration/reconciliation-loops are basically table stakes for any service that is solving significant problems for customers. You might think you don't need this, but when you start needing to run async jobs in response to customer requests, you will always eventually implement one of the above solutions.
IMO the next big improvement in this space is improving the authoring experience. In short, when it comes to workflows, we are basically still writing assembly code.
Writing workflows today is done in either a totally separate language (StepFunctions), function-level annotations (Temporal, DBOS, etc), or event/reconciliation loops that read state from the DB/queue. In all cases, devs must manually determine when state should be written back to the persistence layer. This adds a level of complexity most devs aren't used to and shouldn't have to reason about.
Personally, I think the ideal here is writing code in any structure the language supports, and having the language runtime automatically persist program state at appropriate times. The runtime should understand when persistence is needed (i.e. which API calls are idempotent and for how long) and commit the intermediate state accordingly.
One benchmark would be to track down a python/JS/etc based "hello world" demo container. Base one version on Intel and the other on ARM, and measure each versions container build-time and request latency after it is set-up.
If changing the base image is all that's needed and both Dockerfiles otherwise assume ubuntu, this should not take too long.
This seems like a weird benchmark, reading from /dev/urandom and gzipping random data does not seem like something most folks will want to do. It even appears like /dev/urandom speeds differ greatly on various architectures [0] and there are issues with /dev/random being fundamentally slow due to the entropy pool [1] (but I guess this is why the author uses /dev/urandom).
It would be better to measure something more related to what docker users will actually do, like container build time of a common container, and/or latency of HTTP requests to native/emulated containers running on the some container.
One reason to feel positive about the virtualization issues is that Rosetta 2 provides x86->ARM translation for JITs which an ARM-based QEMU could perhaps integrate into it's own binary translation [2].
I use an 2016 Macbook i7/8GB as my daily development system. I love it. It's light and portable, which is important for me.
The main thing to understand about these machines is that the i7 CPUs are about as good as any other CPU on the market (aside from less cores), but they're fanless which means they rely on the case to dissipate heat, and so will thermally throttle during long-running high CPU jobs. They're perfect for short lived jobs, even multi-minute compilations, but will have a hard time getting through repeated long jobs that require long durations of high CPU.In short, great for bursty computations with longer idle times where the laptop has a chance to cool off.
For instance, I build brew packages, full llvm builds, even small ML models, etc, without problems, because the machine starts cold and there is enough time after the job is done for the machine to cool off again. My machine suffers on tasks like Docker+Kubernetes/minikube that run a constantly polling VM in the background that takes 25-100% CPU when running idle.
For instance, someone in this thread mentioned the iOS emulator might be difficult to run. This may not be true, so long as the emulator does not constantly use lots of CPU - if it just uses high CPU in response to input events, it will likely be fine.
This 'partial interpretation' trick used here has also been used successfully in the database community to accelerate whole queries, etc. Tiark Rompf's group in particular has been pushing this idea to it's limit.
Thanks for the response! FWIW I'm overall really interested in this, as I maintain an application that uses localStorage to keep very important data while the app is offline until it can be uploaded.
I recently tested localForage [0] to see if it could be more reliable and get around storage limitations of localStorage. Unfortunately, LocalForage has a very annoying problem where it picks a single storage backend to use, but the one it chooses can switch on page reloads (yes, even on the same device/browser.) and this switching causes data loss as keys stored in the other backends are unaccessible. I'm very interested to see if IronDB can help here! Thanks for working on this.
> When a value is retrieved via its key, IronDB...
> Looks up that key in every store.
> Counts each unique, returned value.
> Determines the most commonly returned value as the 'correct' value.
> Returns this most common correct value
> Then IronDB self heals: if any store(s) returned a value different than the determined correct value, or no value at all, the correct value is rewritten to that store. In this way, consensus, reliability, and redundancy is maintained.
I'm not sure any of these properties are ensured the way anyone would want. Why do this? What are the failure modes this protects against? If only one of the backing stores is active when a result is written, and then the others later become active with no data, is blank data returned for the prior result? It seems like recording timestamps would fix this problem nicely on a single system and make this thing overall quite reliable.
I didn't mean to say this was a silly thing to do - most modern processors execute instructions out of order on multiple ALUs.
The problem is that the abstraction layer between the python code in question and the processor's instruction stream is so thick that it's hard to say one way or the other that the processor is indeed executing that particular pair of instructions in parallel. It's definitely executing many instructions out of order, but it's unclear (without inspection of the python interpreter and its assembly) what's happening at the machine level.
Looking at the bytecode of the python program at least begins to tells us that the python bytecode of the two versions is fundamentally different, which could account for the performance difference. Although, what exactly makes the material difference is also under debate elsewhere in the thread. :)
This comment is excellent. The title of the original post should be: "When optimising code, never guess, always read the bytecode/assembly."
Without actually reading the assembly/bytecode/etc, you end up speculating about silly things like 'the two evaluations and assignments can happen in parallel, and so may happen on different cores.'.
Minor nitpick about their exit code technique [0]: The command checks if the table exists, but it does not appear to re-run if the source file has been updated. Usually with Make you expect it to re-run the database load if the source file has changed.
It's better to use empty targets [1] to track when the file has last been loaded and re-run if the dependency has been changed.
Last I checked, MapBox's WebGL based vector tile renderer was a cut above the rest. For simple mapping, Google's stuff may cut it, but MapBox has the ability to draw complex vector polygons over a wide area that render/zoom/scroll fast and provide a nice experience. This is pretty important if you want to provide fine grained analysis of geographic areas.
If you wanted this on GMaps, you were stuck rendering all your vectors to image tiles, hugely increasing the size. MapBox's vector support made this very easy! It may be that Google/free options have caught up in this space, but I haven't re-evaluated in a few years.
I think MapBox could also be a winner in the GIS space as the GIS options have not made the most graceful move to the web.
What about the fact that these instructions might get partially executed in the pipeline before the branch gets resolved and the pipeline flushed? If a mis-fetched instruction can reach the LSU stage before the pipeline gets flushed, it might serve as a speculative memory load...
> There's no reason to predict a branch if you're not going to execute speculatively.
Not quite. Branch prediction is typically used on non-speculative architectures in order to avoid pipeline bubbles. (You could argue that pipelining is a form of speculation)
Whether or not they're vulnerable has more to do with how their pipeline is structured. It's possible for an architecture to be vulnerable if a request to the load store unit can be done within the window between post-branch instruction fetch/exec and a branch resolution. Eyeballing the pipeline diagram from the above docs, it looks like you can maybe get a request to the LSU off before the branch resolves. dramatic music
"There is nothing to support bitcoin except the hope that you will sell it to someone for more than you paid for it.”
Genuinely asking, isn't this the plan for many people? How is bitcoin different than other commodities or property in this regard? Would Bogle say the same thing about those investments?
Acquisition and administration of public housing projects. The world needs more publicly housing for people of all economic statuses. It seems incredibly time consuming to build or convert private housing due to vast amounts of paperwork and groups that must be involved to finance and manage these projects. Automation can help to find a business plan, search for funding, help run governance, and do accounting for the on-going operations.
FWIW it should be possible to vectorize the search across the row store. With 24 byte tuples (assuming no inter-row padding) you can fit 2.6 rows into a 64 byte cache line (a 512 bit simd register). Then it's just a matter of proper bit masking and comparison. Easier said than done, I figure because that remainder is going to be a pain. Another approach is to use gather instructions to load the first column of each row and effectively pack the first column into a register as if it were loaded from a row store and then do a vectorized compare as in the column-store case.
All of that to underscore it's not that one format vectorizes and the other doesn't. The key takeaway here is that with the column store, the compiler can automatically vectorize. This is especially a bonus for JVM based languages because afaik there is no decent way to hand-roll SIMD code without crossing a JNI boundary.
I notice on the tech spec's page that the Mini also has bluetooth [0]. Would love to know if this means all connected homes will play (the same) audio via bluetooth.
Undecidable in general, but there are two approaches I've seen work based on AST manipulation and comparison:
1. Solvers that search for applicable re-write rules to transform X into Y, such as Cossette for SQL. These may not terminate because undecidability lol http://cosette.cs.washington.edu/
2. Canonicalization of the AST. This is a form of #1 but much more restricted, and the hope is that functions that are equivalent end up canonicalized in the same way. LLVM and GCC do this for a variety of reasons. In the example given, you'd hope that both functions get canonicalized to either the left or right hand side.
https://gcc.gnu.org/onlinedocs/gccint/Insn-Canonicalizations...http://llvm.org/docs/MergeFunctions.html
IMO the next big improvement in this space is improving the authoring experience. In short, when it comes to workflows, we are basically still writing assembly code.
Writing workflows today is done in either a totally separate language (StepFunctions), function-level annotations (Temporal, DBOS, etc), or event/reconciliation loops that read state from the DB/queue. In all cases, devs must manually determine when state should be written back to the persistence layer. This adds a level of complexity most devs aren't used to and shouldn't have to reason about.
Personally, I think the ideal here is writing code in any structure the language supports, and having the language runtime automatically persist program state at appropriate times. The runtime should understand when persistence is needed (i.e. which API calls are idempotent and for how long) and commit the intermediate state accordingly.