Jump to content

  • Log In with Google      Sign In   
  • Create Account

Juliean

Member Since 29 Jun 2010
Online Last Active Today, 03:46 AM

#5290081 how good is rand() ?

Posted by Juliean on Yesterday, 10:26 AM

Not all that good.

If you have access to C++11 and the STL, try the "<random>" header.

#include <random>

void main(void)
{
    // the random-device/generator, there is a few different of those
    // you create one at the start of your application, or multiple if you want to seperate RNG of different components
    std::mt19937 randgen((unsigned int)time(nullptr));
    
    // the distribution, there are different types for float, int, ...
    // you can create this on-the-fly inside a function
    const std::uniform_int_distribution<int> distribution(0, 1024);
    
    const auto randomNumber = distribution(randgen);
}

You are supposed to get way better results with that.




#5289432 Optimization issue when using operator=

Posted by Juliean on 30 April 2016 - 10:30 AM

I make a simple assignment using my Vec2 operator= for 10m iterations. It takes roughly around 390ms compare to glm's vec2 that is 28ms on my machine. I even removed the code inside it, it is still like that. This is completely the opposite when I'm doing a multiplication (operator*), which is ~410ms vs ~668ms.

 

What kind of optimizations are you applying in your compiler? I assume you are testing with "debug" in Visual Studio (or any other equivalent), since otherwise I strongly assume the loop would be optimized out anyways since it doesn't do any work. What that means is that first of all you are not using the the write-to vector anywhere outside of the loop, which is case one for a compiler to optimize it away, and case two is that you are assigning the same value over and over which could also be eliminated.

 

Why is this important? Any benchmark in debug mode is pretty pointless. Enable optimizations/switch to "release" first, which will most likely result in the loop taking 0 ms at first due to optimizations (which can be fixed by doing "std::cout << vres" after the loop; unless the compiler chooses to optimize the loop based on the second case, for which I do not have a solution).

 

A loop with an empty assignment-operator, if executed with proper optimization should take like not even 1 ms. Then you can see what the real difference between yours and glms class is.

As for why it could be slower: GLM uses template types, which are more likely to be inlined IIRC, which can be a huge deal if you are running in an unoptimized build. See how it behaves in release first, and then if it still doesn't change try "force_inlining" the function.




#5289130 Returning by value is inevitable?

Posted by Juliean on 28 April 2016 - 01:37 PM

First of, and kind of offtopic: Where the hell is the selective quoting function?? Using the regular "quote" button is a pain in the ...
 

Furthermore, __vectorcall and some related optimizations have benefits even for non-SIMD code. Turning on the right set of optimizations and inspecting assembly, you might notice that all kinds of little structs get passed in registers. For ABI reasons, older calling conventions aren't allowed to pass small structs in registers. With /ARCH:AVX and /Gv for instance I find that most of my data gets passed in SSE registers, even non-SIMD things.

 

Oh, my bad, didn't even realize vectorcall was designed primarily for SIMD-code. I didn't come around improving my math-library to use SIMD-code, but after your reply and reading more into it, I'll try out vectorcall and fastcall and if it improves anything in my codebase.

 

Homegrown benchmarks are almost always meaningless, unfortunately. It's exceedingly difficult to be sure that you're benching what you think that you're benching.

 

Well, thats true, I'm not very experienced with running meaningful benchmarks eigther, so might as well be meaningless. It still convinced me to pass most of my small structs by value, and I didn't receive any large performance hit from it... well not that I could see, benchmarking this from an entire application seems to be really hard to me.




#5288766 Returning by value is inevitable?

Posted by Juliean on 26 April 2016 - 10:13 AM

I want to calculate the cross product of two vectors in a class which will be stored in temp variable. When I call on this method I cannot return it by const ref because the stack will be destroyed when the method returns I dont want to return by value because there is a lot copying if I use this method frequently.How can I make a compromise between efficiency and correctness ?
thanks

 

You don't even need to. Any halfway modern compiler will perform RVO (https://en.wikipedia.org/wiki/Return_value_optimization), which means that

Vector cross(const Vector& vector, const Vector& vector2)
{
    Vector crossProduct;

    return crossPoduct;
}

const auto outCrossProduct = cross(v1, v2);

