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

(Native German speaker here), it's a very rare use of the word "free" and usually only used in fixed terms like "Freibier", it wouldn't even work for other drinks, e.g., you can't say "Freisaft" or "freier Saft" for free juice, it has to be kostenlos there.

The other Wiktionary example of "freie Krankenversorgung" sounds wrong to be, but it seems to be used rarely in some more formal or legal contexts, no one would say it like this in a casual conversation. Google results also show a 4x difference between frei and kostenlos here in favor of kostenlos. But both are low since "Krankenversorgung" is already a very unusual word. I suspect many of those uses might be bad translations from English.


"Freier Eintritt" seems to be used quite often.


“Frei” can have the meaning of “kostenlos” (https://www.dwds.de/wb/frei#d-1-1-7), but these are limited circumstances that are usually perceived as metaphorical idioms. “Freie Software” has no direct connotation of being “free as in beer” (unlike “Freeware”).


I think in some way it has become that, but I assume the roots are different. People might have said "Freier Eintritt" before it became associated with money. One might be able to see this in "Portofrei" which does not mean "free as in beer" but no postage required - for my feeling it doesn't feel like "Freier Eintritt" yet, it does not have as much money connotations (though when it wanders to the front like in "freiporto" it feels more to be about money).

I do think there is a spectrum. Funny things like "Freifahrt" or even "Freifahrtschein", or "Freikarte", or "Freiexemplar", "Freiparken", "Freiminuten" or "Freivolumen" (people might use "Inklusivvolumen") - so I'd argue when used as part to form a new word it is a synonym for "kostenfrei" (not yet in "Freiwild" which changed a lot).


The use as a suffix "-frei" is something different I think. I'm pretty sure it has kept its meaning of "-los" ("-less") since a long time ago.


I'm surprised that the recent advances in applying typography to engineering problems [1, 2] are not published at SIGBOVIK but are apparently going to a more serious journal.

[1] https://www.researchgate.net/publication/390635826_Structura...

[2] https://www.youtube.com/watch?v=azDaPm13CT8


The time-travelling UB interpretation was popularized by this blog post about 10 years ago [1].

I'm not enough of a specification lawyer to say that this is definitely true, but the reasoning and example given there seems sound to me.

[1] https://devblogs.microsoft.com/oldnewthing/20140627-00/?p=63...


Yes, random blog posts did a lot of damage here. Also broken compilers [1]. Note that blog post is correct about C++ but incorrectly assumes this is true for C as well.

[1]. https://developercommunity.visualstudio.com/t/Invalid-optimi...


I'm inclined to trust Raymond Chen and John Regehr on these matters, so if you assert that they're incorrect here then a source to back up your assertion would help your argument.


I am a member of WG14. You should check the C standard. I do not see how "time-travel" is a possible reading of the definition of UB in C. We added another footnote to C23 to counter this idea:

https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3220.pdf "Any other behavior during execution of a program is only affected as a direct consequence of the concrete behavior that occurs when encountering the erroneous or non portable program construct or data. In particular, all observable behavior (5.1.2.4) appears as specified in this document when it happens before an operation with undefined behavior in the execution of the program."

I should point out that compilers also generally do not do true time-travel: Consider this example: https://godbolt.org/z/rPG14rrbj


So maybe we have different definitions of "time travel". But I recall that

- if a compiler finds that condition A would lead to UB, it can assume that A is never true - that fact can "backpropagate" to, for example, eliminate comparisons long before the UB.

Here is an older discussion: https://softwareengineering.stackexchange.com/q/291548

Is that / will that no longer be true for C23? Or does "time-travel" mean something else in this context?


There may be different definitions, but also a lot of incorrect information. Nothing changes with C23 except that we added a note that clarifies that UB can not time-travel. The semantic model in C only requires that observable effects are preserved. Everything else can be changed by the optimizer as long as it does not change those observable effects (known as the "as if" principle). This is generally the basis of most optimizations. Thus, I call time-travel only when it would affect previous observable effects, and this what is allowed for UB in C++ but not in C. Earlier non-observable effects can be changed in any case and is nothing speicifc to UB. So if you call time-travel also certain optimization that do not affect earlier observable behavior, then this was and is still allowed. But the often repeated statement that a compiler can assume that "A is never true" does not follow (or only in very limited sense) from the definition of UB in ISO C (and never did), so one has to be more careful here. In particular it is not possible to remove I/O before UB. The following code has to print 0 when called with zero and a compiler which would remove the I/O would not be conforming.

int foo(int x)

{

  printf("%d\n", x);

  fflush(stdout);

  return 1 / x;
}

In the following example

int foo(int x)

{

  if (x) bar(x);

  return 1 / x;
}

the compiler could indeed remove the "if" but not because it were allowed to assume that x can never be zero, but because 1 / 0 can have arbitrary behavior, so could also call "bar()" and then it is called for zero and non-zero x and the if condition could be removed (not that compilers would do this)


I think the clarification is good, probably the amount of optimizations that are prevented by treating volatile and atomics as UB barriers is limited, but as your example show, a lot of very surprising transformations are still allowed.

Unfortunately I don't think there is a good fix for that.


E.g. this godbolt: https://godbolt.org/z/eMYWzv8P8

There is unconditional use of a pointer b, which is UB if b is null. However, there is an earlier branch that checks if b is null. If we expected the UB to "backpropagate", the compiler would eliminate that branch, but both gcc and clang at O3 keep the branch.

However, both gcc and clang have rearranged the side effects of that branch to become visible at the end of the function. I.e. if b is null, it's as if that initial branch never ran. You could observe the difference if you trapped SIGSEGV. So even though the compiler didn't attempt to "time-travel" the UB, in combination with other allowed optimizations (reordering memory accesses), it ended up with the same effect.


While interactions with volatile and interactive streams cannot time-travel, anything else is free to - the standard only imposes requirements on a conforming implementation in terms of the contents of files at program termination, and programs with undefined behaviour are not required to terminate, so there are approximately no requirements on a program that invokes undefined behaviour.


> Also broken compilers [1].

The issue you linked to is not a counter example because, as the poster said, g may terminate the program in which case that snippet does not have undefined behaviour even if b is zero. The fact that they bothered to mention that g may terminate the program seems like an acknowledgement that it would be valid to do that time travelling if it didn't.

> Note that blog post is correct about C++ but incorrectly assumes this is true for C as well.

Presumably you're referring to this line of the C++ standard, which does not appear in the C standard:

> However, if any such execution contains an undefined operation, this International Standard places no requirement on the implementation executing that program with that input (not even with regard to operations preceding the first undefined operation).

I looked at every instance of the word "undefined" in the C standard and, granted, it definitely didn't have anything quite so clear about time travel as that. But it also didn't make any counter claims that operations before are valid. It pretty much just said that undefined behaviour causes behaviour that is undefined! So, without strong evidence, it seem presumptuous to assume that operations provably before undefined behaviour are well defined.


The poster is me. You are right that this is not an example for time-travel. There aren't really good examples for true time travel because compilers generally do not do this. But my point is that with compilers behaving like this, people might confuse this for time-traveling UB. I have certainly met some who did and the blog posts seems to have similar examples (but I haven't looked closely now).

