Sujet : Re: Computer architects leaving Intel...
De : david.brown (at) *nospam* hesbynett.no (David Brown)
Groupes : comp.archDate : 09. Sep 2024, 12:03:19
Autres entêtes
Organisation : A noiseless patient Spider
Message-ID : <vbmkln$2cmfo$1@dont-email.me>
References : 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
User-Agent : Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Thunderbird/102.11.0
On 09/09/2024 08:56, Terje Mathisen wrote:
David Brown wrote:
On 05/09/2024 19:04, Terje Mathisen wrote:
David Brown wrote:
On 05/09/2024 11:12, Terje Mathisen wrote:
David Brown wrote:
Unsigned types are ideal for "raw" memory access or external data, for anything involving bit manipulation (use of &, |, ^, << and >> on signed types is usually wrong, IMHO), as building blocks in extended arithmetic types, for the few occasions when you want two's complement wrapping, and for the even fewer occasions when you actually need that last bit of range.
>
That last paragraph enumerates pretty much all the uses I have for integer-type variables, with (like Mitch) a few apis that use (-1) as an error signal that has to be handled with special code.
>
>
You don't have loop counters, array indices, or integer arithmetic?
>
Loop counters of the for (i= 0; i < LIMIT; i++) type are of course fine with unsigned i, arrays always use a zero base so in Rust the only array index type is usize, i.e the largest supported unsigned type in the system, typically the same as u64.
>
Loop counters can usually be signed or unsigned, and it usually makes no difference. Array indices are also usually much the same signed or unsigned, and it can feel more natural to use size_t here (an unsigned type). It can make a difference to efficiency, however. On x86-64, this code is 3 instructions with T as "unsigned long int" or "long int", 4 with "int", and 5 with "unsigned int".
>
int foo(int * p, T x) {
int a = p[x++];
int b = p[x++];
return a + b;
}
;; assume *p in rdi, x in rsi
mov rax,[rdi+rsi]
add rax,[rdi+rsi+8]
ret
Yes - that's three instructions for 64-bit type T. (To be clear, I had counted the "ret" here.)
With 32-bit int for T, you need a "movsx rsi, esi" first to sign-extend the 32-bit int parameter "x" to 64 bits. (That could be different for different ABI's.) With 32-bit unsigned int for T you need an additional instruction to make sure the result of the first "x++" is wrapped as 32-bit unsigned.
>
Or you could just write sane code that matches what you want to say.
>
:-)
Of course the fine line between "smart code" and "smart-arse code" is somewhat subjective!
It also varies over time, and depends on the needs of the code. Sometimes it makes sense to prioritise efficiency over readability - but that is rare, and has been getting steadily rarer over the decades as processors have been getting faster (disproportionally so for inefficient code) and compilers have been getting better.
Often you get the most efficient results by writing code clearly and simply so that the compiler can understand it better and good object code. This is particularly true if you want the same source to be used on different targets or different variants of a target - few people can track the instruction scheduling and timings on multiple processors better than a good compiler. (And the few people who /can/ do that spend their time chatting in comp.arch instead of writing code...) When you do hand-made micro-optimisations, these can work against the compiler and give poorer results overall. This is especially the case when code is moved around with inlining, constant propagation, unrolling, link-time optimisation, etc.
Long ago, it was a different matter - then compilers needed more help to get good results. And compilers are far from perfect - there are still times when "smart" code or assembly-like C is needed (such as when taking advantage of some vector and SIMD facilities).