is equivalent to:

// this is somewhat along the lines of what the compiler will do with the entire code above

void cross(Vector& crossProduct, const Vector& vector, const Vector& vector2)
{
}

Vector outCrossProduct;
cross(outCrossProduct, v1, v2);

The compiler will construct the otherwise internal "crossProduct" variable directly in the "outCrossProduct"-variable, with no additional copy-constructors/destructors called. If you are really unsure about it, you can eigther check assembly or test this behaviour with putting cout-statements in your copy-ctor/dtor (those won't even be called because RVO might alter side-effects). Just make sure to turn on optimizations.

 

EDIT: Oh, forgot to mention, the first version of the function with RVO can probably perfom even better than if you just wrote the second one, due to aliasing. See how you have to pass in 3 references to "Vector"? The compiler has to assume all of the can point to the same memory location and thus has to produce safer, but slower code. You cannot use "restrict" here though, because taking the cross product of the same vector with itself might be a valid operation. So the second version at least can make optimizations regarding that the local "crossProduct" cannot alias with "vector" and "vector2", while in the second example it can, most likely resulting in better code generated for the first version than the second.




#5287157 [VS2015] Natvis: inspect vector data in custom visualiser

Posted by Juliean on 16 April 2016 - 02:34 AM

Your getting into implementation details of std::vector by using _Myfirst. That kind of code isn't portable. Why can't you just call m_vComponents.data()? as in:

<Item Name="Components">m_vComponents.data()</Item>

Of course I'm not familiar with how natvis works, and there are peculiarities related to it.

 

I'd love too and trust me, this would make some other natvis-code much easier, but unfortunately you cannot call any kind of function in natvis (similar to breakpoint-conditions & debug-watch) because of possible side-effects (tried it, gives an error message).

Portability should be only half-bad though, since natvis is only used by VS2013+ anyways, and I make a conditional display based on some version-define anyways if I want to. Problem is just that it cannot be done that way :(




#5287069 [VS2015] Natvis: inspect vector data in custom visualiser

Posted by Juliean on 15 April 2016 - 12:32 PM

Thanks for the suggestion,
I know I can do that, but the problem is that m_vComponents is of type char, and I want to view it as "ComponentType"-template type.
I think a hackfix could be to store an additional pointer variable of type ComponentType* and assign m_vComponents.data() to is, but I'd really prefer not to make modifications to my code in order to do this.


#5282038 (split thread) c-style casts

Posted by Juliean on 19 March 2016 - 01:18 PM


static_cast<T*> == (T)
reinterpret_cast<T*> == (T*)
const_cast<T*> == (const T*)

I've not once run into a bug caused by C style casting that would not also have been caused by one of those C++ casts. So instead of just shooting yourself in the foot, you should get three guns to choose from?

Just a minor complaint about your usage of const cast - const_cast<const T*> is wrong, the way you use it is actually const_cast<T*>, and thus the equivalent c-style cast is (T*) - thats one of the reasons why C-style casts can be considered bad, because you might just cast a const modifier away where you didn't expect to.




#5281120 how could unlimited speed and ram simplify game code?

Posted by Juliean on 13 March 2016 - 04:10 PM

Some points I thought of:

 

- Physics would be totally simplified. No more broad/narrowphase, bounding volumes, etc... just good old per-vertex n^2 collision checks (well you would probably still want to use bounding volumes for stuff like characters, but you don't have to anymore!)

 

- Pretty much any form of preprocessing could be gone. BSP-Trees, static light/shadow maps, you name it. This could heavily simplify code, especially on the tool side of things. Moved an object, need to notify the build pipeline and rebuild the static shadowmaps? Nope, not anymore, just take everything as it is and use it

 

- Any form of manual optimization would be gone. Memory layout, SSE, Multithreading (for the most part), Assembler... I can't speak for everyone but I for sure would prefer not having to write Struct-of-Array style code, for the most parts.

 

- Oh, and lets not forget about all the GPU-centered algorithms. GPU-Particles, GPU-Clothing, GPU-Watersimulation. Would be way easier if we could just write all those algorithms on one processor, I personally find regular CPU-code easier to work with than ie. compute-shader based equivalents.

 


all object types could have all possible member variables for all object types (unlimited ram), so you only need one type of "uber" game object type for everything.

 

Uh, why would you want to do that now? The reason we don't have "uber"/god classes is not because RAM doesn't support it, but because its harder to read through on 100k line cpp file, than it is through 1000 100 line ones (since you are only interested in part of the code anyways). I mean, be it composition/inheritance or whatever, but a door is a door, and not an "if(i_am_a_door) doDoorStuff();" alongside another thousands of those in a huge god class, for every possible game object, unless I misinterpreted your intention here.




#5276038 Template Specialization

Posted by Juliean on 16 February 2016 - 05:16 PM


Helper class works correctly, but I'm sitting here staring at it wondering what the hell I'm looking at. Could you explain this to me?

 

Sure do.

 

So first of our outer class should act as a replacement of std::is_aritmetic, so we set it up the interface like this:

template<typename T>
class acceptsType
{
public:
    static const bool value = //???
};

So the value of the compile-time evaluated "value" variable should depend both on the type, as well as the value of is_aritmetic. So we do just that, by defining an inner helper class that takes two template arguments:

template<typename T2, bool isAritmetic = false>
class helper
{
public:

    static const bool value = false;
};

So now we call it by making it take <T, std::is_aritmetic<T>::value>, and assigning its "value" to the outer-class "value":

static const bool value = typename helper<T, std::is_arithmetic<T>::value>::value;

And the base template "helper" class now defines that for any type where is_arithmetic is "false", "value" will also be false. But we want to support some types though, so we add a specialisation for every arbitrary type where is_arithmetic is actually true:

template<typename T2>
class helper<T2, true>
{
public:

    static const bool value = true;
};

This uses partial specialisation, and translates to what I've just said - arbitary T2, and std::is_arthmetic == true will generate this overload.

 

So at this state adding anything except int, float, ... will return "false" and thus fail your SFINEA. But adding additional types is now really easy.

template<>
class helper<std::string, false>
{
public:
    static const bool value = true;
};

This time, we use a full specialistation - we know the value of T2 (std::string), and that it is not arithmetic. This combats the ambiquity that you described in your attempts to fix it - due to the second template parameter, you always get a closes match for the types.

 

 

 

Hope this solves any questions, if you are still confused, I'll try to explain anything that is left over.




#5275954 Template Specialization

Posted by Juliean on 16 February 2016 - 09:43 AM


In the existing function it prevents compilation if the type is not arithmetic. The function body handles arithmetic types, but I also want to start supporting specific non-arithmetic types using the same signature but different function bodies. (Beginning with std::string, yes.)



This is in VS, so the way I have it now does the red underline at the call site when a non-supported type is entered, which is sort of what I'm after.



How would you go about using static_assert for this?

 

Okay, so at least I was right about the purpose.

 


Should work in my opinion but I don't have a relevant compiler close enough to check.

 

Unforunately what this doesn't do is add the red underline in VS, at least from what I tested with VS2015.

 

So in case you are the only one adding specialisations to this function, you can use my first suggestion, otherwise BitMasters' (if it fixes the problem).

 

Aside from that, you'll have to think about what you prefer: Red underlines wherever someone uses an imcompatible type with this function, probably followed by a cryptic template-error-message (using SFINEA), or no underlining but a meaningful error message that you yourself can provide for the user (using static_assert). Probably depends on your use-case, which of the two is better.




#5275923 Template Specialization

Posted by Juliean on 16 February 2016 - 08:56 AM


std::enable_if is already C++11. You might be thinking of the old boost::enable_if. That said, I'm not sure about the exact semantics of static_assert but can it really be used to trigger SFINAE, because that's usually the whole point of enable_if.

 

Ah right, wasn't sure if enable_if is already in C++11 or not.

 

No, static_assert won't trigger SFINAE, but that was kind of my point - as with the code example given, I don't see much points in him using SFINEA. What he shows and describes is a function that should fail to instantiate if a incompatible type is used - which is what static_assert does, but only with a better error output. I posted both my solution and the comment towards static_assert based on whether he really wants to use SFINEA here, or not :)




#5275891 Template Specialization

Posted by Juliean on 16 February 2016 - 05:07 AM

template<typename T>
class acceptsType
{
    template<typename T2, bool isAritmetic = false>
    class helper
    {
    public:

        static const bool value = false;
    };

    template<typename T2>
    class helper<T2, true> // every aritmetic value is accepted
    {
    public:

        static const bool value = true;
    };

    template<>
    class helper<std::string, false> // otherwise, only std::string is accepted
    {
    public:
        static const bool value = true;
    };

public:

    static const bool value = typename helper<T, std::is_arithmetic<T>::value>::value;
};

The idea I had involved generating a helper-template class, that acts as a replacement of "is_aritmetic" when you declare your template function:

template<class T, typename = typename std::enable_if<acceptsType<T>::value, T>::type> // exchange here
T function(const std::string& requestString)
{
    //things happen
}

template<>
std::string function<std::string>(const std::string& requestString) // compiles now
{
    //tomfoolery
}

template<>
int function<int>(const std::string& requestString)
{
}

This compiles for me. I don't know if its the smoothest solution, but at least it gives you explicit control over ie. if you want to add more types without it getting too complicated.

 

EDIT:

 

Also, unless you are stuck on pre-C++11, why even go to the trouble of handling this using std::enable_if? If I get it correctly you want to have an compile error if an user tries to use a type thats not aritmetic(or std::string). Why not use an static_assert instead?

template<class T>
T function(const std::string& requestString)
{
    static_assert(std::is_arithmetic<T>::value, "Function only accepts aritmetic values (int, float, ...). Alternatively, try the std::string overload.");
}

Allows you to have an error message that is more clear, and makes specializing way easier.




#5275485 MSVC generating much slower code compared to GCC

Posted by Juliean on 12 February 2016 - 06:44 PM

 __restrict cannot be used on references though, for whatever reason. And even though I read an explanation on those forums that references are already thought of to not be able to be rebound, testing it with assembler showed significant worse generated code than with pointers and __restrict (or whatever equivalent).

 


vec.size() likely is implemented as "return end-begin;", but as shown in this example, if it happened to be "return m_size;", then it could cause aliasing issues for the optimizer.

 

Even if it was implemented as end-begin, whats the difference? It will need to access member variables of the class regardless (this->end, this->begin), so aliasling is bound to happen unless you tell the compiler otherwise.

 

Especially with more compilcated loops, explicitely caching values like the result of size() is well worth, since

- the compiler cannot really assume you are not using a pointer to the vector used in the loop => especially since MSVC is very lenient about pointer casting, pretty much any pointer you use can be filled with any memory address you want, so the compiler always has to asume that every pointer can manipulate the value of everything else (probably one of the reasons why MSVC performs worse than most other compilers).

- Even if it could, you are telling the compiler "I want to compare to the size of the vector at every iteration". Who's guaranteeing that the value of size() is staying constant? In fact, nobody. Specially if you are using the vector inside the loop, it is very likely that some call might just change the size of the vector.




#5274746 Constructor + putting into an array

Posted by Juliean on 07 February 2016 - 07:11 AM


it compiles, so is there a way to initialize an array of do i have to do this by declaring 6 different vars?

 

No, at least in C++11 you can do so:

class Testing
{
public:

    Testing(float x, float y, float z) :
        arr { x, y, z}
    {
    }

    float arr[3];
};

Instead of () you use {}, and pass the elements in the desired order.




#5271478 How would you create a Weapons class?

Posted by Juliean on 16 January 2016 - 05:59 PM


There are tons of other engines out there that you can check out on Github. Inheritance is a common pattern it would seem for such a model based solely upon browsing others code.

 

I wouldn't recommend looking at 11-year old code as a source of inspiration though. Many things have evolved from then, and while the code might have been subjectively good at this point in time, doesn't mean it holds up today. Especially since you mentioned it, inheritance is being dropped in favour of composition nowadays, which offers many benefits.

 

Bottom line, if you want to make an educated obseration of what is "a common pattern", you need to look at modern code, not code from a decade ago. Big current engines like Unreal Engine 4 also use inheritance more than I feel comfortable with, but I don't feel qualified to comment on whether it is justified in that case based on how flexible, as well as easy-to-use they need to be, or if its just a decision based on "we've always done it that way"... in a more specific games code where you have full control over all decisions, composition wins big time over inheritance.






PARTNERS