Jump to content

  • Log In with Google      Sign In   
  • Create Account

Khatharr

Member Since 24 Apr 2010
Offline Last Active Private

#5304963 For Loop Max Bug

Posted by on 09 August 2016 - 02:19 PM

What I'm talking about is being able to write a struct/class that would let you do things like this:

rect.x = 4;
rect.right = 12;
cout << rect.width; //prints out 8

You can do this with functions in C++, of course, but it doesn't feel the same. What I want is to be able to declare this way:

struct Rect {
  int x, y, width, height;
  int left   property;
  int right  property;
  int top    property;
  int bottom property;
};

Where the 'property' would make it expect you to define a get/set for the variable something like this:

int Rect::operator right=(int value) { width = value - x; }
int Rect::operator right()           { return x + width; }



#5304528 Multithreaded Game Engine Architecture With Data Oriented Design

Posted by on 07 August 2016 - 02:39 PM

https://www.unrealengine.com/what-is-unreal-engine-4




#5304411 Beginner Question - Good Practice

Posted by on 06 August 2016 - 02:56 PM

Added comments.




#5304400 Beginner Question - Good Practice

Posted by on 06 August 2016 - 02:09 PM

This may give you a more realistic idea of the overhead. Keep in mind that testing should be done in release build without a debugger attached. When viewing the results consider the number of iterations in comparison to the number of allocations/releases in your program. (100 million is a lot.)

#include <iostream>
#include <memory>
#include <chrono>

//this templated struct allows you to create POD (plain old data) objects of arbitrary size
template<size_t SIZE>
struct POD {
  char doota[SIZE];
};

//same idea but with some busywork constructor and destructors
template<size_t SIZE>
class Complex {
public:
  Complex() {
    for(size_t i = 0; i < SIZE; i++) { doota[i] = 1; }
  }

  ~Complex() {
    for(size_t i = 0; i < SIZE; i++) { doota[i] = 0; }
  }

private:
  char doota[SIZE];
};

//here we define some terms for convenience. this use of 'using' is similar to typedef, but easier to read and it works for templates
const size_t LARGE = 1024;
const size_t SMALL = 1;
using LargePOD = POD<LARGE>;
using SmallPOD = POD<SMALL>;
using LargeComplex = Complex<LARGE>;
using SmallComplex = Complex<SMALL>;

//this is a template function that uses manual memory management to create
// and delete 'iterations' number of objects for type T (the type is passed
// in as a template argument at the call site)
template<class T>
void manualMMTest(size_t iterations) {
  for(size_t i = 0; i < iterations; i++) {
    delete new T;
  }
}

//same thing using unique_ptr
template<class T>
void uniqPtrMMTest(size_t iterations) {
  for(size_t i = 0; i < iterations; i++) {
    std::make_unique<T>();
  }
}

//and shared_ptr
template<class T>
void sharPtrMMTest(size_t iterations) {
  for(size_t i = 0; i < iterations; i++) {
    std::make_shared<T>();
  }
}

//this is a high-precision timer class that should be
//fairly simple to understand by reading it
class Timer {
  using moment = std::chrono::time_point<std::chrono::high_resolution_clock>;

public:
  void startFromZero() {
    start = std::chrono::high_resolution_clock::now();
  }

  double getElapsedSeconds() {
    moment stop = std::chrono::high_resolution_clock::now();
    std::chrono::nanoseconds elapsed = stop - start;
    return elapsed.count() / 1000000000.0;
  }
  
private:
  moment start;
};

//this runs a batch of tests using the previously defined functions and prints the results
template<class T>
void doBatch(size_t iterations) {
  Timer timer;
  double seconds = 0;

  timer.startFromZero();
  manualMMTest<T>(iterations);
  seconds = timer.getElapsedSeconds();
  std::cout << "  Manual management: " << seconds << " seconds.\n";

  timer.startFromZero();
  uniqPtrMMTest<T>(iterations);
  seconds = timer.getElapsedSeconds();
  std::cout << "  unique_ptr: " << seconds << " seconds.\n";

  timer.startFromZero();
  sharPtrMMTest<T>(iterations);
  seconds = timer.getElapsedSeconds();
  std::cout << "  shared_ptr: " << seconds << " seconds.\n\n";
}

