Live data from Hacker News

22nd Century C

procedural.github.io

81–90 of 106 posts

Re: 22nd Century C

#81
post #79

Earlier quoted context omitted.

> specific bit manipulation Rust absolutely lets you work at the bit level, you can shift and mask just like in C. > explicit memory management It depends on what you mean by "explicit" here. Most Rust code has scope-based memory management, but you can also call malloc/free within an unsafe block.

> but you can also call malloc/free within an unsafe block. Very cool. I was afraid I would be forced into an FFI type scenario (which Rust apparently has good support for anyway). Thanks!

No problem. To elaborate on _this_ slightly, today in stable Rust you can call libc's malloc/free directly. There's also the Rust alloc::heap module, but it's not yet stable, and so is only available on nightly Rust https://doc.rust-lang.org/alloc/heap/index.html

Re: 22nd Century C

#82
post #30
post #22

Earlier quoted context omitted.

Yeah, I'm arguing that the situation you describe (accurately) is better than baked-in sizes all over the source code. Use the platform-native types (whatever size they may be) unless you have a reason not to, then use or an analogue. If CHAR_BIT is 13, let char be 13 bits: the platform probably chose that for a reason. When you have to pack it into a TCP header, do your strict fixed-width stuff there.

I completely agree with you when it comes to function return types. For data structure fields, especially structs used many, many times like in very large arrays, I'd say it's sometimes worth using fixed size types to get better control over memory use. Using a 64-bit int for a field a 16-bit integer can handle will use up 4x as much memory. And if you've got a ten or a hundred million structs of that type, then it r…

Even a hundred million integers still adds up to only 300 MB extra for 64- vs. 16-bit. On any reasonable modern server, laptop, or desktop, that kind of memory usage probably will be the least of your worries, all the more if you really have an application that needs to hold hundreds of millions of ints in memmory at the same time.

And if you are programming for a very specific embedded or otherwise constrained system, then you anyways want full control over the exact sizes of your types, as discussed elsewhere here.

Is this "wasting resources", as you say? Probably yes. Is it worth the extra development effort to fine-tune that on modern machines? Probably not - and it might even be premature optimization. (Yes - I agree there are corner case where it indeed will make sense, but those are the exception, not the norm.)

Re: 22nd Century C

#83
post #79

Earlier quoted context omitted.

> but you can also call malloc/free within an unsafe block. Very cool. I was afraid I would be forced into an FFI type scenario (which Rust apparently has good support for anyway). Thanks!

No problem. To elaborate on _this_ slightly, today in stable Rust you can call libc's malloc/free directly. There's also the Rust alloc::heap module, but it's not yet stable, and so is only available on nightly Rust https://doc.rust-lang.org/alloc/heap/index.html

Cool. I didn't even find either of those on the roadmap a bit over year ago when I looked at it -- outside some speculative chat on forums/lists/tickets. It seems as if I need to give it a couple more months and give it a look-see again.

Re: 22nd Century C

#84
post #60
post #30

Earlier quoted context omitted.

I completely agree with you when it comes to function return types. For data structure fields, especially structs used many, many times like in very large arrays, I'd say it's sometimes worth using fixed size types to get better control over memory use. Using a 64-bit int for a field a 16-bit integer can handle will use up 4x as much memory. And if you've got a ten or a hundred million structs of that type, then it r…

I strongly disagree with your sentiment. If you wrote an application that's as efficient as possible without any wasted bits in the size_t type, it then only works on your machine. If I wanted to run such an application on my supercomputer with 2TB of RAM (such machines exist), I would then have to recompile for a 41-bit size_t. We use machine-neutral (but architecture-specific) size_t for these kinds of things expli…

A 48-bit size_t like I suggested could address up to 256TB of RAM. All modern x86_64 cpus are limited to 48-bits of address space, you're not losing any portability here.

Also consider my strlen example. Say you compute strlen by iterating through the whole string until you find a 0, then you return a size_t for the number of bytes you iterated through. That operation is O(n) in the length of the string. If you were to use my strlen function on a string whose length is greater than would fit into a 32-bit integer, say a 1TB string or something, then the function would take so long to compute it that it would be useless. To be efficient, you'd probably have to redesign your program to do some special things to handle 1TB strings, maybe some special algorithms, or some kind of indexing. Returning a 64-bit integer type does not mean that the function can actually handle working with 64-bit sized quantities. So if you have a lot of datastructures where you keep string length, why store them as a 64-bit size_t when your application would be completely unable to handle strings of that size without keeling over?

Of course you wouldn't really use a 48-bit size_t, because x86_64 cpus don't work well with 48-bit quantities.

Re: 22nd Century C

#85
post #82
post #30

Earlier quoted context omitted.

