Hacker Newsnew | past | comments | ask | show | jobs | submit | tasty_freeze's commentslogin

I don't think it is that simple. I would wager $$$ that dead drivers tend to skew younger, mostly young men who think they are great drivers, drive way too fast, pass with little margin, etc. Young people probably skew higher for THC use as well.

Having said that, I think that effect explains only part of the 40%, but can't explain all of it.


In the same way that Shazam can identify songs despite the audio source being terrible over a phone, mixed with background noise. It doesn't capture the audio as a WAV and then scan its database for an exact matching WAV segment.

I'm sure it is way more complex than this, but shazam does some kind of small windowed FFT and distills it to the dominant few frequencies. It can then find "rhythms" of these frequency patterns, all boiled down to a time stream of signature data. There is some database which can look up these fingerprints. One given fingerprint might match multiple songs, but since they have dozens of fingerprints spread across time, if most of them point to the same musical source, that is what gets ID'd.


I use moises frequently for track separation for learning songs. It does pretty dang well. I was shocked that the score of moises is ranked way worse than just about everything else, including lalal.ai, which I also used before buying moises. Perhaps lalal.ai has gotten better since I last tried it.

Maybe I'm totally misinterpreting, but the chart I'm looking at says "Net Win Rate of SAM Audio vs. SoTA Separation (text prompted)", so perhaps a lower number means that the alternative model is better?