//and this runs it all
int main() {
  const size_t ITERATIONS = 100000000;

  std::cout << "Begin testing on int (using " << ITERATIONS << " iterations):\n";
  doBatch<int>(ITERATIONS);

  std::cout << "Begin testing on small POD objects (using " << ITERATIONS << " iterations):\n";
  doBatch<SmallPOD>(ITERATIONS);

  std::cout << "Begin testing on large POD objects (using " << ITERATIONS << " iterations):\n";
  doBatch<LargePOD>(ITERATIONS);

  std::cout << "Begin testing on small complex objects (using " << ITERATIONS << " iterations):\n";
  doBatch<SmallComplex>(ITERATIONS);

  std::cout << "Begin testing on large complex objects (using " << ITERATIONS << " iterations):\n";
  doBatch<LargeComplex>(ITERATIONS);


  std::cin.get();
}





#5304387 Beginner Question - Good Practice

Posted by on 06 August 2016 - 01:12 PM

However I meant smart pointers (which deletes itself when the block falls of the stack eg.: shared pointer) being 8-9 times more performance heavy, than manually handled pointers

 

Pics or it didn't happen.

 

 

Obviously the latter, as you described. I totally agree. Btw here is my source for that smart pointer overhead info, tho, he did his own tests: https://youtu.be/obrB-Rei9qA?t=3m44s

 

Saying out loud on an internet video "this is eight to nine times slower" does not constitute testing, even if you say, "after all, I'm more like a C programmer" at the end.




#5304265 Beginner Question - Good Practice

Posted by on 05 August 2016 - 05:00 PM

Pointer deferencing is effectively free if you're not getting goofy with multiple indirection in a tight loop. The CPU has an address lookup unit that does any needed pointer math in the pipeline before the address is actually needed. As long as you don't overload that unit (which is actually hard to do) you should never see a performance impact just from dereferencing.

 

You may be thinking of cache coherency, which is a matter of how you traverse memory rather than how you reach the memory you're traversing. That can be impactful for things like linked lists because over time the list may end up referring to objects that are scattered all over memory, which means that the cache can become worthless and you have to wait on the bus for every node that misses, which is vvveeerrryyy ssslllooowww. Using a container like a vector is very fast in comparison because the data is contiguous in memory, which means that the next element is very likely to be in the cache where it can be accessed quickly and easily.

 

Traversing a linked list can also be expensive because looking through a pointer to find a pointer to find a pointer (etc) can indeed overload the lookup unit.




#5304084 Best Way To Comment Code Without Cluttering

Posted by on 04 August 2016 - 06:04 PM

I almost never comment on the "how" of things, but I occasionally comment on the "why" of things. Most of the times this means a single line of code where needed, about 2% of my lines consist of comments. If you ever get back to your code after a while and you don't remember a thing, the code will still tell you how it works eventually, but it might not always make clear why you chose to do some thing in some particular way.

 

QFE.

 

I generally try to pretend that I'm picking up a project that I was pair programming on a few days before, and I write comments to the other programmer. If the "what" of my code is not obvious then there's a problem with the code and I mark it as needing a rewrite. I leave "why" comments wherever they're useful, which usually happens at the top of a function definition (because madness should be encapsulated). In some cases I comment declarations, and will sometimes include information about guarantees if relevant.

 

It makes me mad when I have to wade through worthless comments to find code, and it irritates me to maintain comments when changing code, so I try not to inflict those kinds of problems on myself or on other people.




#5303009 Xml Parser, C++

Posted by on 28 July 2016 - 05:07 PM