I completely agree with you when it comes to function return types. For data structure fields, especially structs used many, many times like in very large arrays, I'd say it's sometimes worth using fixed size types to get better control over memory use. Using a 64-bit int for a field a 16-bit integer can handle will use up 4x as much memory. And if you've got a ten or a hundred million structs of that type, then it r…

Even a hundred million integers still adds up to only 300 MB extra for 64- vs. 16-bit. On any reasonable modern server, laptop, or desktop, that kind of memory usage probably will be the least of your worries, all the more if you really have an application that needs to hold hundreds of millions of ints in memmory at the same time. And if you are programming for a very specific embedded or otherwise constrained syste…

I'm not sure where you are getting the 300MB figure from. ((64-16)/8)x10^8/2^20 gives me 572.2MB of wasted space. What's even more interesting is looking at the percentage of wasted space. (((64-16)/8)x10^8/2^20)/(((64)/8)x10^8/2^20) means a whopping 75% of the memory use of our program is completely useless wasted space.

Using more space than you need to will also impact performance. First there's the cache issues. A 64KB L1 cache can fit 32768 16-bit integers, but only 8192 64-bit integers. Other cache layers will also fit less 64-bit than 16-bit integers in them, causing 4x more hits to the slow RAM backing store. Hitting RAM is very slow in comparison with cpu operations, so this will make your program a lot slower.

There's also the computational speed issues. Lets say your problem can be implemented using the AVX/AVX2 instructions. These registers can compute multiple results at once, in parallel. The AVX registers are 256 bits, which means they can operate on 16 16-bit integers at once. In comparison, they can only work on 4 64-bit integers at once. So there's another potential for 4x improvement, although the cache problems are probably going to be a bigger issue in practice.

Re: 22nd Century C

#86

Earlier quoted context omitted.

You have an int16_t? How do you port it to a machine without a 16-bit type? Hint: you're being done a favor by being forced to think about that.

Ok, now lets suppose you have done the job of thinking about it, and you are now up to actually acting on your thinking. What do you write? You want: - A single code base - The code be compiled on the "I don't have a 16-bit type" machine, as well as on your amd64 machine and x86 machine. Having it work on a 24-bit machine might be nice too if you want bonus points. - You don't want to waste memory on the crappy embed…

Honestly, there's no way I'd use an MCU that doesn't support 16-bit integer types. I've never even heard of such a thing. It's really a property of the compiler, not the hardware. I use 32-bit ints and floats all the time on 8-bit 8051s and AVRs, both of which are among the most primitive microcontrollers in use today. They are typedefed to S32 and F32 precisely because I want a unified code base that compiles and works the same under Windows.

If I know I'll never need more than 8 or 16 bits, I'll use an S8 or S16, but I certainly won't hesitate to use an S32 or even an S64 on these chips if needed. Using "int" is asking for some serious grief in the embedded world, which is why it's almost never done anymore.

I've also never heard of a 24-bit processor -- unless you're thinking of some antediluvian bit-slice processor, or something like that? -- but I'm sure the same code will compile and run on it just fine, if and when I ever encounter one. If not, that's not my problem. It can be blamed jointly on the administration and faculty at the school that handed a diploma to the programmer who wrote the C compiler for it.

At the end of the day, if I'm that worried about RAM and resource usage, or if I'm building 100,000,000 of something that has to use the smallest, cheapest controller possible, I'm probably writing in assembly anyway.

Re: 22nd Century C

#87
post #68

Earlier quoted context omitted.

> Memory. Defaulting to 16-bits when an 8-bit variable will do can be incredibly wasteful on an 8-bit µC. I hope it doesn't seem like I'm just moving the goalposts, but the obvious answer here is to use a type which is at least 8-bits wide if you only need 8-bits. Such a type exists, and is called "char" (with the appropriate signedness modifiers). This whole discussion has been about writing portable code. Using a "…

I did not expect you'd say "char", because it's a fixed size numeric type. Isn't that what you have been arguing against so far? Anyway, I'd get yelled at if I checked in code where I do math on char. It has to be on uint8 or int8 (or other widths). Not that I'd ever check in such code, after all, I agree with the company policy there. But "char" is just a part of the problem. We have the same issue with all kinds of…

char isn't fixed width. It is guaranteed to be at least 8-bits wide, but on a SHARC chip it is 32-bits wide.

> The thing is, I assume all uintX types to really mean at least X bits wide. I am aware that the compiler is free to use its discretion for allocating memory. I worry about finer details only when I need to.

That is explicitly not what they are. The standard is very clear that they must be exactly X bits wide, and if there is no type that is exactly X bits wide (and 2s compliment) then the types must not exist.

But, if this is how you are treating these types, then aren't you basically just saying that in your mind you aren't using fixed width types? If everywhere you write uintX_t you are thinking "a type that is at least X bits wide", then you have to do all the reasoning as if the type is of an unknown width. It sounds like you have the worst of both worlds - the lack of portability of fixed width types combined with the slipperiness of minimum width types.