Note that I am a member of WG14. We added more clarification to C23 to make clear that this is not a valid interpretation of UB, see here: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3220.pdf


I didn't notice that section when I last read through C23, but I'm very glad to see it. Reining in UB is one of the hardest problems I've had to deal with, and being able to say operations are defined up to the point of UB makes my job so much easier.

The lack of clarity in earlier standards made it impossible to deal with code incrementally, since all the unknown execution paths could potentially breach back in time and smash your semantics.


Thank you. This was my motivation. It is only a small step... much more work to do.


Ok, fair enough. I must admit I was looking at C99 as I thought that was most generally relevant, I don't follow recent C standards (as much as I do those for C++) and C23 hasn't been ratified yet. I've found your new snippet:

> In particular, all observable behavior (5.1.2.4) appears as specified in this document when it happens before an operation with undefined behavior in the execution of the program.

I consider that a change in the standard but, of course, that's allowed, especially as it's backwards compatible for well defined programs.

The wording is a little odd: it makes it sound a like you need some undefined behaviour in order to make the operations beforehand work, and, taken very literally, that operations between two undefined behaviours will work (because they're still "before an operation with undefined behavior"). But I suppose the intention is clear.


The definition of UB (which hasn't changed) is: "behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this document imposes no requirement."

Note that the "for which" IMHO already makes this clear that this can not travel in time. When everything could be affected these words ("for which") would be meaningless.


uIP (TCP/IP stack for tiny microcontrollers) is a another fun real-world example for these types of coroutines: https://github.com/adamdunkels/uip/blob/master/uip/lc-switch...


66% was drivers in 2019.


My aunt donated my grandpa‘s old slide rule there some time ago [0]. Crazy how far we came in just two generations.

Fun to see this site show up on HN :)

[0] https://sliderulemuseum.com/Circular/Curtis_L-M_FrequencyRes...


Yes, you should publish it. Don't bother cleaning it up if you don't feel like it. No one will judge you for the code quality.

Published terrible code is far better than unpublished code.


We wrote some experimental network drivers in high-level languages a few years ago and Java performed better than expected. Yeah, that's a very special and somewhat odd use case, but it was fun :)

We've got some graphs [0] comparing throughput and long-tail latency for the various Java GCs available in OpenJDK 12. Shenandoah's worst case pause time was ~45µs which is the same as disabling the GC (Epsilon GC) which is pretty impressive. Overall performance did suffer under Shenandoah a little bit back then, though. However, I've heard that this improved recently.

[0] https://github.com/ixy-languages/ixy-languages/blob/master/J...


Java can be amazingly performant. One particular example is a video compressor handling many concurrent streams, recompressing them on the fly and shipping them out again saturating a gigabit link.


Most "new tech" is engineering, not science.

If you want empiricism try academic papers instead of mailing lists ;) Some numbers: most critical bugs in the Linux kernel are due to memory safety: 40 out of 65 bugs allowing for code execution found in Linux in 2017 could have been prevented by using a memory-safe language [0]. The paper is about Go, but the same logic applies for Rust. Fun fact: 39 out of these 40 bugs were in drivers [1], so I think starting with drivers in Rust is a good idea.

