2,803 karma · joined July 22, 2012
Opus was intentionally designed so that the most important bits are in the front of the packet, which can be better protected by your modulation scheme (or simple FEC on the first few bits). See slide 46 of https://people.xiph.org/~tterribe/pubs/lca2009/celt.pdf#page... for some early results on the position-dependence of quality loss due to bit errors.
It is obviously never going to be as robust as G.711, but it is not hopeless, either.
The primary benefit of PVQ is the side-information-free activity masking. That is the sort of thing that cannot be judged via PSNR and requires careful subjective testing with human viewers. Not something you want to be rushing at the last minute. After gauging the rest of AOM's enthusiasm for the work, we decided instead to improve the existing segmentation coding to make it easier for encoders to do visual tuning after standardization. That was a much simpler task with much less risk, and it was adopted relatively easily. I still think it was the right call.
[1] https://datatracker.ietf.org/doc/html/draft-cho-netvc-applyp...
I am not sure I would say that is true.
The entire entropy coder, used by every tool, came from Daala (with changes in collaboration with others to reduce hardware complexity), as did some major tools like Chroma from Luma and the Constrained Directional Enhancement Filter (a merger of Daala's deringing and Thor's CLPF). There were also plenty of other improvements from the Daala team, such as structural things like pulling the entropy coder and other inter-frame state from reference frames instead of abstract "slots" like VP9 (important in real-time contexts where you can lose frames and not know what slots they would have updated) or better spatial prediction and coding for segment indices (important for block-level quantizer adjustments for better visual tuning). And that does not even touch on all of the contributions from other AOM members (scalable coding, the entire high-level syntax...).
Were there other things I wish we could have gotten in? Absolutely. But "done" is a feature.
I can think of at least a few risks that one would not have with T-bills besides counterparty risk. You have management risk: they have said what their strategy is and what they will do, but what if they don't? I would also ask who is on the other side of these trades and how big that market is or can be. Alpha Architect's own explainer implies that box spreads can also be used to borrow money and that this might be cheaper for the borrowers than margin loans, which makes some sense, but it seems like a pretty esoteric instrument to use for that. Most of their argument is an appeal to the efficiency of markets, which might be true until it isn't. Finally, there's regulatory risk. They think this works under the current rules, but a regulator might disagree with that. If someone does not like it a sufficient amount the rules could be changed, just like there's already an exception for "original issue discount" that makes zero-coupon bond income like Treasuries count as interest income, not capital gains, even though no interest payments are ever made.
I'm not in finance, though, I'm just some guy, so take all of the above with a grain of salt.
You should always be able to make your opponent's arguments at least as well as they do, as that is the first step to overcoming them.
The argument from patent proponents is that without the legal monopoly, they would rely on trade secret law instead, so they would do their best to ensure no one else understood what they do. They still do, within the confines of what disclosure is legally required to get a patent issued (I once had an engineer tell me that if he had not invented the thing being patented, he would have no idea what the patent application the lawyers wrote for it was describing), but at least there is a legal requirement.
Of course, there are important contexts where that argument is irrelevant, such as standards development. Trade secret law is no use there, because the value is in the network effects of the standard, not the invention. Yet we still have patent-riddled standards.
f[x_Integer] := ...
will define a rule for f[] that only matches expressions where the argument to f[] has the head "Integer". It is even possible to use arbitrary predicates: vec3Q[v_] := VectorQ[v, NumberQ]&&Length[v]==3
f[v_?vec3Q] := ...
This lets you sort-of have type-checking. This is entirely opt-in, so you have to be somewhat rigorous about its use or it does not do any good. Also, in practice if any invocation of f[] does not have arguments which match the types for which you have defined it, the expression just remains unevaluated, which can create a mess (but maybe less of a mess than evaluating the function on input of the wrong form). The performance impact (particularly of the predicate version) is also non-zero, but my experience is that the biggest performance limitations come from trying to keep your machine from grinding to a halt when a runaway expression applied to the wrong thing explodes in complexity and eats all of your RAM... and this helps avoid that.While I have found this to be very helpful for writing and debugging hairy expressions, I used Mathematica for years before I even knew this was a thing. In reality almost no one does this, certainly not with any consistency, and the situation is as bad as you fear it would be.
The value created by a sale, i.e., the difference between what something costs to produce and what someone is willing to pay for it, can be divided into producer surplus and consumer surplus, i.e., the benefit that accrues to the producer and the consumer, respectively, at a given price point.
Price discrimination is an attempt to convert consumer surplus into producer surplus by charging individuals a price closer to what they are willing to pay. The blue region gets bigger (producer profit increases) and the red region gets smaller.
Price discrimination requires market power in order to set prices higher than the equilibrium price (that is, it cannot happen if there is perfect competition). With market power but without price discrimination, producers maximize profit by charging a "monopoly price" higher than the competitive equilibrium price. So there will already be a wedge on the right hand side of the economic surplus region missing, as shown in this graph: https://en.wikipedia.org/wiki/Deadweight_loss#/media/File:Ta... (the labels are for taxes, but the math is the same for monopoly pricing) [2]. Price discrimination may recapture some of that "deadweight loss" (selling to some consumers at cheaper prices than they would with a uniform monopoly price), but again producers will take more of it than they would in a competitive market, and they do not create any value that would not have existed in said competitive market (selling to any consumer at a price lower than the competitive equilibrium point is unprofitable by definition, so producers will do their best to avoid it).
Looked at in this way, it is easy to see that consumers end up worse off overall than they would in a competitive market with a uniform market price.
[1] From https://en.wikipedia.org/wiki/Economic_surplus#Consumer_surp...
See https://ieeexplore.ieee.org/abstract/document/660992 for the connection between fractal image coding and wavelets + vector quantization.
To go from JPEG to lossy WebP (really, VP8 intra frames), the main differences are
(a) using smaller (4x4) transform blocks instead of 8x8,
(b) noticing that if you take just the first coefficient in each block, it looks like a lower resolution of the original image, and applying another 4x4 transform to those coefficients,
(c) adding more sophisticated prediction between blocks (e.g., subtract the pixel on the right edge of the previous block from all the pixels in the same row of the current block before transforming them, or something similar to predict in other directions),
(d) filtering along block edges to reduce blocking artifacts (many JPEG decoders do this, but WebP/VP8 make it a mandatory part of the standard), and
(e) using arithmetic coding instead of Huffman coding.
There are of course other minor differences in the details.
But the main mechanism that underlies both is a transform that can be derived from the SVD if one assumes a simple statistical model of natural image data.
What? Sure you can. See the W3C patent policy, as one example: https://www.w3.org/Consortium/Patent-Policy-20200915/
That other organizations do not do that is a choice.
As a video codec developer I was a little sad about that, actually. I had to start looking closer to see problems.
[1] https://people.xiph.org/~tterribe/pubs/gpusurf.pdf Section III.A
If you try casting your pointer to (__m128i *) and dereferencing, sometimes the compiler will optimize it correctly (to PMOVSXBD with a memory operand, which really only loads 4 bytes), and sometimes (for example, when optimizations are disabled) it will emit MOVDQA, which does a 16-byte aligned load. Tools like UBSan will also (correctly) complain about it.
Eventually __mm_loadu_epi32() was added, but it was released in a broken state in the initial gcc implementation: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99754 so you can't rely on it. There is __mm_load_ss(), but gcc's implementation derefs a (float *), so it still requires 4-byte alignment: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84508
The workarounds described in the article (combined with _mm_cvtsi32_si128()) are the only reliable solution I have found. Various compilers still generate an extra MOVD instruction instead of using a memory operand, and reportedly ICC will even do an extra load into a general-purpose register before moving the value to a SIMD register: https://stackoverflow.com/questions/72837929/mm-loadu-si32-n...
Those extra moves are cheap on x86, so it is not the end of the world, but the whole situation is less than ideal.
There is. It is called patent exhaustion. See https://en.wikipedia.org/wiki/Exhaustion_doctrine_under_U.S....
There was a fad of issuing a "covenant not to sue" in lieu of a license, specifically to try to avoid triggering exhaustion, but courts seem to be (correctly, in my IANAL opinion) treating them as licenses anyway. See, e.g., https://www.patentlyo.com/patent/2013/01/3rd-circuit-covenan...
I believe the team we worked with at Arm during AV1 standardization is no longer there, which is too bad. They were really great guys to work with.
Your suspicion is mostly correct, though obviously you cannot share too much of the DCTs as these must be bit-exact and are different for each of the standards. But especially things like the compressed tile cache for reference frames used in motion compensation are extremely complicated (to save memory bandwidth and power) and entirely shareable. The SRAM used for line buffers is also a lot of area and shareable. And so on.
Yes, sorry, 2**27 32-bit words, so 512 MB.
>However a downside of A* is that I need to store the depth in the visited map as they may be found out of order. I also store the move that got there to allow quick reconstruction. (It is within the same byte as the depth.)
There is a 1:1 correspondence between visited configurations and queue entries, so for total RAM usage it actually doesn't matter what information you're storing in which one. Storing the moved robot and previous robot position (10 or 11 bits) will be more efficient than storing a 32-bit queue index for the back-pointer, though.
I agree the bitmap is harder to scale for 5 robots, but 256 * 0.5 GB is just 128 GB, not terabytes, and there are machines with that much RAM. Sorting and deduplicating non-goal robots should reduce this to around 5.6 GB. The challenge is that the actual reachable states are quite dense (2/3rds of all possible states with 4 robots and no deduplication, and probably an even higher percentage with 5 robots and deduplication), so there is not actually a lot of room to make this smaller if you want to allow searching the full (~100 move) configuration space. That is not necessary to reach any single-robot goal, of course. The worst 4-robot single-goal solution I'm aware of is 27 moves. See this example:
+---+-----------------+---------+
| | | |
| + +-+ + + |
| | | |
| + +-+ +-+ |
| |G|
| + + |
| | |
| + +-+ + |
| | | |
| +-+ +-+ +-+
| |
+-+ +-+ +-+ |
| | | |
| + +---+ + |
| | | |
| | | |
| | | |
| +-+ +---+ +-+ |
| | | |
| + + + |
| | |
| + +-+ +-+
| | |
| +-+ + +-+ |
| | g| |
| +-+ + |
| |
+-+ +-+ + |
|Y R| | |
| + + + +-+ |
|B | | |
+-----------+---------+---------+
My solver on my laptop finds a solution in 67 seconds using 666 MB of RAM after examining 82,872,901 configurations, so just 2.1% of the possible configuration space. Only 316 MB is needed for the queue, so that means it was using ~350 MB for the bitmap, which is not particularly efficient (~35.4 bits of RAM mapped per visited state).This example was found by Pieter Wuille, who wrote a reverse-state-space explorer (given a board layout and a goal, it finds the optimal number of moves to the goal for every initial robot position combination in about a minute). www.robotreboot.com allowed for 20,736 board configurations and 20 possible goal locations per board. With four robots there are 656,239,500 possible robot configurations after sorting/deduplication. Using rotational symmetry, and storing four bits per board/goal/configuration combination to say what robot to move and in which direction, he estimates you could store the solutions for all possible games in around 31 TB with about a year of CPU time. That won't fit in RAM, but it is certainly achievable.
Another fun property of solutions to consider is what we term the solution "complexity", defined as the minimal number of times you must switch from moving one robot to moving a different one (plus one, since originally you are not moving any robot). The "must" there is important, since if two robots do not interact, you could just alternate moving between them, but the goal of complexity is to measure the amount of interaction. A related property is the "causality": the amount by which a solution's complexity exceeds the number of robots it moves.
Optimizing for minimum complexity is pretty straightforward, but of course the "fun" solutions are the ones with maximal complexity/causality...
+---+-----------------+---------+
| | | |
| + +-+ + + |
| | | |
| + +-+ +-+ |
| | |
| + + |
| | |
| +-+ + |
| R | |
| +-+ +-+ +-+
| | |
+-+ + + +-+ |
| | |r |
| +-+ +---+ + |
| | | |
| | | |
| | | |
| +-+ +-+ +---+ +-+ |
|Y | G | | |
| + + + +-+
| |
+-+ |
| B |
| +-+ |
| | |
| + + + |
| | | |
| + +-+ + +-+ |
| | | |
| +-+ + +-+ + |
| | | |
+-----------+---------------+---+
Solving this board required examining 30,427,896 unique states.One of the things I found that improved the speed the most was simply reducing the memory usage.
The "visited states" test is just a 2**32 bit (2**27 byte) table indexed by the 32-bit combined robot positions. For most problems, most of the pages in that table will never even get mapped, but at worst the whole thing is 128 MB. There are probably clever ways to permute the indices of the robot positions to get better cache coherency in the table lookups, but I did not bother.
The state in the breadth-first search queue is just the 32-bit combined robot positions. No back-pointers are stored to recover the solution(s). Instead, a side-table keeps track of the index in the queue where the number of moves increases (since all entries are ordered by the number of moves required to get there), and upon reaching a goal the solution is recovered by back-tracking one move at a time and searching the relevant portion of the queue for a state plus a move that gets us to the next state. This sounds slow (and it is! printing the solution now takes a non-negligible fraction of the runtime), but it is actually faster than storing back-pointers in the queue. The queue size grows exponentially with the number of moves¹, but you do not need to search for the last move in your solution, because you were just making it when you reached that goal. So you avoid having to read through a large portion of the queue just by skipping that last level, and you avoid having to write the extra data for the whole queue. Assuming your back pointer would have been a 32-bit queue index, that cuts the data written in half. You also don't have to check the "visited states" table during back-tracking, so you avoid lots of cache pressure that was present when filling the queue the first time.
¹: At least for long enough to reach any single robot goal... if you search for all reachable combinations of robot positions, eventually you run out of unvisited states and the number of newly reachable states starts shrinking again until it hits almost nothing, at somewhere approaching 100 moves. For a given board, roughly 2/3rds of the 3,937,437,000 possible combinations of robot positions are reachable, giving a maximum memory usage of about 10 GB.
That was not true of the pandemic.
Yes, that is the point. WebRTC has to work with cheap / commodity hardware running a general purpose OS with clocks that are not synchronized. You get a bunch of clocks in different units running at different rates, and periodically are told the mappings between them. It is your job not to "run out of memory, or advance too quickly and run out of data to process," and indeed, WebRTC implementations have methods of solving those problems.
> Probably practical implementations just use buffer fullness to drive their resampler.
You are correct that in practice this gets driven by the jitter buffer in libwebrtc, but there is no resampler at all. Small changes in sampling rate are hard / computationally expensive to do well (as you certainly know). That is an implementation detail, though. You can use a different strategy if you have different requirements.
> ...precise clock recovery required in professional applications.
What are your actual precision requirements?
I also do not understand your concerns with wrap-around. Even SRs once every 5 seconds are more than enough to resolve ambiguity. You would have to go over half a day without seeing one before you could actually get confused, by which point you would have bigger issues.
Keep in mind that even at the start of the stream, the media timestamps are not absolute: each chooses an initial offset randomly.