Re: 22nd Century C

#88

Earlier quoted context omitted.

Ok, now lets suppose you have done the job of thinking about it, and you are now up to actually acting on your thinking. What do you write? You want: - A single code base - The code be compiled on the "I don't have a 16-bit type" machine, as well as on your amd64 machine and x86 machine. Having it work on a 24-bit machine might be nice too if you want bonus points. - You don't want to waste memory on the crappy embed…

Honestly, there's no way I'd use an MCU that doesn't support 16-bit integer types. I've never even heard of such a thing. It's really a property of the compiler, not the hardware. I use 32-bit ints and floats all the time on 8-bit 8051s and AVRs, both of which are among the most primitive microcontrollers in use today. They are typedefed to S32 and F32 precisely because I want a unified code base that compiles and wo…

> Honestly, there's no way I'd use an MCU that doesn't support 16-bit integer types. I've never even heard of such a thing.

SHARC chips don't have any integer types smaller than 32-bits. Why would they, when they can't address any smaller than 32-bits?

> I've also never heard of a 24-bit processor -- unless you're thinking of some antediluvian bit-slice processor, or something like that?

56k chips have 24-bit chars. Not so common any more, but they are still kind of kicking around.

> but I'm sure the same code will compile and run on it just fine, if and when I ever encounter one.

My point is that it won't. If you use types which must be exactly 16-bits wide, then you can't compile that code for machines that don't have a 16-bit wide register.

Re: 22nd Century C

#89

Earlier quoted context omitted.

Honestly, there's no way I'd use an MCU that doesn't support 16-bit integer types. I've never even heard of such a thing. It's really a property of the compiler, not the hardware. I use 32-bit ints and floats all the time on 8-bit 8051s and AVRs, both of which are among the most primitive microcontrollers in use today. They are typedefed to S32 and F32 precisely because I want a unified code base that compiles and wo…

> Honestly, there's no way I'd use an MCU that doesn't support 16-bit integer types. I've never even heard of such a thing. SHARC chips don't have any integer types smaller than 32-bits. Why would they, when they can't address any smaller than 32-bits? > I've also never heard of a 24-bit processor -- unless you're thinking of some antediluvian bit-slice processor, or something like that? 56k chips have 24-bit chars.…

[deleted]

Re: 22nd Century C

#90

Earlier quoted context omitted.

Honestly, there's no way I'd use an MCU that doesn't support 16-bit integer types. I've never even heard of such a thing. It's really a property of the compiler, not the hardware. I use 32-bit ints and floats all the time on 8-bit 8051s and AVRs, both of which are among the most primitive microcontrollers in use today. They are typedefed to S32 and F32 precisely because I want a unified code base that compiles and wo…

> Honestly, there's no way I'd use an MCU that doesn't support 16-bit integer types. I've never even heard of such a thing. SHARC chips don't have any integer types smaller than 32-bits. Why would they, when they can't address any smaller than 32-bits? > I've also never heard of a 24-bit processor -- unless you're thinking of some antediluvian bit-slice processor, or something like that? 56k chips have 24-bit chars.…

SHARC chips don't have any integer types smaller than 32-bits. 56k chips have 24-bit chars. Not so common any more, but they are still kind of kicking around.

Well, those chips are DSPs, which is kind of a different horse.

Why would they, when they can't address any smaller than 32-bits?

How, for instance, would you write a TCP/IP stack, if you can't do something like this?

    struct tcp_hdr {
      PACK_STRUCT_FIELD(u16_t src);
      PACK_STRUCT_FIELD(u16_t dest);
      PACK_STRUCT_FIELD(u32_t seqno);
      PACK_STRUCT_FIELD(u32_t ackno);
      PACK_STRUCT_FIELD(u16_t _hdrlen_rsvd_flags);
      PACK_STRUCT_FIELD(u16_t wnd);
      PACK_STRUCT_FIELD(u16_t chksum);
      PACK_STRUCT_FIELD(u16_t urgp);
    } PACK_STRUCT_STRUCT;
There are a couple of right answers and a wrong answer. The wrong answer is, "You can't."

The right answer is, "Go ahead and declare the 8-bit or 16-bit integer. The compiler will do the necessary shifting and masking to simulate unaligned reads and writes, including those that straddle two word boundaries."

Another right answer might be, "You wouldn't," in the case of a dedicated DSP chip.

What I'm saying is that I've never seen a general-purpose platform where that wasn't supported. It might not be fast, of course, but embedded work is often more about interoperability than speed. (DSP being an obvious exception; I'm lucky enough not to have had to work with any old-school DSP coprocessors.)

Post reply on HN