I wrote a partial xml parser (for Tiled's tmx files) with std::regex yesterday, lol. Knowing what fields are going to be (or should be) present makes it a lot easier to work on.




#5301001 Collision Meshes / Aabb Generation

Posted by on 16 July 2016 - 02:23 PM

AABBs are for broad phase detection. It sounds like you're trying to use them for narrow phase.

 

http://www.metanetsoftware.com/dev/tutorials




#5300946 Questions About Storing Opcodes With Operands

Posted by on 15 July 2016 - 11:08 PM

In x86 the registers are

 

general purpose registers:

EAX - free to use, holds function return values

EBX - free to use

ECX - free to use

EDX - free to use

these are 32 bit registers, but can be referred to as 16 bits AX,BX,CX,DX or 8 bits AH/AL/BH/BL/CH/CL/DH/DL

 

pointer registers:

ESI - source index, which is paired with...

EDI - destination index - these two are used for rapidly advancing pointer values when performing work that reads from one buffer and writes to another

EBP - the base pointer - points to the base of the stack frame, convenient for referring to function arguments or local variables, but not really required since the compiler can work out the math from...

ESP - the stack pointer - points to the top of the stack

EIP - index pointer - holds the address of the next instruction to be executed (after the currently executing one), can't be mov'd to but jmp or call can set its value

 

segment registers (used by virtual memory model):

CS - code segment - pointer to page that holds the unpacked exe file (or whatever. the bytecode is in this page)

DS - data segment - pointer to page that holds the heap

SS - stack segment - pointer to page that holds the stack

ES - for an extra page

FS - for an extra page

GS - for an extra page

 

flags register:

EFLAGS - a register used to store binary flags set by various common operations - allows for basic branching logic by remembering the results of comparisons, etc. For example, when you use greater than/less than in C you're setting the SIGN flag, and a conditional jump statement like JLE (jump if less than or equal to) will read the sign flag when deciding whether to jump, so the statement:

 

if(x < 2) {

  A

}

B

 

could theoretically be compiled as:

 

CMP 2,x //compare 2 to x - this performs an "implicit subtraction", that is it executes "2 - x" but does not store the result in a register, though it does set the sign bit and zero bit in EFLAGS according to the result of the subtraction.

JLE B //if 2 was less than or equal to x then jump ahead to B, otherwise just keep going

A //compiled code of A

B //compiled code of B

 

There are also usually sets of special registers depending on the feature set of the architecture. (usually things like SSE or MMX)

 

Old instruction set reference and general system explanation: http://css.csail.mit.edu/6.858/2015/readings/i386.pdf

Very old discussion of exe format: https://msdn.microsoft.com/en-us/library/ms809762.aspx

 

Modern resource (64 bit architecture): http://www.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html




#5300810 Please Help Stuck On What To Do Here (C++)

Posted by on 14 July 2016 - 06:34 PM

This code will not compile. I see several spelling and syntax errors just at a casual glace. Before pursuing the exercise how about correcting the code that you have here and ensuring that your additions work correctly? (hint - the additions are not correct) For exercises like this you really ought to be working in some form of development environment where you're regularly compiling and checking your code. If you don't have one then here are some things you can use:

 

VS2015 (Microsoft's Windows IDE): https://www.visualstudio.com/en-us/visual-studio-homepage-vs.aspx (click on "Download Community 2015")

ideOne (online compiler - runs in browser): https://ideone.com/

 

Further, this seems like homework, especially since I can't imagine any other reason to reach this point without being able to at least venture a guess at how to implement this feature. If you understand how the linked list works (which is probably the objective of the section) then you really should have no difficulty with this whatsoever.

 

Edit: Yeah. I knew this code was familiar. This is from Beginning C++ Through Game Programming. Read through the section again (starting from "Introducing the Game Lobby Program") and get clear on how the system works. Once you've done that, remember that the container (the lobby) starts out empty and you add and remove elements (players) one at a time. You should be able to reason about how to keep a tail pointer up-to-date here. If not then you need to continue to review the program until you understand how it works.

 

I think it would be doing you a grave disservice to simply answer this question directly. Those questions exist to ensure that you understand the material.




#5300766 Questions About Storing Opcodes With Operands

Posted by on 14 July 2016 - 12:43 PM

umm I really don't know what purpose the call stack is. is that true the call stack memory is in RAM and CPU provides stack operations?

 

 

Yes and yes. The call stack is pushed when you enter a function and it contains that function's local variables. The 'stack frame' for that function also includes some data about where the function was called from so that when the function returns the stack is popped and it has the information about where to go to in the bytecode in order to return to the calling function.

 

If you compile some simple, minimal functions in Visual Studio you can put a breakpoint in the program and run it, then when the debugger stops at the breakpoint you can right-click on the code and 'View disassembly' to get a look at the assembly that pushes and pops the stack (the instructions in x86 are 'call' and 'ret'). There's an option to also view the compiled bytecode for each instruction.

 

You can also add registers to the variable watch list, and view the call stack in the memory viewer. If you take things one instruction at a time you should be able to work out what it's doing. Just note that the stack 'grows' backward in memory. That is, it starts at a higher address and when it's pushed to the new data goes into lower addresses.

 

86c0b220ca.png




#5300762 Newbie Question About Std:vector

Posted by on 14 July 2016 - 12:17 PM

That 'initialize' is making my teeth grind.

 

drwhat, do you know about member initialization lists?




#5300599 c++: replace word in char array?

Posted by on 13 July 2016 - 02:33 PM

But using only char array (no string class or helper stuff).


By the pricking of my thumbs, buffer overflows - here they comes.




#5300598 How to automate texture deleting?

Posted by on 13 July 2016 - 02:30 PM

I don't see any problem using std::unordered_map<filename_string, texture> for storing textures. They're not something you're going to be iterating often, so it's not really a performance concern. One approach is to load resources this way as a cache. That is, have a loader encapsulated in a 'Cache' class with an unordered_map. When the user requests a resource from the cache, it looks to see if the map already holds the resource, and if not it loads the resource into the map. Then it just returns the mapped resource and you avoid reloading things you've already loaded.






PARTNERS