Jump to content
  • Advertisement

Archived

This topic is now archived and is closed to further replies.

Spearhawk

C file IO faster than C++ file IO?

This topic is 5578 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi, I decided to re-write the OGL for Game Programming''s tga loader with C++ style file reading instead of C style. Only now my egine takes about twice as long to load. Is C++ file IO slower, or did I do someitng wrong? The code for the loading function is below (old C style commented out). Thanks in advance
  

bool GcTarga::Load(char *fileName)
{
	//byte		ucharBad;		// Garbage byte data

	//short int	sintBad;		// Garbage short int data

	long		imageSize;		// Since of TGA image

	int			colorMode;		// 4 for RGBA or 3 for RGB

	byte		colorSwap;		// Swap variable


	// Open the TGA file

	//filePtr = fopen(fileName, "rb");

	ifstream file(fileName, ios::in | ios::binary);

	// Check to see if opened

	if(!file) {
		MessageBox(NULL, "Failed to open TGA file.", "ERROR", MB_OK);
		return false;
	}

	// Read first two bytes of data not needed

	file.seekg(sizeof(byte) * 2, ios::cur);
	//file.read((char*)&ucharBad, sizeof(byte));

	//file.read((char*)&ucharBad, sizeof(byte));


	// Read the image type

	//fread(&imageTypeCode, sizeof(byte), 1, filePtr);

	file.read((char*)&imageTypeCode, sizeof(byte));

	
	// Check that it''s the corect type

	if((imageTypeCode != 2) && (imageTypeCode != 3)) {
		fclose(filePtr);
		MessageBox(NULL, "The TGA file is not of a suported format", "ERROR", MB_OK);
		return false;
	}

	// Skip unneaded data

	//fread(&sintBad, sizeof(short int), 1, filePtr);

	//fread(&sintBad, sizeof(short int), 1, filePtr);

	//fread(&ucharBad, sizeof(byte), 1, filePtr);

	//fread(&sintBad, sizeof(short int), 1, filePtr);

	//fread(&sintBad, sizeof(short int), 1, filePtr);

	file.seekg(sizeof(short int) * 4 + sizeof(byte), ios::cur);

	// Read image dimensions

	//fread(&imageWidth, sizeof(short int), 1, filePtr);

	//fread(&imageHeight, sizeof(short int), 1, filePtr);

	file.read((char*)&imageWidth, sizeof(short int));
	file.read((char*)&imageHeight, sizeof(short int));
	
	// Read bit depth

	//fread(&bitCount, sizeof(byte), 1, filePtr);

	file.read((char*)&bitCount, sizeof(byte));

	// Read 1 byte of garbage data

	//fread(&ucharBad, sizeof(byte), 1, filePtr);

	file.seekg(sizeof(byte), ios::cur);

	// Check for depth

	if((bitCount == 24) || (bitCount == 32))
	{
		// colorMode -> 3 = BGR, 4 = BGRA

		colorMode = bitCount / 8;
		imageSize = imageWidth * imageHeight * colorMode;

		// Allocate memory for image data

		imageData = (byte*)malloc(sizeof(byte) * imageSize);

		// Read image data

		//fread(imageData, sizeof(byte), imageSize, filePtr);

		file.read((char*)imageData, sizeof(byte) * imageSize);

		// Change BGR to RGB (BGRA to RGBA)

		for(int i = 0; i < imageSize; i += colorMode)
		{
			colorSwap = imageData[i];
			imageData[i] = imageData[i + 2];
			imageData[i + 2] = colorSwap;
		}
	}
	else if(bitCount == 16)
	{
		unsigned short pixels = 0;
		int r=0, g=0, b=0;

		// RGB color mode (after conversion)

		colorMode = 3;
		imageSize = imageWidth * imageHeight * colorMode;

		// Allocate memory for image data

		imageData = (byte*)malloc(sizeof(byte) * imageSize);

		// Load in all the pixel data pixel by pixel and convert to RGB

		for(int i = 0; i < imageWidth * imageHeight; i++)
		{
			// Read in the current pixel

			//fread(&pixels, sizeof(unsigned short), 1, filePtr);

			file.read((char*)&pixels, sizeof(unsigned short));
			
			// Change the pixel to 24 bit

			b = (pixels & 0x1f) << 3;
			g = ((pixels >> 5) & 0x1f) << 3;
			r = ((pixels >> 10) & 0x1f) << 3;
			
			// Swap to RGB and save in array

			imageData[i * 3 + 0] = r;
			imageData[i * 3 + 1] = g;
			imageData[i * 3 + 2] = b;
		}
	}
	else
	{
		MessageBox(NULL, "TGA file of a not suported bpp", "Error", MB_OK);
		return false;
	}

	// Close the file

	file.close();
	//fclose(filePtr);


	return true;
}  
-- Spearhawk productions Project Andromeda

Share this post


Link to post
Share on other sites
Advertisement
were you benchmarking debug or release build?

iostreams sit on top of stdio, so they will be slightly slower than using stdio if all you need is stdio-style read and write functions. then again, you can use low-level i/o functions in <io.h> and avoid stdio buffering if you know that your operating system will buffer and/or cache data for you (win32 does, dos doesn''t).

of course, your code can be optimized on algorithm level (as usual). use one read call to read in the entire header, and use one read call per line of image data, not for every pixel. you''re killing performance by constantly invoking i/o functions when you should read a sizable block of data at once, process it, then repeat.

Share this post


Link to post
Share on other sites
quote:
if(!file)

u silly poppet u shud not use a exclimaton mark on a ifstream thing cos its not a pointer. use ifstream::is_open insted.

Share this post


Link to post
Share on other sites
quote:
Original post by YodaTheCoda
u silly poppet u shud not use a exclimaton mark on a ifstream thing cos its not a pointer. use ifstream::is_open insted.

Acording to "C++: The Complete Refernce" if(!file)... is a valid way to check if the file was opened or not. Of course, considering Schikdt's reputation it might be wrong...


Thanks niyaw. I was using debug mode for the testing, I know that it's not optimed but since both was in debug mode they should suffer as much, shouldn't they? I have now tested in release mode and the diffrence is still there, although both goes a bit faster.
Anyway, I guess I'll just re-write it as per your sugiestions, just thought it a bit odd that while I was doing the same thing in C and C++ style the C style was much faster.


--
Spearhawk productions
Project Andromeda


[edited by - Spearhawk on March 4, 2003 9:32:09 PM]

[edited by - Spearhawk on March 4, 2003 9:32:37 PM]

Share this post


Link to post
Share on other sites
No, it''s not a pointer; it''s an object. Therefore, it can have overloaded operators. In this case, the ! operator returns fail(), meaning it can be used to determine if the operation completed successfully.

Share this post


Link to post
Share on other sites
quote:
Original post by YodaTheCoda
[quote]if(!file)

u silly poppet u shud not use a exclimaton mark on a ifstream thing cos its not a pointer. use ifstream::is_open insted.

Uhm... ohkayyyyyy

I see you must have your PhD in CompSci.



Share this post


Link to post
Share on other sites
Yes, C functions happen to be faster than their C++ counterparts in most cases you''ll find, because most C++ implementations simply call the C counterpart, meaning it''s just extra over-head. If you use block reading, this difference will be small, because less calling overhead, and you''ll start getting closer to the maximum transfer rate of your hard-drive, so instead of being memory/cpu limited by having so much over-head, it will be hardware limited, meaning either way will be about as fast, since both are waiting on the hard-drive. That said, I normally use C functions to do everything, since they are normally faster. I have been using classes a lot more lately due to their ease of use (constructors, destructors, inheritance, etc), but in my raytracer, I found that putting everything in classes resulted in a HUGE drop in performance, and these were SIMPLE classes. (It was along the lines of a 400% increase when switched to straight C structs from classes). I found it very odd that classes were so slow (yes, it was tested in release mode), I always thought that they were much more efficient (and please, don''t tell me I was using them incorrectly, etc, etc, because they were VERY simple classes, constructor, destructor, some member functions that dealt with the creating/modifying/etc of the class, and the member variables... no over loaded functions, no virtual functions, etc. All I did when switching to structs, was pulled all the functions out of them, and made the functions take a struct of that type, and this resulted in a 400% increase in rendering speed on average!).

Share this post


Link to post
Share on other sites
Ready4Dis: "I found it very odd that classes were so slow (yes, it was tested in release mode), I always thought that they were much more efficient (...)"

Nope, slower, for the very reason you give: C++ classes are basically an overlay of C, so you get the penalty of the extra level of abstraction.

Same reason the display core of most games'' engines used to be written in assembly, rather than C, even though C was used for the outer game shell and logic -- well written ASM can much faster than C, because it''s one abstraction layer closer to the machine.

The "efficiency" of classes that you mentioned is really programmer efficiency: once you''re used to inheritance, polymorphism, etc, you have another tool in your toolchest. A powerful one. You can simulate all those things in C. It''s just not clean or pretty. In C++, though, it can "look" clean, and is easier to do code reviews of.

Use a code profiler, see what parts of your code are taking most of the CPU. Drop those down to C. If already there, drop further. You can still get all the benefits of OO design and have fast code, you just need to write certain functions at a lower level.

Share this post


Link to post
Share on other sites
quote:
Original post by Spearhawk
I was using debug mode for the testing, I know that it''s not optimed but since both was in debug mode they should suffer as much, shouldn''t they?


The less data you read in a single request, the more time proportionally it takes to make function calls, validate arguments, update file pointers, etc. than it takes to actually move the data from harddrive and/or disk cache to your application. Increasing number of bytes read per call will mitigate the performance hit incurred by high-level i/o code (iostream-stdio-low-level io, if you''re talking win32 api). in particular, debug builds usually have disabled optimizations and inline functions, on which stl relies to get fast execution. make sure your release build is optimized before you run tests. and do read more than one byte per call.
quote:

Anyway, I guess I''ll just re-write it as per your sugiestions, just thought it a bit odd that while I was doing the same thing in C and C++ style the C style was much faster.


if you''re looking for something really fast, use a file mapping. this will give you data directly from the operating system disk cache, eliminating many intermediate code layers and unnecessary buffer copies.

Share this post


Link to post
Share on other sites
quote:
Original post by Merle
Nope, slower, for the very reason you give: C++ classes are basically an overlay of C, so you get the penalty of the extra level of abstraction.


what exactly constitutes this "penalty" you're talking about?
quote:

Same reason the display core of most games' engines used to be written in assembly, rather than C, even though C was used for the outer game shell and logic -- well written ASM can much faster than C, because it's one abstraction layer closer to the machine.


code that writes to video memory might still be written in assembly, utilizing latest processor technologies such as sse. are you implying that the same techniques can be used with equal success for high-level tasks like file i/o?
quote:

The "efficiency" of classes that you mentioned is really programmer efficiency...


interesting. do you think you can do better than compiler at implementing virtual functions, multiple inheritance, templates, and so on? compare apples to apples. using classes vs not using classes is a non-argument; try classes vs code that achieves equivalent result without using classes.
quote:

Use a code profiler, see what parts of your code are taking most of the CPU. Drop those down to C. If already there, drop further. You can still get all the benefits of OO design and have fast code, you just need to write certain functions at a lower level.

algorithm-level optimizations have far more noticeable impact on overall code performance than assembly-level optimizations. in the current scenario, reading entire file header with a single iostreams' read() call will be faster than any assembly you can write that reads by one, two or four bytes at a time. time spent moving c++ code into assembly is better spent writing better c++ code, if not for this task, then for something else.

[edited by - niyaw on March 4, 2003 11:18:10 PM]

Share this post


Link to post
Share on other sites

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!