[0] https://www.usenix.org/conference/osdi18/presentation/cutler (really good read!)

[1] https://arxiv.org/pdf/1909.06344.pdf (disclaimer: I'm a co-author of that paper)


Do you think engineering is somehow less empirical and more abstract than science?


Ridiculous. Have you met engineers from other disciplines? They employ empiricism constantly. You can't build the golden gate bridge without proving that the steel you want to use can take the strain. "Obviously steel C is better than steel Y" won't cut it.

If anything, software development is sandcastle-building. We all have ideas on how to build it, but at the end of the day we're just amateurs fucking around with plastic shovels.


Arguably, other disciplines doesn’t have to handle undecidable problems everywhere. Building a bridge can basically be “statically analyzed” completely, in that every requirement is known beforehand and there is a very constrained mutable state there. On the other hand, even the most basic program one can write will have properties not decidable without actually running the program. I would like to see much more rigorous testing/quality control in user-facing apps as well like websites, but lower level tools usually are written with much higher quality. You would be hard pressed to routinely find a bug in eg. the JVM — and writing formally verified programs is infeasible for any significant complexity.


> Building a bridge can basically be “statically analyzed” completely

This. And even then, after the calculations are done the engineer add a “safety margin” (and it's not 10%, more like 500 or 1000%).


Good point. And also, it is possible to add safety margins to other areas of engineering. One may be able to eg. store an array with padded nulls (actually we sort of do it at an OS level, just not with null but some “magic number”), but if it’s a loop/recursion, non-correct software will blow up either way - here it is more close to mathematics than science or engineering in my opinion.


We expect software to work, in places that are engineered. A SpaceX Falcon launch control system that encounters anything undecidable, whether in launch preparation or in flight, has utterly failed.

It was Microsoft that got people used to software failing on a routine basis, and "have you rebooted?". Before, anything that ever crashed, you sent back for a refund, or expected a tech to come out and fix it post-haste. Clearly MS could not make money that way, but getting people with no experience to believe failure was normal was quite cheap to arrange.


You are comparing apples and oranges here. Bridge building should be compared to aerospace software engineering, not to the average CRUD app.

Apart from that, I think you are giving too much credit to construction engineering outside of the highly regulated sectors. They mess up just as often, and expensively so. But stakeholders in construction are slighly more aware that their requirements are going to be set in stone, pun fully intended.

What really makes a difference is the malleability of software. This is an invitation for feature creep and last-minute changes. Also, the impact of overengineered, unmaintainable software is not quite in-ya-face as for physical assets. Therefore, in software engineerin project managers need to be religious about these matters. And software engineers must get better at communicating about these concerns.


Calling programming engineering in the absence of licensure or any standard of performance for the entire industry is kind of silly.


This is a popular argument, but I'd argue isn't really true. Looking at electrical engineering, there is no standard license and no standard of performance. Like programming, it's just too broad across different areas with vastly different requirements. The equivalent of very sloppy web development might be a blinky (non-kids) toy, that fails after a few uses with no particular requirements (other than "make it as cheap as possible"). Or look at how often smartphones and laptops fail electrically (or compound failures) for really obvious reasons, that just weren't important enough to fix.

To address the particular point being made here, lots of things in EE are experience driven, for example which decoupling capacitors to use or to not use 90 degree angels on pcbs. I think many aspects of typical engineering are way more driven by experience and vague rules of thumb than we expect looking in from the outside.

The reason why this is more obvious in software development may be because it's pretty hard to have good measurements and understanding failures is pretty hard and working around them is pretty easy.


Very few countries in the world have "licensure" for engineers. It's not a protected term of any sort in the US nor in most of Europe. Canada is one of the few places that's strict about it. Occupational licensing is a form of job market restriction that's generally frowned upon.


In the three European countries I've lived in (2 in EU + UK), licensed engineers must sign off on new building construction plans, and take responsibility for the suitability of the design.


Licensed engineers exist, but the term "engineer" is not protected.


The real root cause for all that flow director mess and core balancing is that there's a huge disconnect between how the hardware works and what the socket API offers by default.

The scaling model of the hardware is rather simple: hash over packet headers and assign a queue based on this. And each queue should be pinned to a core by pinning the interrupts, so you got easy flow-level scaling. That's called RSS. It's simple and effective. What it means is: the hardware decides which core handles which flow. I wonder why the article doesn't mention RSS at all?

Now the socket API works in a different way: your application decides which core handles which socket and hence which flow. So you get cache misses if you don't tak into account how the hardware is hashing your flows. That's bad. So you can do some work-arounds by using flow director to explicitly redirect flows to cores that handle things but that's just not really an elegant solution (and the flow director lookup tables are small-ish).

I didn't follow kernel development regarding this recently, but there should be some APIs to get a mapping from a connection tuple to the core it gets hashed to on RX (hash function should be standardized to Toeplitz IIRC, the exact details on which fields and how they are put into the function are somewhat hardware- and driver-specific but usually configurable). So you'd need to take this information into account when scheduling your connections to cores. If you do that you don't get any cache misses and don't need to rely on the limited capabilities of explicit per-flow steering.

Note that this problem will mostly go away once TAPS finally replaces BSD sockets :)


We didn't mention RSS/RPS in the post mostly because they are stable. (Albeit, relatively ineffective in terms of L2 cache misses.) FlowDirector, OTOH, breaks that stability and causes a lot of migrations, and hence a lot of re-ordering.

Anyways, nice reference for TAPS! Fo those wanting to dig into it a bit more, consider reading an introductory paper (before a myriad of RFC drafts from the "TAPS Working Group"): https://arxiv.org/pdf/2102.11035.pdf

PS. We went through most of our low-level web-server optimization for the Edge Network in an old blogpost: https://dropbox.tech/infrastructure/optimizing-web-servers-f...


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

Search: