How is int_fast32_t faster than other ints

I have already used stdint header, but I am trying to understand how int_fast32_t can be "faster" than other ints ? What is different about it?
Try this and tell me what you get out of it: cout<<"regular Int32_t is the same as int_fast32_t(true or false?)"<<endl<<boolalpha<<(typeid(int32_t)==typeid(int_fast32_t));
Last edited on
viliml

It just printed this:

regular Int32_t is the same as int_fast32_t(true or false?)




I don't doubt that int_fast32_t is faster - I am wondering why it is. I couldn't see anything in stdint.h that gave any clues.

Does it use registers or maybe some assembler trickery. How can one 32bit variable be quicker than another 32bit variable?

Edit: Here is the code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

#include <QtCore/QCoreApplication>
#include <iostream>
#include <typeinfo>
#include <stdint.h>

using namespace std;  //I don't normally do this


int main(int argc, char *argv[])
{
   QCoreApplication a(argc, argv);

   cout<<"regular Int32_t is the same as int_fast32_t(true or false?)"<<endl<<boolalpha<<(typeid(int32_t)==typeid(int_fast32_t));
   
   return a.exec();
}
Last edited on
My mind dredges up the idea that it's guaranteed to be the best performing int that will store at least 32 bits. It saves you looking up the size of the various int types yourself on your implementation.
Fastest integer types which are guaranteed to be the fastest integer type available in the implementation, that has at least specified number N of bits

Now, suppose that you've got 32 bits registers.
If you want a 32 bit integer, the fastest type would be one of 32bit. If you choose to use 64bits, you are wasting time.
But another one may have 64bits registers, in which case the fastest int_32t may be 64 bits

By instance, I've got
1
2
typedef long int int_fast{16,32,64}_t;
typedef signer char int_fast8_t;
¿why is char faster? IIRC there are optimized instructions to operate with 8 bits (x86).

You need to think that C is not only for PC.
Thanks for your replies.

It saves you looking up the size of the various int types yourself on your implementation.


Yes - I have used uint_64_t and others before for that reason.

I guess that the reason why the fast versions are best performing, could be a have rather technical explanation. Maybe I should be happy knowing they are faster, and one day I might learn why.

Thanks for the help, I will leave this thread open for a few days, see what happens. It's not a biggie for me, so no stress.

Cheers
I don't doubt that int_fast32_t is faster

Then start doubting! It may be implementation-defined, but on my comp, they are all the same! Try this:
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
29
#include <iostream>
#include<typeinfo>
int main()
{
	cout<<name(typeid(int8_t))<<endl<<
		name(typeid(int16_t))<<endl<<
		name(typeid(int32_t))<<endl<<
		name(typeid(int64_t))<<endl<<
		name(typeid(int_fast8_t))<<endl<<
		name(typeid(int_fast16_t))<<endl<<
		name(typeid(int_fast32_t))<<endl<<
		name(typeid(int_fast64_t))<<endl<<
		name(typeid(int_least8_t))<<endl<<
		name(typeid(int_least16_t))<<endl<<
		name(typeid(int_least32_t))<<endl<<
		name(typeid(int_least64_t))<<endl<<
		name(typeid(uint8_t))<<endl<<
		name(typeid(uint16_t))<<endl<<
		name(typeid(uint32_t))<<endl<<
		name(typeid(uint64_t))<<endl<<
		name(typeid(uint_fast8_t))<<endl<<
		name(typeid(uint_fast16_t))<<endl<<
		name(typeid(uint_fast32_t))<<endl<<
		name(typeid(uint_fast64_t))<<endl<<
		name(typeid(uint_least8_t))<<endl<<
		name(typeid(uint_least16_t))<<endl<<
		name(typeid(uint_least32_t))<<endl<<
		name(typeid(uint_least64_t))<<endl;
}
signed char
short
int
long long
signed char
short
int
long long
signed char
short
int
long long
unsigned char
unsigned short
unsigned int
unsigned long long
unsigned char
unsigned short
unsigned int
unsigned long long
unsigned char
unsigned short
unsigned int
unsigned long long

Don't bother with that silly Qt stuff for now, I'm sure that's why it didn't work in the first place!
As you can see, the normal, fast and least versions are compleatly the same!
The implementation of the name() function isn't important here, it simply transforms the output from type_info::name() to human-readable format.
Last edited on
How can one 32bit variable be quicker than another 32bit variable?
It isn't
int_fast32_t may not be 32bits.

@viliml, your style is awful.
Last edited on
I guess that the reason why the fast versions are best performing, could be a have rather technical explanation.


