Sujet : Re: Prioritize Performance over Correctness
De : cr88192 (at) *nospam* gmail.com (BGB)
Groupes : comp.lang.cDate : 28. Jul 2026, 22:02:12
Autres entêtes
Organisation : A noiseless patient Spider
Message-ID : <114b5jd$cdh4$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
User-Agent : Mozilla Thunderbird
On 7/28/2026 8:17 AM, bart wrote:
On 28/07/2026 13:18, Johann 'Myrkraverk' Oskarsson wrote:
On 28/07/2026 8:02 PM, bart wrote:
>
In any case, there is no conversion to int involved; it is just has to implement that comparison by whatever means works.
>
On the other hand, to the programmer, especially those not who have not
and never will read the C language standard, it does look like the p
pointer is "truncated" to an integer valued 0 or 1, in boolean context.
It doesn't look like that all. This is just testing for 'truthiness', which in many languages can be applied to data types such as strings or lists.
'Truncation' doesn't work either: you can have all-bits-zero NULL, and a value for 'p' of 0x123456 - truncating to one bit would give you zero, which is false.
There is also rarely any result - no value needs to be yielded when the result just affects control-flow.
In practice, it is mostly come sort of "compare with 0", of some sort.
In my case, typically a 64-bit compare with 0 is used, as while tagged NULLs could exist in theory, by default they don't, and it is not worth the added cost of dealing with them (either in software or hardware).
I once did experiment with supporting a few 48-bit ALU ops specifically for pointers, but these worked out as "deceptively expensive" for the CPU core. So, this idea was soon dropped (well, along with 48-bit Load/Store with a 16-bit index scale; was overly niche and too expensive to justify that niche).
Eventually noted, the main winning strategy is mostly to just use native 64-bit compare ops for everything. Earlier forms of the ISA had 32-bit compare ops that ignored the high 32 bits, but these are effectively gone in the newer variant.
Even if it does mean that in cases where one wants to ignore the high 16 bits, it is necessary to sign or zero extend from 48 bits. In the default mode, my compiler does not do so, so using tagging bits may interfere with pointer comparison. Most cases where tagging are used though are not places where the pointers are likely to be compared though.
Except well, in the experimental bounds-checked mode, which had more expensive multi-op zero-extending pointer compares (as otherwise the bounds-check tagging would unleash chaos).
Did create another mess:
Pointer subtract also needs to sign-extend;
Casting pointers to long needing to truncate the high bits;
...
This created a hassle for code that needed to twiddle the tag bits though, so, say:
x=(long)((__m64)(ptr));
With casting via __m64 as a "just give me the raw bits" thing, where __m64 and __m128 were understood as opaque "bag of bits" types, which lack any operations of their own, but can be used to do raw bit-casts in other cases where the cast would have modified the type.
In this compiler:
memcpy(&x, &y, sizeof(T)); //not ideal, various drawbacks
x=*(T*)(&y); //less bad, still not ideal
x=(T)((__mXX)y); //usually the fastest, bare register MOV.
Contrast with MSVC which treated __m64/__m128 as "some sort of unholy hackery masquerading as a struct", personally I think "type whose sole purpose is to be N bits" is a more sensible interpretation.
Then added __m32 and __m16 for other cases where I felt a need for raw-bit casting. No "__m8" though mostly for lack of relevant types to cast between (no useful distinction from "unsigned char" or similar).
They also still have tie-ins with SIMD, though had mostly ended up taking a different approach (from either MSVC or GCC), and instead effectively bolting GLSL style vectors onto C.
Where, the GLSL approach seemed closest to my typical use-cases.
Or, say, vector types:
__vec2f, __vec3f, __vec4f //2/3/4 element, float
__vec2d, __vec3d, __vec4d //2/3/4 element, double
__vec2h. __vec3h, __vec4h //2/3/4 element, half / "short float"
__vec2sf. __vec3sf, __vec4sf //2/3/4 element, half, same as above
__quatf, __quatd //quaternion
Where, types:
float : 32-bit, S.E8.M23
double : 64-bit, S.E11.M52
short float : 16-bit, S.E5.M10
long double : 128-bit, S.E15.M112
Where, vectors have elements: x/y/z/w.
Multiple or repeated elements gives a new vector or shuffle.
'_' is a filler spot containing 0.
"_Complex float" and "_Complex double" also exists as sub-types of vectors (or quaternions), where complex has i/r members (equiv x/y), and quaternions have i/j/k/r (equiv x/y/z/w). Convention would tend to put 'r' as the first component, but putting 'r' at the end works better for reusing existing SIMD handling. At present, these lack native "short float" or "long double" variants.
Typical SIMD operators being, mostly:
+, -: Pairwise add/sub
*: Pairwise mul (vec), complex/quaternion product
/: Pairwise div (vec), complex/quaternion division
^: Dot Product (scalar result)
%: Cross Product (scalar for vec2, vector otherwise)
Also sorta supports GCC style "__attribute__((vector_size(N)))" style SIMD as well.
Well, I will probably stop here...
...
Haut de la page
Les messages affichés proviennent d'usenet.
NewsPortal