Now that I go back and read it again I agree with you. Presumably "win rate" means what percent of the time did the SAM model (Meta's new one) beat the other tool over some set of examples.

and there is a linked article about Waymo's data reporting, which is much more granular and detailed, whereas Tesla's is lumpy and redacted. Anyway, Waymo's data with more than 100M miles of self-supervised driving shows a 91% reduction in accidents vs humans. Tesla's is 10x the human accident rate according to the Austin data.

ZX BASIC (as far as I know) was not a MS BASIC product, but many of the same techniques were used in MS BASIC. There were books describing the techniques that this blog post mentions, plus a lot more.

For instance, the symbol table is built as each variable is encountered, and it is not ordered. Thus, it was common for the first line of a BASIC program to be something like:

   10 Y=X=B=Z=1
because Y, X, B, and Z were the most frequently accessed variables, so getting them at the start of the symbol table sped up future look-ups.

Another thing: I'm pretty sure MS BASIC was smart enough to check if the GOTO or GOSUB line number was greater than the current line number, and if so, it would seek the target line from the current line, instead of the start of the program.

WANG 2200 BASIC-2 had an optimization for DEFFN statements. There was a small, fixed-size table, something like 16 entries, to hold a pointer to the first n DEFFN statements. Most programs used less than this limit. When an "FNX(...)" or whatever was encountered, it would search that DEFFN table, which then had a direct pointer to the line where the DEFFN was located. If there were more than 16 DEFFN statements, it would then have to do a linear search through the program text to find it.4

[EDIT] I forgot to mention, the article mentions that NEXT and RETURN also cared about line number placement. I haven't studied ZX BASIC so maybe it is different, but in MS BASIC and WANG BASIC there is a stack frame that contains a pointer to the head of the loop or the return point, respectively, for those two cases. Just storing a line number would be insufficient because the FOR body and the RETURN point might be in the middle of a line, eg

    200 X=X+1:GOSUB 1000:FOR I=1 TO 20:T=T-B(X):NEXT I

GW-BASIC, and probably other MS BASICs, only looked up destination lines once, then replaced the GOTO code with a direct pointer to the destination. Something similar may have been used for other symbols, but I don't remember for sure?

Guess the ZX BASIC did not do that, or it would have improved the measurements seen in the article?

BLUE Book of GW-BASIC is full of details and tricks like that and some of it probably is useful for other BASICs as well.

https://github.com/robhagemans/hoard-of-gwbasic


The Z80 had LDIR which was a string copy instructions. The byte at (HL) would be read from memory, then written to (DE), HL and DE would be incremented, and BC decremented and then repeated until BC became zero.

LDDR was the same but decremented HL and DE on each iteration instead.

There were versions for doing IN and OUT as well, and there was an instruction for finding a given byte value in a string, but I never used those so I don't recall the details.


LDIR? We used DMA for that.

I was referring to LODSB/W (x86) which is quite useful for processing arrays.


LDIR sounds great on paper but is implemented terribly making it slower than manual unrolled loop

https://retrocomputing.stackexchange.com/questions/4744/how-...

Repeat is done by decrementing PC by 2 and re-loading whole instruction in a loop. 21 cycles per byte copied :o

To be fair Intel did same fail implementation of REP MOVSB/MOVSW in 8088/8086 reloading whole instruction per iteration, REP MOVSW is ~14 cycles/byte 8088 (9+27/rep) and ~9 cycles/byte 8086 (9+17/rep), ~same cost as non REP versions (28 and 18). NEC V20/V30 improved by almost 2x to 8 cycles/byte V20 or unaligned V30 (11+16/rep) and 4 cycles/byte on fully aligned access V30 (11+8/rep) with non REP cost being 19 and 11 respectively. V30 pretty much matched Intel 80186 4 cycles/byte (8+8/rep, 9 non rep). 286 was another jump to 2 cycles/byte (5+4/rep). 386 same speed, 486 much slower for small rep counts, under a cycle for big rep movsd. Pentium up to 0.31 cycles per byte, MMX 0.27 cycle/byte (http://www.pennelynn.com/Documents/CUJ/HTML/14.12/DURHAM1/DU...), then 2009 AVX doing block moves at full L2 cache speed and so on.

In 6502 corner there was nothing until 1986 WDC W65C816 Move Memory Negative (MVN), Move Memory Positive (MVP) 7 cycles/byte. Slower than unrolled code, 2x slower than unrolled code using 0 page. Similar bad implementation (no loop buffer) re-fetching whole instruction every iteration.

1987 NEC TurboGrafx-16/PC Engine 6502 clone by HudsonSoft HuC6280 Transfer Alternate Increment (TAI), Transfer Increment Alternate (TIA), Transfer Decrement Decrement (TDD), Transfer Increment Increment (TII) theoretical 6 cycles/byte (17+6rep). I saw one post long time ago claiming block transfer throughput of ~160KB/s on a 7.16 MHz NEC manufactured TurboGrafx-16 (hilarious 43 cycles/byte) so dont know what to think of it considering NEC V20 inside OG 4.77MHz IBM XT does >300KB/s.

    CPU / Instruction   Cycles per Byte
    Z80 LDIR 8-bit              21
    8088 MOVSW 8bit             ~14
    6502 LDA/STA 8bit           ~14
    8086 MOVSW                  ~9
    NEC V20 MOVBKW 8bit         ~8
    W65C816 MVN/MVP 8bit        ~7  block move
    HuC6280 T[DIAX]/TIN 8bit    ~6  block transfer instructions
    80186 MOVSW 16bit           ~4
    NEC V30 MOVSW               ~4
    80286 MOVSW                 ~2
    486 MOVSD                   <1
    Pentium MOVSD               ~0.31
    Pentium MMX MOVSD           ~0.27 http://www.pennelynn.com/Documents/CUJ/HTML/14.12/DURHAM1/DURT1.HTM

Only the Z80 refetched the entire instruction, x86 never did it this way. Each bus transfer (read or write) takes multiple clocks:

    CPU                        Cycles  per              theoretical minimum per byte for block move
    Z80 instruction fetch      4       byte
    Z80 data read/write        3       byte             6
    80(1)88, V20               4       byte             8
    80(1)86, V30               4       byte/word        4
    80286, 80386 SX            2       byte/word        1
    80386 DX                   2       byte/word/dword  0.5
LDIR (etc.) are 2 bytes long, so that's 8 extra clocks per iteration. Updating the address and count registers also had some overhead.

The microcode loop used by the 8086/8088 also had overhead, this was improved in the following generations. Then it became somewhat neglected since compilers / runtime libraries preferred to use sequences of vector instructions instead.

And with modern processors there are a lot of complications due to cache lines and paging, so there's always some unavoidable overhead at the start to align everything properly, even if then the transfer rate is close to optimal.


This is correct, but it should be noted that the 2-cycle transfers of 286/386SX/386DX could normally be achieved only from cache memory (if the MB had cache), while for DRAM accesses at least 1 or 2 wait states were needed, lengthening the access cycles to 3 or 4 clock cycles.

Moreover, the cache memories used with 286/386SX/386DX were normally write-through, which means that they shortened only the read cycles, not also the write cycles. Such caches were very effective to diminish the impact on performance of instruction fetching, but they brought little or no improvement to block transfers. The caches were also very small, so any sizable block transfer would flush the entire cache, then all transfers would be done at DRAM speed.


0 wait state 286 was pretty standard affair for 8-10 and some 12MHz gray boxes. Example https://theretroweb.com/motherboard/manual/g2-12mhz-zero-wai...

"12MHz/0 wait state with 100ns DRAM."

another https://theretroweb.com/chip/documentation/neat-6210302843ed...

"The processor can operate at 16MHz with 0.5-0.7 wait state memory accesses, using 100 nsec DRAMs. This is possible through the Page Interleaved memory scheme."


Despite what the article says, the 68000 was microcoded too. Another difference is that the 68K was a 32b architecture, not 16b, and that required investing more transistors for the register file and datapath.

Not only was it microcoded, but it was sufficiently divorced from the assumptions of the 68000 instruction set that IBM were able to have Motorola make custom "68000-based" chips that ran S/370 code directly.

Want a different architecture? Sure, just draw it with a different ROM. Simple (if you've got IBM money to throw around).


I read (30 years ago) the book "Microprocessor Design" by Nick Tredennick. He was the architect and wrote the microcode for both the 68K and the S/370. The S/370 was based on his recent design experience with the 68K, but it wasn't just a microcode swap. In the book he describes his process where he would write the ucode for each instruction on a 3"x5" card (or was a 4"x7"). At times he'd find sequences that were too clunky and then go back to the circuit design folks and ask for them to add some extra logic for that corner case to make the ucode more efficient.

The book also had a glossary section in the back and a number of the entries were funny. One I recall was his definition for "methodology", which was something like "A word people use when 99% of the time they mean 'method'."



Oh right, nerdsniped into hunting that book down.


Well that's my Sunday evening sorted, thanks :-)

The 68000 actually had both microcode and nanocode, so it was even further from hardwired control logic than the 8086. In terms of performance the 68000 was slightly faster than the 286 and way faster than the 8088 (I never used an 8086 machine).

The 286 looks like it ought to be usefully quicker in general? Motorola did a good job on the programming model, but you can tell that the 68000 is from the 1970s. Nearly all the 68000 instructions take like 8+ cycles, and addressing modes can cost extra. On the 286, on the other hand, pretty much everything is like 2-4 cycles, or maybe 5-7 if there's a memory operand. (The manual seems to imply that every addressing mode has the same cost, which feels a bit surprising to me, but maybe it's true.) 286 ordinary call/ret round trip time is also shorter, as are conditional branches and stack push/pop.

The timings given in the datasheet of 286 are very optimistic and they can almost never be encountered in a real program.

They assume that instructions have been fetched concurrently without ever causing a stall and that memory accesses are implemented with 0 wait states.

In reality, instruction fetching was frequently a bottleneck and implementing a memory with 0 wait states for 80286 was much more difficult than for MC68000 or MC68010.

With the available DRAM, normally both 80286 and 80386 would have needed a cache memory. Later, after the launch of 80386DX, cache memories became common on 386DX MBs, but I have not seen any 80286 motherboard with cache memory.

They might have existed at an earlier time when 286 was the highest end, but by the time of the coexistence with 386 the 286 became the cheap option, so its motherboards never had cache memory, thus the memory accesses always had wait states, increasing the probability of instruction fetch bottlenecks and resulting in significantly more clock cycles per instruction than in the datasheet.


> but by the time of the coexistence with 386 the 286 became the cheap option, so its motherboards never had cache memory

Not true. I vaguely remember servicing systems with chipsets from OPTI(only 2 large ones) having it. IIRC those were funtional(not exact) clones of Chips&Technologies NEAT(4 to 5 large chips), later shrunken to one by SCAT (Single Chip AT).

Also in times when the 386 ran at 33Mhz, or even at 40 if made by AMD, Compaq introduced 386SX systems with cache, and I remember wondering "why, oh why?". Talk about overengineering...


My reading is that there aren't really a lot of addressing modes on 286, as there are on 68000 and friends, rather every address is generated by summing an optional immediate 8 or 16 bit value and from zero to two registers. There aren't modes where you do one memory fetch, then use that as the base address for a second fetch, which is arguably a vaguely RISC flavored choice. There is a one cycle penalty for summing 3 elements ("based indexed mode").

What you say about memory indirect addressing is true only about MC68020 (1984) and later CPUs.

MC68000 and MC68010 had essentially the same addressing modes with 80286, i.e. indexed addressing with up to 3 components (base register + index register + displacement).

The difference is that the addressing modes of MC68000 could be used in a very regular way. All 8 address registers were equivalent, all 8 data registers were equivalent.

In order to reduce the opcode size, 80286 and 8086 permitted only certain combinations of registers in the addressing modes and they did not allow auto-increment and auto-decrement modes, except in special instructions with dedicated registers (PUSH, POP, MOVS, CMPS, STOS, LODS), resulting in an instruction set where no 2 registers are alike and increasing the cognitive burden of the programmer.

80386 not only added extra addressing modes taken from DEC VAX (i.e. scaled indexed addressing) but it made the addressing modes much more regular than those of 8086/80286, even if it has preserved the restriction of auto-incremented auto-decremented modes to a small set of special instructions.


Not for the data path; the 68000 operates on 32 bit values 16 bits at a time, both through its external 16 bit bus and internal 16 bit ALU. Most 32 bit operations take more cycles. But yes, it has a 32 bit programming model.

Actually, the 68000 had one full (all operations) 16 bit ALU and two more simple (add/subtract, so AU might be a better name) 16 bit ALUs so in the best case it could crunch 48 bits per clock cycle. The 8086 had one full 16 bit ALU and one simple 16 bit ALU (the ancestor of todays AGUs - address generator units).

Check out "Perl Best Practices" by Damien Conway, and the more recent "Modern Perl" by Chromatic. Both can be had as paperbacks, and I think both are also available free on online.


I'll go further. Ignore the Perl specific bits and Conway's "Perl Best Practices" is one of the best general programming books ever written.

It has so many great pieces of advice that apply to any programming task, everything from naming variables, to testing, error handling, code organization, documentation, etc, etc. Ultimately, for timeless advice on programming as a profession the language is immaterial.


It isn't bad language design that you need to study the language before you can use it. I look at haskell programs and it looks mysterious to me because I haven't spent any time studying it, but I'd not thing to say it is bad language design.

Yes, one can write obscure perl code and some love perl golfing. In the same way there is an IOCCC which delights in unreadable code, it doesn't mean that the C language should be relegated to the dustbin. The answer is to write readable code, no matter which language is in use.


But I can look at most Python code and be able to understand what it does. With perl, I have to look up so much.

- Why is there a `1;` on a single line in the middle of this file?

- What is `$_`?

- This parallel execution manager doesn't actually seem to define what code needs to run in parallel in any specific way, how does this work?

- What is this BEGIN block at the start of this Perl file? Why is that necessary?

- What's going on with qx, qw, qq?

- What does chomp do when it's just on its own line, with no arguments given to it?


To be able to fully comprehend Perl (even without having to embrace it), one needs a fiddle.

Perl and some of Perl's quirks will make more sense once you realise that it is deeply rooted in UNIX command line utilities, UNIX conventions and some UNIX shell defaults, except when it is not, i.e.

  - What is `$_`?
$_ follows the spirit of shell variables (such as $*, $@, $! etc., heavily used in Korn, Bourne flavours but not the C flavours), but was repurposed or – more likely – picked from a pool of vacant characters with the help of a dice roll. Kind of like how ancient Egyptians built the pyramids with the help of sophisticated cranes and machinery and then vapourised their tools with high-particle beams to leave future generations guessing «how on Earth did they manage to do that». This is one of the main criticisms of Perl.

  - What is this BEGIN block at the start of this Perl file? Why is that necessary?
Perl started out as an improvement over «awk», and BEGIN is an awk construct where it is used frequently, e.g. awk 'BEGIN { IFS=":" } { … do something … }'

  - What does chomp do when it's just on its own line, with no arguments given to it?
It follows the standard convention of UNIX utilities that expect the input to come from the standard input stream (file descriptor 0 or <file-in in the shell) when no input file name has been specified. So, when no <FILE1> given to chomp, it chomps on the standard input.


Again: python syntax is more akin to what you are used to, and so it feels more comfortable to you.

$_ is inscrutable if you haven't studied perl, but the same thing would happen to anyone who sees a python decorator for the first time. what does "else: do after a while loop in python? Only people who know python know what it does (and I suspect most don't). The different quoting operators are also trivial to learn. In comparison, yield from python is also simple syntax but the semantics are much more involved.

BEGIN? Take 60 seconds to read what it means. And if you knew awk, you'd not have to do that, as it was directly lifted from awk.


> BEGIN? Take 60 seconds to read what it means.

Yes, that's exactly the problem: it's additional mental load you have to read up on.

Have 60 of those small oddities in a file, and suddenly you're spending an hour dealing with Perl quirks rather than actually debugging that obscure script a retired coworker wrote a decade ago. A 5-minute fix turned into a 65-minute fix, solely because of Perl.

Most programming languages use more-or-less the same constructs, and the few per-language oddities are usually fairly obvious from context. In practice this means someone familiar with any programming language will to a certain extent be able to read, debug, and patch small issues in code written in any other programming language. Perl's obscure and dense syntax makes this virtually impossible. Give a buggy Python script to a developer who daily-drives Javascript and they can probably fix it. Give a buggy Perl script to that same Javascript developer, and they probably have absolutely no clue what's going on.

In practice this turns Perl into technical debt. It has very few natural niches where it is genuinely the best, so experienced Perl developers are quite rare to have around. Any script written in Perl will most likely have to be read by someone who isn't an experienced Perl developer - which is significantly harder than a script written in just about any other language. The result is that any Perl scripts you have lying around are basically a grenade waiting to go off: they can't be maintained, so they should probably be replaced.


> what does "else: do after a while loop in python? Only people who know python know what it does (and I suspect most don't).

OK, I had never heard of the syntax, but in its own defense it does exactly what you'd guess, the same thing it does after an "if".

These are equivalent statements:

    preloop:
      if condition:
        do_more_stuff()
        goto preloop

    while condition:
      do_more_stuff()
and these are also equivalent:

    preloop:
      if condition:
        do_more_stuff()
        goto preloop
      else:
        wrap_it_ip()

    while condition:
      do_more_stuff()
    else:
      wrap_it_up()


It could've easily been defined that the else branch runs if the while condition never had a true value at all. In fact, I think that's more intuitive.


What are you trying to say? It is defined that way. And the example I provided above makes that completely explicit.

But here, from the official documentation:

> if the expression is false (which may be the first time it is tested) the suite of the else clause, if present, is executed and the loop terminates.

https://docs.python.org/3/reference/compound_stmts.html#the-...


It's not just a matter of "read the docs", though, because languages can differ in how many distinct concepts/constructs they employ. Python has gradually added more over the years but still I think is well short of Perl in this regard.


Given python’s love for string-leading sigils, the previous commenter should be quite comfortable with the idea of obscure single-letter operators that dictate the interpretation of the following tokens.


With regards to BEGIN

The only reason AWK needs a BEGIN is due to it's implied data loop. As far as I know perl has an explicit data loop and as such needs no BEGIN.

Oh god, perl has an implied data loop mode doesn't it. Sigh, now I am reading perl manpages to find out.

Update: of course it does, -n or -p


If you think that's bad, try learning python or a verbose language while not speaking english, all of these words like while, for, if, else, break are just gibberish and your code just reeks of some weird mish mash of broken english and broken <mother tongue>, I have a hypothesis that terseness favors universality, if you don't speak english, something like $_ is equal or easier to grasp, it honestly just looks like terse and weird math.


That was the idea of APL (and its successors like J and K) -- make programming a math notation rather than pretend to be a human language (generally English, but there have been programming languages with keywords in Chinese and Russian, among others).


I'd love to use APL if it wasn't pay to use, or just, an absolute pain to install, there's also j, but I find it worse to use single letter names for functions, ivy seems like the best of both worlds.

You’re mad that you have to look up what keywords do in a programming language you aren’t familiar with? If you think Python is always clear, I can guarantee you (as someone with relatively expert grasp of Bash, Ruby, Go, and once long ago, Perl) that no, it isn’t always obvious.


Yeah, it's true that Perl did not have as a design goal that a complete newcomer should be able to intuitively understand the code without having any prior exposure to the language. There is a little bit of a learning curve, and that was completely expected by Perl's creators. Yes, you have to learn about the idioms above, but they became second-nature. For many of us, the model clicked in our heads and the terseness was worth it. You could express a lot of functionality in very few characters, and if you had invested in learning, it was very quick to grok because common patterns were reduced to familiar abstractions in the language.

And yet, as the industry grew and all sorts of people from all sorts of backgrounds converged in this space, the tolerance and appetite for funky/terse waned in favor of explicit/verbose/accessible. It's probably for the better in the end, but it did feel a little bit like the mom-and-pop store on the corner that had weird pickled things at the register and a meemaw in the back got replaced by a generic Circle K with a lesser soul.


> And yet, as the industry grew and all sorts of people from all sorts of backgrounds converged in this space, the tolerance and appetite for funky/terse waned in favor of explicit/verbose/accessible. It's probably for the better in the end, but it did feel a little bit like the mom-and-pop store on the corner that had weird pickled things at the register and a meemaw in the back got replaced by a generic Circle K with a lesser soul.

This is an amazing point that I haven't seen anyone else make about languages in this way.

As someone who got into the industry right after Perl's heyday and never learned or used it but learned programming from some former Perl power users, Perl has a pre-corporate/anarchic/punk feel about it that is completely opposite to something like Golang that feels like it was developed by a corporation, for a corporation. Perl is wacky, but it feels alive (the language itself, if not the community). By contrast, Golang feels dead, soulless.


Honestly, $_ and "what does a function do when I don't supply any arguments?" are really nice in Perl, and not that difficult to understand. I think a lot of languages could use a 'default variable'.


$_ was one of the things that put me off perl, because the same syntax meant different things depending on context.

The Pragmatic Programmers had just started praising Ruby, so I opted for the that over Perl, and just went with it ever since. Hated PHP and didn't like Python's whitespace thing. I never Ruby on Rails'd either. That said my first interactive website was effectively a hello world button with cgi/perl.

But trying to learn to code from reading other peoples perl scripts was way harder than the (then) newer language alternatives.

Now I'm over 50 none of that is nearly as important. I remember being young and strongly opininated, this vs. that - its just part of the journey, and the culture. It also explains the current FizzBuzz in CSS minimisation post. We do because we can, not necessarily because we should.


When I was choosing between learning python and perl in the late 90's, it was the context sensitivity of perl expressions which really squicked me. To me, that was the critically bad language decision in perl. You can make context-sensitive expressions in python (using operator overloading, for instance), but you have to go out of your way to do it in one way or another. In perl you can easily do it by accident, and it can result in serious bugs. It seemed masochistic, to me.


Seems like the essential criteria is not whether you can write opaque code in it, but rather whether the language enables you to accomplish most tasks using clear, readable code. They aren't mutually exclusive.

Hopefully I am paraphrasing you correctly.


can you make explicit the point you are making?


The vaccine was not tested on people that were already dead.


Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: