Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
raminasi
Jan 25, 2005

a last drink with no ice
Oh yeah, I totally forgot that C++/CLI uses different syntax for managed types :rolleye:

Adbot
ADBOT LOVES YOU

tractor fanatic
Sep 9, 2005

Pillbug
I have a thread which checks if a bool is set, and if it is, calls a function through a pointer. Once set, neither the bool nor the pointer will change for the lifetime of the thread. Another thread will set the pointer and then set the bool. I don't care about the case where the pointer has been set but the bool is not. Are there any race conditions I haven't thought of, and is it a horror to just mark both the bool and the pointer as volatile, ignoring any other threading structures?

That Turkey Story
Mar 30, 2003

tractor fanatic posted:

I have a thread which checks if a bool is set, and if it is, calls a function through a pointer. Once set, neither the bool nor the pointer will change for the lifetime of the thread. Another thread will set the pointer and then set the bool. I don't care about the case where the pointer has been set but the bool is not. Are there any race conditions I haven't thought of, and is it a horror to just mark both the bool and the pointer as volatile, ignoring any other threading structures?

Yes, it is a horror. volatile is not good enough, even for this. Never simply use volatile for anything multi-threaded related in standard C++, period.

ShoulderDaemon
Oct 9, 2003
support goon fund
Taco Defender

tractor fanatic posted:

I have a thread which checks if a bool is set, and if it is, calls a function through a pointer. Once set, neither the bool nor the pointer will change for the lifetime of the thread. Another thread will set the pointer and then set the bool. I don't care about the case where the pointer has been set but the bool is not. Are there any race conditions I haven't thought of, and is it a horror to just mark both the bool and the pointer as volatile, ignoring any other threading structures?

Aside from volatile not being the correct thing, wouldn't it be simpler to just check the pointer, and call through it if it isn't NULL? The bool does not seem required in this situation, and with only a single pointer to base your condition on you can make use of atomic operations fairly easily.

tractor fanatic
Sep 9, 2005

Pillbug

ShoulderDaemon posted:

Aside from volatile not being the correct thing, wouldn't it be simpler to just check the pointer, and call through it if it isn't NULL? The bool does not seem required in this situation, and with only a single pointer to base your condition on you can make use of atomic operations fairly easily.

Yeah I should probably just do that.

Jabor
Jul 16, 2010

#1 Loser at SpaceChem

tractor fanatic posted:

I have a thread which checks if a bool is set, and if it is, calls a function through a pointer. Once set, neither the bool nor the pointer will change for the lifetime of the thread. Another thread will set the pointer and then set the bool. I don't care about the case where the pointer has been set but the bool is not. Are there any race conditions I haven't thought of, and is it a horror to just mark both the bool and the pointer as volatile, ignoring any other threading structures?

The answer is "it's platform-dependent". volatile guarantees that the compiler won't reorder any of the volatile accesses, but doesn't put any such constraints on the hardware.

If you want your code to work fine on x86 but have a hard-to-catch race condition on any other architecture, then just using volatile is fine.

yippee cahier
Mar 28, 2005

The latest article on http://blog.regehr.org/ has a nice writeup on the subject with some interesting examples of code that may introduce problems.

OddObserver
Apr 3, 2009

tractor fanatic posted:

I have a thread which checks if a bool is set, and if it is, calls a function through a pointer. Once set, neither the bool nor the pointer will change for the lifetime of the thread. Another thread will set the pointer and then set the bool. I don't care about the case where the pointer has been set but the bool is not. Are there any race conditions I haven't thought of, and is it a horror to just mark both the bool and the pointer as volatile, ignoring any other threading structures?

You need memory barriers --- otherwise the write to the pointer may become visible -after- the write to the bool in the reading thread.

snorch
Jul 27, 2009
I'm writing C#, but this probably applies to C++ as well. Recently I got it into my head that my next 2D game project would profit in many ways by using signed distance fields to describe the levels. This means that any point in 2D space is accompanied by a positive or negative distance to the nearest surface. I've started out by writing a class with various primitive shapes, each with a function called "DistanceFrom" that returns a float representing the signed distance, like so:

code:
 class SDFPrims
    {

        public class Box // Rectangle described by top left and bottom right corners
        {
            public Vector2 TopLeft { get; set; }
            public Vector2 BottomRight { get; set; }
            public bool Inverted { get; set; }
            public float DistanceFrom (Vector2 pos)
            {
                float dist;
                dist = 0f; // TODO: Return a useful distance
                return dist;
            }
        }

        public class Circle // Circle described by center and radius
        {
            public Vector2 Center { get; set; }
            public float Radius { get; set; }
            public bool Inverted { get; set; }
            public float DistanceFrom (Vector2 pos)
            {
                Vector2 subtractedVectors = pos - Center;
                if (Inverted == false)
                {
                    return (subtractedVectors.Length() - Radius);
                }
                else
                {
                    return -(subtractedVectors.Length() - Radius);
                }
            }
...and so on.

What I would like to do is create another class with useful operations that can be performed on the signed distance fields to get useful things like gradient vectors, kind of like this:

code:
Vector2 getGradient (whatever SDF, Vector2 CenterPoint)
{
	valCenter = SDF.DistanceFrom(Centerpoint);
	valNegX = SDF.DistanceFrom(Centerpoint - 1);
	... and so on...
}
... where the variable SDF can be any class, so long as it has a DistanceFrom function. Is there any way I can easily do this?

Mr.Radar
Nov 5, 2005

You guys aren't going to believe this, but that guy is our games teacher.

snorch posted:

What I would like to do is create another class with useful operations that can be performed on the signed distance fields to get useful things like gradient vectors, kind of like this:


... where the variable SDF can be any class, so long as it has a DistanceFrom function. Is there any way I can easily do this?

It looks like this would be a good use case for extension methods. Just define your common operations (like DistanceFrom) in interfaces (grouped as appropriate; I'm assuming not all operations will make sense for all shapes), have your concrete classes implement them and write extension methods taking an instance of the appropriate interface type as the "this" parameter.

nielsm
Jun 1, 2009



snorch posted:

... where the variable SDF can be any class, so long as it has a DistanceFrom function. Is there any way I can easily do this?

I'm pretty sure the .NET megathread would be a better place for your C# questions, but anyway...

What I would do in this case was define an interface (call it BoundedObject or something) that has the DistanceFrom method, then have all your various 2D objects implement that interface. Your getGradient method would then take an object of the BoundedObject type.

Sylink
Apr 17, 2004

Are there any coding tutorial/puzzle sites similar to project euler or in the same vein that would be good for C++?

I like doing self teaching/messing around but I am terrible at coming up with crap for me to do so exercises like "create a solver for x problem" are great.

tractor fanatic
Sep 9, 2005

Pillbug
edit: welp, it turns out the compiler was inlining everything, I just did something dumb with the testing.


I'm doing a bicubic interpolation inside a loop, and I'm trying to cut some of the time off. The interpolation function itself is a templated function that calls a get function parameter, to get a bunch of points, and then returns the interpolated value. It looks like this:

code:

inline float interp(float v1, float v2, float v3, float v4, float d){
    return ...;
}

template <class getter>
float bicubic(int x, int y, float dx, float dy, getter get){
    return interp( interp( get(x - 1, y - 1), 
                           get(x    , y - 1),

    ...etc, calling get() 16 times.

}
There is a class that contains the values to be interpolated, and has a get and a get_interpolated member function.
The inner loop calls the get_interpolated function, which essentially just calls the bicubic function, like this:

code:
float get_interpolated(float x, float y){
    int intx = floor(x), inty = floor(y);
    float dx = x - intx, dy = y - inty;
    return bicubic(intx, inty, dx, dy, /* ??? */);
And I'm not sure what to do here. Right now, I'm passing it a lambda that just calls the get function, like so

code:
    return bicubic(intx, inty, dx, dy, [this](int x, int y){
        return this->get(x, y);
    });
However, if I just copy and paste the code from bicubic, but not from interp directly into the get_interpolated function, the entire loop runs about twice as fast. I'd like to keep the genericity of the bicubic function, so is there a better way to pass it a getter?

tractor fanatic fucked around with this message at 22:16 on Jan 5, 2012

Crazy Mike
Sep 16, 2005

Now with 25% more kimchee.
I'm trying to give myself another go at C++ after failing miserably in college over a decade ago. The book I'm using is Ivor Horton's Beginning Visual C++ 2010. http://www.wrox.com/WileyCDA/WroxTitle/productCd-0470500883.html Since I only have the express edition, I won't be able to get everything this book goes through but for right now I'm struggling with pointers and arrays.

For the most part the book is good. The first half of the book is learning C++ with the first part of the chapter dealing with native C++ and the second part dealing with the differences and additional features in C++/CLI.

Aside from the confusion that can cause, the only big problem I noticed was an end of chapter exercise used a function that wasn't introduced to solve a problem that couldn't be solved with previously explained material.

Right now I am on Chapter 4: Arrays, Strings, and Pointers. When I tried this book last summer this is where I gave up. I have had the most difficult time understanding pointers and this was no different. For this chapter I decided to do the native C++ exercises before reading about C++/CLI.

The first two exercises are:
1. Write a native C++ program that allows an unlimited number of values to be entered and stored in an array allocated in the free store. The program should then output the values, five to a line,followed by the average of the values entered. The initial array size should be five elements. The program should create a new array with five additional elements, when necessary, and copy
values from the old array to the new.
2. Repeat the previous exercise, but use pointer notation throughout instead of arrays.

For the first problem I can't figure out how to use new/delete for an array instead of a pointer to an array, or how to copy information to a bigger array, to the point where I don't understand what they were asking. After failing to get that for a day I tried doing it in pointer notation since using pointers was how new/delete was demonstrated. After fixing up all the errors I got this:

code:
#include<iostream>
#include<iomanip>

using std::cout;
using std::cin;
using std::endl;
using std::setw;

int main()
{
	int size = 5;			//size of initial array
	int count = 0;
	double sum = 0;
	double average = 0;

	double* pvalues(nullptr);	// declare and initialize pointer 
	double* ptemp(nullptr);		// temporarily holds old array while allocating bigger array	
	pvalues = new double[size];	// allocate array for size

	char indicator ='y';			
	for(int i=0;;i++)
	{
		
		cout << "Enter a value: ";
		cin >> *(pvalues+i);
		++count;
		sum += *(pvalues+i);
		cout << endl <<"Do you want to continue? (Press \"y\" or \"n\"): ";
			cin >> indicator;
		if(indicator != 'y' && indicator != 'Y')
			break;
		if(i==(size-1))						//all the places in the array are filled
		{
			size+=5;					//add five more to size
			ptemp = new double[size];			//array five more than previous
			for(int j=0;j<(size-5);j++)
			{
				*(ptemp+j) = *(pvalues+j);		//fill temp array with old data;
			}
		delete [] pvalues;					//free up memory
		pvalues = new double[size];				//now we have the array reset...
			for(int k=0;k<(size-5);k++)
			{
				*(pvalues+k) = *(ptemp+k);		//fill  array with temp data;
			}
			delete [] ptemp;				//free up memory
		}
	}



		for(int l=0; l<count;l++)
		{
			if(l % 5 == 0)			//New line on 1st and every fifth line
				cout << endl; 
		cout << setw(10) << *(pvalues+l);	// pointer notation
		}
		delete [] pvalues;			//free up memory
		average = (sum/static_cast<double>(count));
		cout << endl << "The average of the values is: " << average << endl; 
	return 0;
}
This seems to work and I'm amazed I got this far, but still can't figure out what the first problem is looking for. The last time I got stuck I downloaded the code from the site and didn't really understand it so I figured I would ask the collective knowledge here... What was the first problem asking for? Is there anything blatantly wrong with the code above? If I get stuck in the future should I just download the code and try to decipher it? Thanks in advance for your help.

tractor fanatic
Sep 9, 2005

Pillbug
I think they just wanted you to use array notation for the first one, so pvalues[i] instead of *(pvalues+i)

Your code is inefficient in that it makes a temporary array and then discards it. You don't need to make a temporary array and fill it will the old data, then make a new array and copy the data from the temporary. You can just use the temporary directly as the new array, saving you a bunch of copies.

Not So Fast
Dec 27, 2007


I'm working on a C++/CLI project that incorporates some older MFC dialogs for legacy purposes. I also have a new dialog that inherits from the System::Windows::Forms Form class. Is there a way to get the new dialog to be a child of one of the older MFC dialogs, so that modal properties of blocking access to the parent window works properly?

So far, I've tried using NativeWindow / Control's FromHandle function together with the CWnd HWND handle and passing that into the Form's ShowDialog function. This doesn't seem to work though.

Crazy Mike
Sep 16, 2005

Now with 25% more kimchee.

tractor fanatic posted:

I think they just wanted you to use array notation for the first one, so pvalues[i] instead of *(pvalues+i)

Your code is inefficient in that it makes a temporary array and then discards it. You don't need to make a temporary array and fill it will the old data, then make a new array and copy the data from the temporary. You can just use the temporary directly as the new array, saving you a bunch of copies.

Indeed, I tried to envision the array like a bucket and the loop like a kid filling the bucket up with water. When the bucket gets full he needs a new bucket. So how do we tell the kid to fill up the temp bucket instead of getting him a new bucket with his name on it. Why not peel his nametape of the old bucket and put on the temp bucket instead of trading the old bucket for a new bucket.
So..
code:
pvalues = new double[size];				//now we have the array reset...
			for(int k=0;k<(size-5);k++)
			{
				*(pvalues+k) = *(ptemp+k);		//fill  array with temp data;
			}
becomes
code:
pvalues = ptemp;		//Put the nametape on the new bucket...
Of course I had one more problem...
Where do I put the delete [] ptemp; line?
If I put it after pvalues = ptemp or after the end; it spits out garbage data or crashes the program. If I comment it out, the program works fine. I gave up and dowloaded the code. The author does it like this
code:
// Soln4_2.cpp
#include <iostream>
#include <iomanip>
using std::cin;
using std::cout;
using std::endl;
using std::setw;


int main()
{
  int arraySize(5);
  double* values = new double[arraySize];    // Initial array to store values
  double* temp(nullptr);                     // Temporary store for pointer to new array
  double inputValue(0.0);                    // Current input value
  int index(0);                              // Index to the values array
  for(;;)
  {
    // Read the next value
    cout << "Enter a value or 0 to end: ";
    cin >> inputValue;

    // If it is 0 we are done so exit the loop
    if(inputValue == 0.0)
      break;

    *(values+index++) = inputValue;          // Store the value and increment index

    if(index == arraySize)                   // If index reaches arraySize
    {                                        // We need a bigger array...
      arraySize += 5;                        // Increase the array size value
      temp = new double[arraySize];          // Allocate the new array

      for(int i = 0 ; i<index ; i++)         // Copy old array elements to new
        *(temp+i) = *(values+i);

      delete[] values;                       // Delete the old array
      values = temp;                         // Store address of new array
      temp = nullptr;                        // Reset temp to null
    }
  }
  double average = 0.0;
  for(int i = 0 ; i<index ; i++)
  {
    average += *(values+i);
    cout << setw(10) << *(values+i);
    if((i+1)%5 == 0)
      cout << endl;
  }
  cout << endl
       << "Average is " << average/index
       << endl;
  delete[] values;                           // Release memory for array
  values = nullptr;                          // Reset to null

  return 0;
}
and he doesn't have a delete [] temp; line. After wondering why we don't need this line and writing this all out it seems that by deleting the old values array and storing the address of the new array, when you delete that array when it is called values it has the same effect of deleting the temporary array that is now being used.

I have a feeling from here on out this is going to be a long difficult book.

roomforthetuna
Mar 22, 2005

I don't need to know anything about virii! My CUSTOM PROGRAM keeps me protected! It's not like they'll try to come in through the Internet or something!

Crazy Mike posted:

and he doesn't have a delete [] temp; line. After wondering why we don't need this line and writing this all out it seems that by deleting the old values array and storing the address of the new array, when you delete that array when it is called values it has the same effect of deleting the temporary array that is now being used.
Think of it like your buckets and it makes sense - you throw out the old, smaller bucket with delete [] values, in his code. Now what are you planning to throw out with delete [] anything else? The only other bucket you have is the one that's now full of all the data. All you did was stick a new nametag on it, and if you tried delete [] temp, you're saying "throw out that bucket that has both nametags on it."

Dicky B
Mar 23, 2004

Remember that "temp" and "values" are just names, nothing more. You cannot "delete" a pointer, only the memory that it points to. So when you say delete[] values; it's freeing the memory which the name "values" currently points to.

I always hated all the analogies people liked to throw at me when learning about pointers. I finally understood it once somebody explained it to me in literal terms.

So here's my explanation if it helps:
code:
1. double* values = new double[5];
2. double* temp = new double[10];
3. for(int i = 0; i < 5; i++)
4.     *(values+i) = i;
5. for(int i = 0; i < 5 ; i++)
6.     *(temp+i) = *(values+i);
7. delete[] values;
8. values = temp;
9. temp = nullptr;
Line 1: double* values = new double[5];
"values" points towards some newly allocated memory. Call it memory A. The contents of memory A is uninitialized. Enough space exists for 5 doubles.

Line 2: double* temp = new double[10];
"temp" points towards some newly allocated memory. Call it memory B. The contents of memory B is uninitialized. Enough space exists for 10 doubles.

code:
 --------
| values |------> memory A (*,*,*,*,*)
 --------
 --------
| temp   |------> memory B (*,*,*,*,*,*,*,*,*,*)
 --------
Line 3-4: for(int i = 0; i < 5; i++) *(values+i) = i;
The contents of memory A is assigned to 0,1,2,3,4.

code:
 --------
| values |------> memory A (0,1,2,3,4)
 --------
 --------
| temp   |------> memory B (*,*,*,*,*,*,*,*,*,*)
 --------
Line 5-6: for(int i = 0; i < 5 ; i++) *(temp+i) = *(values+i);
The contents of memory A is copied into memory B. The same data now exists in both memory locations.

code:
 --------
| values |------> memory A (0,1,2,3,4)
 --------
 --------
| temp   |------> memory B (0,1,2,3,4,*,*,*,*,*)
 --------
Line 7: delete[] values;
Memory A is freed. That memory space is now no longer owned by the program. Memory B remains untouched.

code:
 --------
| values |------> memory A ()
 --------
 --------
| temp   |------> memory B (0,1,2,3,4,*,*,*,*,*)
 --------
Note how "values" still points at memory location A even though that memory is no longer owned by the program. At this point, you can still attempt to access that memory if you want. Doing so will cause the program to crash on most systems.

Line 8: values = temp;
The name "values" now points to Memory B. We don't have to worry about memory A anymore.

code:
 --------
| values |--------        memory A ()
 --------        |
 --------        v
| temp   |----> memory B (0,1,2,3,4,*,*,*,*,*)
 --------
Line 9: temp = nullptr;
"temp" stops pointing to memory B. The final state is this:

code:
 --------
| values |------> memory B (0,1,2,3,4,*,*,*,*,*)
 --------   
 --------    
| temp   | = nullptr
 --------

Dicky B fucked around with this message at 03:53 on Jan 8, 2012

nielsm
Jun 1, 2009



Good explanation, except for one thing:

Dicky B posted:

Line 7: delete[] values;
Memory A is freed. That memory space is now no longer owned by the program. Memory B remains untouched.

code:
 --------
| values |------> memory A ()
 --------
 --------
| temp   |------> memory B (0,1,2,3,4,*,*,*,*,*)
 --------
Note how "values" still points at memory location A even though that memory is no longer owned by the program. At this point, you can still attempt to access that memory if you want. Doing so will cause the program to crash on most systems.

I think you aren't explicit enough about values being invalid after this. Sure, the variable has a value (points somewhere), but it points to invalid memory, it has an invalid value.
I would change your illustration to:

code:
 --------
| values |------> invalid (previous memory A address)
 --------
 --------
| temp   |------> memory B (0,1,2,3,4,*,*,*,*,*)
 --------
Then completely remove memory A () from the following. Simply because memory A is gone after the deletion.

tractor fanatic
Sep 9, 2005

Pillbug
Also, don't use l as an array indexer. It gets confusing fast.

pseudorandom name
May 6, 2007

Only if you use a terrible font.

tractor fanatic
Sep 9, 2005

Pillbug
e:wait this was stupid anyways

tractor fanatic fucked around with this message at 22:51 on Jan 7, 2012

Crazy Mike
Sep 16, 2005

Now with 25% more kimchee.
Thanks for your help explaining those things. The next exercise was a bit simpler but still exposes some problems with this book.

Declare a character array, and initialize it to a suitable string. Use a loop to change every other character to uppercase. Hint: In the ASCII character set, values for uppercase characters are 32 less than their lowercase counterparts.

Now I remember earlier the book mentioning string functions to see if the character is a letter or change its case, however looking back those Char::IsLetter and Char::ToUpper()are for C++/CLI programs. The book mentions using the cstring header for certain string functions but in his solution he uses the string header. I just checked out his answer
code:
// Soln4_3.cpp
#include <iostream>
#include <string>
using std::cout;
using std::endl; 

int main()
{
   char str[] = "Doctor Livingstone, I presume?";
   cout << str << endl;                     // Output the string

   for (unsigned int i = 0; i < strlen(str); i += 2)
   {
      if (str[i] >= 'a' && str[i] <= 'z')   // If it's lowercase letter
         str[i] -= 32;                      // change to uppercase
   }

   cout << str << endl;                     // Output the modified string
   return 0;
}
I got something similar but more long winded before I started to wonder if I could change it to pointer notation. I do have one problem with that...
code:
#include<iostream>
#include<cstring>
using std::cout;
using std::endl;
using std::strlen;

int main()
{
	int count = 0;						//letter count
	int length = 0;						//string length
	char* saying("Who am I and why am I here, Oh well, whatever.");
	length = strlen(saying);
		for(int i =0;i<=length;i++)
		{
		
			if((*(saying+i) >= 'a' && *(saying+i) <= 'z') || (*(saying+i) >= 'A' && *(saying+i) <= 'Z'))
			{
				count++;
				if((*(saying+i) >= 'a' && *(saying+i) <= 'z') && count%2 == 1)	//every other letter...
					*(saying+i) -= 32;	// why does this crash the program?
			}
		}
		
		cout << endl;
		for(int i = 0;i<=length;i++)
		{
		
			cout << *(saying+i);
		}
		cout << "Length = " << length << endl;
	return 0;
}
Now aside from the fact that in his book examples he uses the #include <cstring> and using std::strlen lines but is different in his downloaded code, why does my *(saying+i) -= 32; line not work like the str[i] -= 32; line? Instead it crashes the program after compiling leaving me to wonder if that logically shouldn't work or I have a grammar error somewhere.

roomforthetuna
Mar 22, 2005

I don't need to know anything about virii! My CUSTOM PROGRAM keeps me protected! It's not like they'll try to come in through the Internet or something!

Crazy Mike posted:

why does my *(saying+i) -= 32; line not work like the str[i] -= 32; line?
Those lines work the same, but char* saying("Who am I and why am I here, Oh well, whatever."); does not work the same as char str[] = "Doctor Livingstone, I presume?"; - yours initializes a pointer such that it points to a constant string, hence the error when you try to modify it - the array initialization in the working code copies the constant string into the array on the stack, so modifying it there works.

Bisse
Jun 26, 2005

Profiling my first stab at a voxel engine. It's fun but it feels like I have no clue at all what are the actual performance impacts on a C++ program. I model the world with 16x16x64 chunks of voxels defined like:
code:
typedef unsigned char uint8; // Using uint8 due to storing RGBA values as 0-255 range values

class VoxelChunk
{
public:
    uint8 voxels[CHUNK_WX][CHUNK_WY][CHUNK_WZ][4];
    
    VoxelChunk() : voxels() { };
    void clear( uint8 *clearWith );
    uint8* getVoxel( unsigned int x, unsigned int y, unsigned int z );
    void setVoxel( unsigned int x, unsigned int y, unsigned int z, uint8 *src );
};
Results from profiling on a series of chunks (with OpenGL rendering disabled, just iterating over the voxels and reading a value from each) give me 10FPS for a 64x128x128 world and show that by far the slowest function is my uint8* VoxelChunk::get(x,y,z), taking 85-90% of CPU time, and it looks like this in the report from Very Sleepy:
code:
        uint8* VoxelChunk::getVoxel( unsigned int x, unsigned int y, unsigned int z )
27.22s	{
2.94s	    return &voxels[x][y][z][0];
0.86s	}
I'm unsure if it's the function call being slow due to three arguments, or if the 4D-array is doing it. The set function is similiar and equally slow. I'm wondering how to improve on this, did I do any serious design oversight?
Would it be better to do something like this instead:
code:
class VoxelChunk
{
    uint8 voxels[CHUNK_WX * CHUNK_WY * CHUNK_WZ * 4];
    ....
};

uint8* VoxelChunk::getVoxel( unsigned int x, unsigned int y, unsigned int z )
{
    return &voxels[(x << 10) | (y << 6) | (z << 2)];
}
Or maybe I should make voxels into a struct instead of a 4-value array, and the array in the chunk would then be a series of structs?

Bisse fucked around with this message at 01:37 on Jan 10, 2012

Captain Cappy
Aug 7, 2008

Are you in release mode when you're testing the speed? I'm pretty sure a compiler knows all the tricks of multi-dimension array lookups that you would try. Also, when I've run very sleepy it doesn't even measure the get functions because they're usually inlined by the compiler already. Maybe four dimensional array lookup puts it past the threshold of simple functions though.

Other than that, maybe your get function is called by too many things and you should look into reducing the number of calls.

nielsm
Jun 1, 2009



Make sure that function gets inlined. Compared to the actual body of it, all the prologue and calling stuff is going to be really heavy. All the actual body of the function does is calculate a pointer value, it doesn't even fetch (dereference) anything -- well apart from this->voxels.

Move the function definition into the class declaration, like this:
code:
typedef unsigned char uint8; // Using uint8 due to storing RGBA values as 0-255 range values

class VoxelChunk
{
public:
    uint8 voxels[CHUNK_WX][CHUNK_WY][CHUNK_WZ][4];
    
    VoxelChunk() : voxels() { };
    void clear( uint8 *clearWith );
    uint8* getVoxel( unsigned int x, unsigned int y, unsigned int z )
    {
        return voxels[x][y][z];
    }
    void setVoxel( unsigned int x, unsigned int y, unsigned int z, uint8 *src )
    {
        // ...
    }
};
That makes it implicitly inlineable and should rid you of the call and prologue for every call.

On a side note, #include <stdint.h> and use standard types such as uint8_t defined by your compiler vendor. (Except if you use MSVC 2008 or an earlier MSVC, they don't ship stdint.h but there exists a third-party implementation of it for those compilers.)

Paniolo
Oct 9, 2007

Heads will roll.
edit: misinterpreted what you were saying. But I'll leave this bit: He's obviously profiling a debug build.

Paniolo fucked around with this message at 05:15 on Jan 10, 2012

Bisse
Jun 26, 2005

:psyduck: HOLY MOTHER OF gently caress WE HAVE LIFTOFF

Without inlined get/set function: 10 FPS
With inlined get/set: 700 FPS

Only a 7000% performance boost, tyvm


EDIT: Seems I'll need to turn this guy into a #define to be able to do any debugging at all. (Since Visual C++ 2010 inline optimization is incompatible with its debug mode)

Bisse fucked around with this message at 23:35 on Jan 10, 2012

nielsm
Jun 1, 2009



Bisse posted:

EDIT: Seems I'll need to turn this guy into a #define to be able to do any debugging at all. (Since Visual C++ 2010 inline optimization is incompatible with its debug mode)

Don't profile your debug builds.
Don't debug your profiling builds.


Debugging is identifying incorrect behaviour, profiling is identifying performance bottlenecks. You generally shouldn't need performance to be able to identify your bugs, and you should only start profiling when you know your code is otherwise correct.

roomforthetuna
Mar 22, 2005

I don't need to know anything about virii! My CUSTOM PROGRAM keeps me protected! It's not like they'll try to come in through the Internet or something!

nielsm posted:

Debugging is identifying incorrect behaviour, profiling is identifying performance bottlenecks. You generally shouldn't need performance to be able to identify your bugs, and you should only start profiling when you know your code is otherwise correct.
You do need some performance for debugging. 10fps is pretty nasty.

Agreeing that obviously you don't want to profile a debug build, but when not inlining a function takes you from 700fps to 10fps, it's a bitch that VC++ won't inline it in debug mode.

chglcu
May 17, 2007

I'm so bored with the USA.
You can enable inlining in debug builds, it just defaults to disabled. It's under Project Properties -> Configuration Properties -> Optimization -> Inline Function Expansion. Not sure if it negatively impacts the debugging of those functions or not, though.

Edit: If it still doesn't get inlined with that option, you can try also using __forceinline instead of inline.

chglcu fucked around with this message at 00:38 on Jan 11, 2012

Princess Kakorin
Nov 4, 2010

A real Japanese Princess
Any ideas on how I could access information from a structure contained in a structure that inherits from an outside structure?

code:

struct A
{
   string myString;
};

struct B
{
   struct C : A
   {
      int myInt;
   };

};
code:

A *myStruct = new B::C;
//myInt is not accessible, even though B::C is derived from A

raminasi
Jan 25, 2005

a last drink with no ice
The nested structures aren't your problem. As don't have a myInt field. To do exact what you're asking you'll have to downcast (or more generally, access that field through a type that actually has it - you could just use a B::C * to begin with).

This seems like one of those "what are you actually trying to do?" questions, though.

That Turkey Story
Mar 30, 2003

Princess Kakorin posted:

Any ideas on how I could access information from a structure contained in a structure that inherits from an outside structure?

As GrumpyDoctor said, this doesn't have to do with nested types, you are misunderstanding inheritance. In the code you posted you are accessing an instance of C via a pointer to A. There is no "myInt" in A so you get a compile error. If you need to access myInt, then the object through which you're accessing the instance of C needs to know it's a C (either use a pointer or reference to a C, or a C object itself).

Princess Kakorin
Nov 4, 2010

A real Japanese Princess
WELP.
I actually have no idea what I was thinking when I made the post, or why I thought inheritance worked that way. But thanks guys, I have it figured out now!

Bisse
Jun 26, 2005

prolecat posted:

You can enable inlining in debug builds, it just defaults to disabled. It's under Project Properties -> Configuration Properties -> Optimization -> Inline Function Expansion. Not sure if it negatively impacts the debugging of those functions or not, though.

Edit: If it still doesn't get inlined with that option, you can try also using __forceinline instead of inline.
The problem is VC++ 2010 refuses to compile saying the option /O-something (inline) is incompatible with the option /Z-something (debug). AFAICT it does that for all optimization choices. I'm using Express though, dunno if that makes a difference.

chglcu
May 17, 2007

I'm so bored with the USA.
That's odd. It didn't complain for me when I tried it out before posting. I was using Professional though.

Edit: Just tried it again, and I did get an error, so don't know WTF was up yesterday. Changing the debug format to /Zi instead of /ZI seems to get rid of the error, though it will disable edit and continue.

Edit #2: Just to make sure, checked the assembly and it did inline correctly.

chglcu fucked around with this message at 21:00 on Jan 11, 2012

Adbot
ADBOT LOVES YOU

TheMover
Jun 24, 2010
For background: I'm programming a robot, I want the robot to execute tasks on its own but allow the user to type some command into the console and be able to take control from it at any time. The first way I've though of this is have a loop going around constantly (Check commands, execute robot actions, print data) but is it possible to do that in a way such that it ignored the "check commands" step if nothing has been entered? Maybe I'm being dumb, but any cin >> operation is just gonna pause it all, right?

The other option, of course, is threading. If that's the way I should go instead, could anyone suggest a good library that's nice and simple for this, any background documents I should maybe read or just any tips in general?

Many thanks in advance everyone!

e: C++ y'all!

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply