My understanding of the heart rate correlation to lifespan between different species is that it is “within a couple levels of magnitude” and that an increase of 10% in a human’s rate does not correlate at all to a 10% decrease in lifespan
I feel like that example is missing some context - if signups did increase then their experiment was successful - we aren’t here to make pretty pages, we’re here to make money.
The problem is that it's easy to prove that signups are increasing, and lot harder to prove that there was a measurable increase in number of paying users. Most A/B tests focus on the former, very few on the latter. We had a free plan, and most users who signed up never made a single API request. So, assuming that the increase in signups is driving more business is just foolhardy.
> The problem is that it's easy to prove that signups are increasing, and lot harder to prove that there was a measurable increase in number of paying users.
Okay? The A/B test sought to measure which of two options A and B led to more signups.
> So, assuming that the increase in signups is driving more business is just foolhardy.
Your "A/B test enthusiast" was not testing for or trying to prove a causal relationship between increased signups and more business.
If he made the claim separately, then that is the context that is missing from now multiple comments.
You can always track signup/paying-users ratio. Purpose of landing/pricing page is to get the users to sign-up. Unless some dark pattern or misinformation is used to confuse users into sign-up, more users is a positive thing.
We could also have bumped 255.255.255.255 to 999.999.999.999 = 1 trillion IP addresses, easy-to-remember and backward compatibility with legacy devices.
Modern clients and servers get IP addresses in these new whole IP ranges and can communicate together.
Relatively easy to adapt the code of modern software also since it's about removing a restriction from a client-perspective.
Load-balancers and legacy clients use IP addresses from the old pool.
If you have Windows XP you can communicate only to legacy IPv4 (in practice only loadbalancers from Cloudflare, GCP, AWS and co) and your other legacy stuff.
Others happily communicate together.
But no, we got this wonderful IPv6.
Sad because it was really doable, theoretical maximum below 512 GB of memory for routers to store the whole routing table, it's manageable, versus the 2.176×10^22 exabytes (!) of IPv6.
I'm guessing everyone downvotes you for the very strange implication that most software stores IP addresses in ASCII. All networking APIs I'm aware of expect IPv4 addresses as a DWORD.
This is the point, instead of rewriting a full stack, I would rather change the prototype of these APIs.
To store 999.999.999.999, then you are totally fine with a 64-bits INT (QWORD), and there is no struggle to backward-compatibility store a 32-bits INT (DWORD) into it.
It's more of a matter of doing #ifdef IPV4_EXTENDED #define DWORD QWORD #endif
and add an extra IP field inside the IP header packet itself that says, "this is the IPV4_EXTENDED DESTINATION 5-bytes IP", and the previous field is marked a legacy/deprecated.
In fact, it's quite convenient, since we are all INT64, sockaddr_in would largely fit in an INT64 for both IP itself and the other elements that are in the struct.
5 bytes for the sin_addr field is enough to store until 999.999.999.999.
Gives you 3 bytes to store the port etc.
The networking APIs guys could be drinking cocktails at the bar by now, if they would change these types.
There is backward compatibility and smaller effort for a great impact, and this is beautiful.
It's actually beneficial for the majority of developers.
From the developer of Windows, to the developer of Age of Empires, to the developer of a CRUD app on the web (who stores IP addresses as a string or as an int), they wouldn't see too much struggle to port to int64.
Less than having to build a full new IPv6 experience.
In practice, client apps, at the time you open a new socket, if your lib says it wants an INT32 or an INT64 it doesn't matter for the developer of that app, since type is automatically casted.
time() had a similar situation.
We migrated by adding new bytes, we didn't redefine the concept of time.
From a developer-experience, "link to the latest version of the network library, oh btw, connect() accepts an int64" and remove the UI restriction of 255.
It could even be possible to give compatibility to very old software that we lost source-code from by overriding the network layer with LD_LIBRARY_PRELOAD or equivalent, and patch these softwares by manually NOP the right JGE instruction (the asm code for " >= " ) that checks if we are over 255.
So you need to send a message from your host 5.6.7.8 to one of these newly enabled hosts 500.600.700.800. You update the software on your host, and your target's ISP is updated, and your target updates, and we'll even hand wave and assume your ISP is updated despite apparently having enough legacy addresses to allocate you one.
The message goes out to your ISP router, who then sends it to their upstream ISP, who looks at the IP message, doesn't understand whatever header you've shoved the extended address in, and discards it. Then what's in your standard, backwards compatible 32 bit field? The start of the address? Does your packet go to some other random host on the internet? A placeholder address like all 0s? Does your message get discarded?
How do you convince that middleman to update their hardware? They have no benefit from it? This is the situation IPv6 was in for decades until their literally were not enough IPv4 addresses which finally lit a fire under companies to start enabling it.
(I'm not pushing this idea to the max, I mean, now IPv6 is here so we'll just go with it, but this is for the mental and engineering exercise).
To answer your question, in my model, the legacy IPv4 field contains the IP addresses of "IPv4 to IPv4 Extended bridges".
Let's imagine you want to connect to [example.com]:
Clients who speak IPv4 Extended and their ISP is compatible, get the IPv4 Extended answer:
425.223.231.123 A+ example.com
and directly to it
Clients who speak IPv4 Extended but don't have an IPv4 Extended compatible ISP, add that extra IPv4 Extended header and speak to the bridges.
425.223.231.123 A+ example.com
34.23.12.2 BR example.com (the bridge)
Clients who speak IPv4 only but don't speak IPv4 Extended don't have to think about IPv4 Extended at all, since they will go through the usual layer-7 (typically HTTP) reverse-proxy, or a routing based on rules (ip/port pair).
Cloudflare does search large scale reverse proxies, it works fine in practice.
If someone has an incentive to run such bridges or reverse proxies solution, first it's yourself, to save your preciouses IPv4.
To the end user the promise is "you will connect faster to the internet if you are in native IPv4 Extended (because you skip these intermediate bridges)"
We actually have a nice mechanism that we could reuse for knowing which bridges to use, it's reverse DNS lookup.
> In practice, client apps, at the time you open a new socket, if your lib says it wants an INT32 or an INT64 it doesn't matter for the developer of that app, since type is automatically casted.
A lot of networking gear is far closer to an ASIC than a general-purpose CPU, so you can't "just change it to int64". They were built to process 32-bit addresses, and are unlikely to be able to swap to 64-bit without enormous performance penalties.
E.g. routing tables would balloon in size, which in practice means that you can store far fewer routes. Ignoring changes in the size of the netmask, it's 4x the size to store 64-bit address pairs, so your route tables are a quarter the size they used to be.
The hardware refresh requirements are a big part of the reason why IPv6 rollout is so slow, and your proposal doesn't avoid that. Getting the software side of things to play nice has always been the easy part of this, even in IPv6.
> It could even be possible to give compatibility to very old software that we lost source-code from by overriding the network layer with LD_LIBRARY_PRELOAD or equivalent, and patch these softwares by manually NOP the right JGE instruction (the asm code for " >= " ) that checks if we are over 255.
In IPv6 land, you just encapsulate IPv4 in IPv6 [1]. It's a lot cleaner than jankily trying to override instructions, especially when the relevant code may run on your NIC rather than your CPU and require kernel firmware patches (or, god forbid, custom NIC firmware) to implement.
and what about the protocol bytes that go over the wire - you know, the most important and hardest to change part?
There've been several proposals to make "IPv4 but bigger addresses". All of them are just as hard to deploy as IPv6. You still need to upgrade all your routers and you still need to run two parallel networks.
If it's going in the same spot in the packet header as the current IPv4 address, how do you make sure that the 20-30 routers owned by 3 different companies that are likely to be between your computer and the destination computer exhibit a behavior that is consistent with moving packet closer to the destination?
(If they don't, you've just made a version of IPv6 that is worse-- it's missing the last 30 years of IPv6 implementation.)
It's written above, bridge destination address in the "legacy" IPv4 destination header, and that bridge can be figured out by looking up the reverse dns entries on a IPv4 Extended IP, until the user is natively using an IPv4 Extended network.
This brings the packet closer to the destination.
The new address goes into the Options field, you can store lot of data there (somewhere up-to-60 bytes, and we need 1 or 2 byte actually).
Reminder: The goal is to add one-byte to have more IP addresses, not rewrite the whole internet.
Here it looks like the guys wanted to fix that IP allocation problem, and then they went all-in, and decided to rewrite everything at the same time.
It's ok, and even a good idea in theory, but network administrators don't like to be pressured "in emergency" into upgrading to this solution.
The practice shows that people rather prefer doing NAT than IPv6.
In the same breath he talked about how he wanted to build this “pristine” system with safety and fault tolerance as priority and how he wanted to use raw pointers to shared memory to communicate between processes which both use multiple threads to read/write to this block of shared memory because he didn’t like how chatty message queues are.
He also didn’t want to use a ring buffer since he saw it as a kind of lock
I forgot to mention that we were building all this in C#, as mandated by Mr CTO.
He also couldn’t decide between windows server and some .RHEL or Debian flavor
I doubt this guy even knew what a kernel driver was.
He very transparently just discounted anything he didn’t already understand.
After poorly explaining why he didn’t like ring buffers, he said we should take inspiration from some system his friend made.
We started reading over the system and it all hinged on a “CircularBuffer” class which was a ring buffer implementation.
Okay, that would be a normal amount of bonkers thing to suggest in C or another language with real pointers.
But in C#, that is a batshit insane thing to suggest. I'm not even sure if it's even legal in C# to take a pointer to an arbitrary address outside of your memory. That's.. That's just not how this works. That's not how any of this works!
It is legal to do so. C# pointers == C pointers, C# generics with struct arguments == Rust generics with struct (i.e. not Box<dyn Trait>) arguments and are monomorphized in the same way.
All of the following works:
byte* stack = stackalloc byte[128];
byte* malloc = (byte*)NativeMemory.Alloc(128);
byte[] array = new byte[128];
fixed (byte* gcheap = array)
{
// work with pinned object memory
}
Additionally, all of the above can be unified with (ReadOnly)Span<byte>:
var stack = (stackalloc byte[128]); // Span<byte>
var literal = "Hello, World"u8; // ReadOnlySpan<byte>
var malloc = NativeMemory.Alloc(128); // void*
var wrapped = new Span<byte>(malloc, 128);
var gcheap = new byte[128].AsSpan(); // Span<byte>
Subsequently such span of bytes (or any other T) can be passed to pretty much anything e.g. int.Parse, Encoding.UTF8.GetString, socket.Send, RandomAccess.Write(fileHandle, buffer, offset), etc. It can also be sliced in a zero-cost way. Effectively, it is C#'s rendition of Rust's &[T], C++ has pretty much the same and names it std::span<T> as well.
Note that (ReadOnly)Span<T> internally is `ref T _reference` and `int _length`. `ref T` is a so-called "byref", a special type of pointer GC is aware of, so that if it happens to point to object memory, it will be updated should that object be relocated by GC. At the same time, a byref can also point to any non-GC owned memory like stack or any unmanaged source (malloc, mmap, pinvoke regular or reverse - think function pointers or C exports with AOT). This allows to write code that uses byref arithmetics, same as with pointers, but without having to pin the object retaining the ability to implement algorithms that match hand-tuned C++ (e.g. with SIMD) while serving all sources of sequential data.
C# is a language with strong low-level capabilities :)
I heard my whole life how my generation was going to be useless too. Ruined by arcades and violent tv shows. I am not saying there aren’t going to be drawbacks but “5 second attention span” sounds like the same ole same ole to me.
The pace and scale is completely different though, it's accelerating and all encompassing
Before it was "rock n roll is bad" for a full generation, now in a single generation you have multiple paradigm shifts, we don't even start to understand the impact of tech X that we're already deploying Y and Z