1
2
3
4
5
6
7
8
9
#include <iostream>
#include <stdint.h>

using namespace std;
int main()
{
  std::cout << sizeof(int) << endl;
  std::cout << sizeof(int_fast32_t);
}


On my system, this produces
4
8

because my system (and processor) is 64-bit, and as such working with an 8 byte int is faster than a 4 byte one. viliml's system presumably is different.

If you want to get into it, wander over to google with search topics like "64 bit processor". It's worth knowing about.
Last edited on
OK , I had just started a new minimal project in QtCreator ( didn't think it would make any diff)

Now that I have got rid of the Qt bits, it produces false for the output. My system is 64bit Fedora 17

I am thinking there must have been a reason for there to be a different type.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <typeinfo>
#include <stdint.h>

using  std::cout;
using  std::endl;
using  std::boolalpha;

int main()
{
   cout<<"regular Int8_t is the same as int_fast8_t(true or false?)"<<endl<<boolalpha<<(typeid(int8_t)==typeid(int_fast8_t))<<endl;

   cout<<"regular Int16_t is the same as int_fast16_t(true or false?)"<<endl<<boolalpha<<(typeid(int16_t)==typeid(int_fast16_t))<<endl;

   cout<<"regular Int32_t is the same as int_fast32_t(true or false?)"<<endl<<boolalpha<<(typeid(int32_t)==typeid(int_fast32_t))<<endl;

   cout<<"regular Int64_t is the same as int_fast64_t(true or false?)"<<endl<<boolalpha<<(typeid(int64_t)==typeid(int_fast64_t))<<endl;
   
   return 0;
}
}


regular Int8_t is the same as int_fast8_t(true or false?)
true
regular Int16_t is the same as int_fast16_t(true or false?)
false
regular Int32_t is the same as int_fast32_t(true or false?)
false
regular Int64_t is the same as int_fast64_t(true or false?)
true

Here, this is the code for the name() function, run it for the regular and fast variants of int16_t and int32_t, so we can see which types are they, and why.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#ifdef __GNUG__

#include <cxxabi.h>
#include <string>
std::string name( const std::type_info& tinfo )
{
    enum { MAX_LEN = 4096 } ;
    char buffer[ MAX_LEN ] ;
    std::size_t size = MAX_LEN ;
    int status ;
    __cxxabiv1::__cxa_demangle( tinfo.name(), buffer, &size, &status ) ;
    return status==0 ? buffer : "__cxa_demangle error" ;
}
#else

std::string name( const std::type_info& tinfo ) { return tinfo.name() ; }

#endif 
Last edited on
int_fast32_t may not be 32bits.

then what does the "32" in it stand for??
It stands for "will be capable of storing a 32 bit integer".

On my system, the fastest int type is the 64-bit int for all cases except 8-bit, where a char is used.
Last edited on
I tried cout<<sizeof(int_fast32_t); on my other, 64-bit, computer, and it was the same. Maybe becouse I use a 32-bit compiler?
> I am trying to understand how int_fast32_t can be "faster" than other ints ?

Nowhere is it said that std::int_fast32_t is "faster". It is just the "fastest" available signed signed integer type with at least 32 bits.

Other signed integer types with 32 bits or more may be equally fast; but none of them will be faster than std::int_fast32_t .


In general:

If you want a signed integer with exactly 32 bits, try using a std::int32_t. (However, such an integer type may not be available on a particular implementation.)

If you want a signed integer with at least 32 bits, use a std::int_fast32_t.

If you want the smallest signed integer with at least 32 bits available on the implementation, use a std::int_least32_t.
OK here is the new code, I added some string so we could see what was what.

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include <iostream>
#include <typeinfo>
#include <stdint.h>

using  std::cout;
using  std::endl;
using  std::boolalpha;

std::string name( const std::type_info& tinfo );

int main()
{
  

   cout<<name(typeid(int8_t))<< " int8_t " <<endl<<
           name(typeid(int16_t))<< " int16_t " <<endl<<
           name(typeid(int32_t))<< " int32_t " <<endl<<
           name(typeid(int64_t))<< " int64_t " <<endl<<
           name(typeid(int_fast8_t))<< " int_fast8_t " <<endl<<
           name(typeid(int_fast16_t))<< " int_fast16_t " <<endl<<
           name(typeid(int_fast32_t))<< " int_fast32_t " <<endl<<
           name(typeid(int_fast64_t))<< " int_fast64_t " <<endl<<
           name(typeid(int_least8_t))<< " int_least8_t " <<endl<<
           name(typeid(int_least16_t))<< " int_least16_t " <<endl<<
           name(typeid(int_least32_t))<< " int_least32_t " <<endl<<
           name(typeid(int_least64_t))<< " int_least64_t " <<endl<<
           name(typeid(uint8_t))<< " uint8_t " <<endl<<
           name(typeid(uint16_t))<< " uint16_t " <<endl<<
           name(typeid(uint32_t))<< " uint32_t " <<endl<<
           name(typeid(uint64_t))<< " uint64_t " <<endl<<
           name(typeid(uint_fast8_t))<< " uint_fast8_t " <<endl<<
           name(typeid(uint_fast16_t))<< " uint_fast16_t " <<endl<<
           name(typeid(uint_fast32_t))<< " uint_fast32_t " <<endl<<
           name(typeid(uint_fast64_t))<< " uint_fast64_t " <<endl<<
           name(typeid(uint_least8_t))<< " uint_least8_t " <<endl<<
           name(typeid(uint_least16_t))<< " uint_least16_t " <<endl<<
           name(typeid(uint_least32_t))<< " uint_least32_t " <<endl<<
           name(typeid(uint_least64_t))<< " uint_least64_t " <<endl;
   
   return 0;
}

#ifdef __GNUG__

#include <cxxabi.h>
#include <string>
std::string name( const std::type_info& tinfo )
{
    enum { MAX_LEN = 4096 } ;
    char buffer[ MAX_LEN ] ;
    std::size_t size = MAX_LEN ;
    int status ;
    __cxxabiv1::__cxa_demangle( tinfo.name(), buffer, &size, &status ) ;
    return status==0 ? buffer : "__cxa_demangle error" ;
}
#else

std::string name( const std::type_info& tinfo ) { return tinfo.name() ; }

#endif 


signed char int8_t 
short int16_t 
int int32_t 
long int64_t 
signed char int_fast8_t 
long int_fast16_t 
long int_fast32_t 
long int_fast64_t 
signed char int_least8_t 
short int_least16_t 
int int_least32_t 
long int_least64_t 
unsigned char uint8_t 
unsigned short uint16_t 
unsigned int uint32_t 
unsigned long uint64_t 
unsigned char uint_fast8_t 
unsigned long uint_fast16_t 
unsigned long uint_fast32_t 
unsigned long uint_fast64_t 
unsigned char uint_least8_t 
unsigned short uint_least16_t 
unsigned int uint_least32_t 
unsigned long uint_least64_t 


So, it's interesting. int32_t is an int, but int_fast32_t is a long.



Moschops wrote:
because my system (and processor) is 64-bit, and as such working with an 8 byte int is faster than a 4 byte one. viliml's system presumably is different.


Aha that's it. Good work, that's gold. Thanks guys for your efforts. A valuable thing learnt today.
Go on, keep going. Learn assembler for the x64 processor...
Ha !

I did some assembler years ago, using the DOS API. The main comment after the tutorials was "MY BRAIN HURTS" :D

The awkward part about the DOS API was the number of different commands, and the number of arguments for them. I have heard that modern assemblers are much better in that regard.

Also, I don't think it is worth it for me to learn that, because modern compilers are very good at optimising. I have seen some stuff where guys can do a better job than the compiler, but that is way over my head - I have enough things to learn as it is.

We did have some fun with assembly (if one could call it that), our project was to reverse engineer an exe file. The program showed a menu, each item showed a submenu, and each item did something like print numbers 1 to 20, print letters etc. It was a complete pain in the A**, going from 0's & 1's to a program, the worst was trying to figure out what variables meant. Half the class failed because they were out by 1 bit and nothing else made any sense.

Any way thanks again for your help. Cheers
Hi. Excuse me for mistimed reply.
a 64-bit CPU does not necessarily perform 64-bit variables fastest. When you call a CPU 64-bit, this is actually the number of bits in general registers that can hold data or address of memory. this action is not time wasting. (performing CPU internal actions)
Time wasting action is that when CPU wants to communicate with memory controller with address and data bus. If data bus is 64-bits length, then the fastest variable that CPU can handle is a 64-bit integer (or anything that is 64-bits long). but if data bus is not 64-bits length (e.g. 32-bits), for transferring a 64-bit value CPU needs to do the transfer twice. This is time wasting.

Talked too much LOL, excuse me. Check your CPU documentation to make sure what is the length of your CPU data bus. That would be the fastest transfer.
Thanks majidkamali1370 for your reply.

I can see now, that is the purpose of the fast types in stdint.h, to select the quickest one based on whether the system is 32 or 64 bit.

Cheers
Topic archived. No new replies allowed.