DEV Community

Discussion on: Exploring the Use of C++ for Game Development

 
taqmuraz profile image
Taqmuraz

Can you demonstrate such code for C++? To replace C++ test code from my repo.
Requirement is – memory we get from new Person() call must be located in the heap, and new code must not exploit current test implementation (example of such exploit – always returning the same pointer).

Thread Thread
 
tandrieu profile image
Thibaut Andrieu

Arguing with software developer is like chasing a pig in the mud. It leads you to nowhere and at some point, you will realize the pig likes that.

Thread Thread
 
_ce7114b1f3f00437dc3e0 profile image
張宏欣 • Edited

I loled again. You are trying so hard on numbers that does not even translate to a thing. Here is a hint, you forgot memory consumption and memory constraint. Some games need to run on minimum memory, try your benchmark on a limited memory, the smaller it gets, you'll see the picture. And FYI, this is why Android phones needs more RAM compared to iOS phone, and why Android phones was laggy since they were only 1GB RAM at first. Your claim that Java is (I assume you mean always) faster than C++ is easily disproven by Android (java) vs iOS (objective C), Java can be faster only if it has significantly MORE memory to use.

Thread Thread
 
miketalbot profile image
Mike Talbot ⭐ • Edited

Also JVMs aren't carved out of stone and magic pixel dust. JVMs being written in C or C++ is common - the point being, you can implement anything Java does in C++ - you have to be able to, because that's how Java is made... The other way around is not true because the designers decided to allow a subset and enforce garbage collection - which seems really fast when there's lots of memory, but then really doesn't seem fast when the world pauses for mark and sweep.

Thread Thread
 
taqmuraz profile image
Taqmuraz

I am writing 3D games with Java (using own game engine) and usually they take 75-150 MB of RAM. So what do you say here.

Thread Thread
 
miketalbot profile image
Mike Talbot ⭐ • Edited

Years ago I worked on JMonkey Engine, Java is a great platform and it is fine for building games. I'm simply saying that C++ is a low level language capable of implementing any pattern, including garbage collection should you wish. It's also great at allowing a developer the flexibility to create what they need and to optimally ensure that there aren't glitches and slow downs. Your games may never suffer those, which is fine - but that says something about the type of games you are making and the performance in the cases you have discovered.

I definitely agree that it is easier not to mess stuff up in Java, but that comes with a flexibility cost that a seasoned developer can exploit in a lower level language to wring every ounce of performance out of their solution.

Language choice, language level, virtual machine versus specific compilation, these are tool choices for us as developers, we should choose the right tool for the right job. It's a bad idea to worship tools and a good idea to question assumptions. If your games are 150MB then unsurprising you can get good performance out of Java because it will use way more actual memory than that and use free space compaction and a variety of other techniques to make memory allocations optimal. If you made a game that used more memory, you'd find it didn't perform as well. If you knew C++ and system architecture well, you could do the same thing in that language, probably with a few optimisations - but why bother, you don't need to so its fine for the types of project you are working on.

It's easier to write bad C++ than it is bad Java. This is a weakness of C++ and the only antidote is to be very good at developing it.

Thread Thread
 
taqmuraz profile image
Taqmuraz

Mostly, you are correct. I just wonder why it is appropriate to say "your C++ code is slow because you don't know C++ well" while "my Java code is slow because I don't know Java well" is so rare to hear.

Thread Thread
 
miketalbot profile image
Mike Talbot ⭐

It's one of the great strengths of Java for sure and why I'd choose it over C++ for any business application and many other projects.

Thread Thread
 
_ce7114b1f3f00437dc3e0 profile image
張宏欣 • Edited

just put a demo for it, just claiming that without proof is moot, just like you insist on benchmark result, lol, also, you only mention your game's memory usage, but not constraint, try running it on a 256MB or 512MB RAM machine, years ago 512MB RAM could run 3D games smoothly, but still, it can't be compared with your game, since its not apple to apple, the thing is, you have more ways to reduce memory usage on C++ than on Java

Thread Thread
 
taqmuraz profile image
Taqmuraz

I already tried running it on 256 MB of RAM. JVM has options to limit heap size.
About demo – to much effort would be for this argument with you. You could run it yourself, my github repos are public.

 
plotarmouredtitan profile image
PlotArmouredTitan • Edited

This is a version that uses std::allocator to pre-allocate memory before later constructing it for usage

#include "stdio.h"
#include "stdlib.h"
#include <chrono>
#include <iostream>
#include <string>
#include <memory>

class Person {
public:
    int age;
    Person(int age)
    {
        this->age = age;
    }
};

auto time_ms()
{
    return std::chrono::high_resolution_clock::now();
}

int run(int r, int s) {
    auto start = time_ms();
    int e = 0;

    for (int i = 0; i < r; i++) {
        Person* p = new Person(s);
        e += p->age;
        delete p; // only to avoid `out of memory` error
        // in fact, `delete` improves performance here
        // almost twice
    }
    auto end = time_ms();
    auto took = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
    printf("Took : %lld ms  ", took.count());
    return took.count();
}

int run_alloc(int r, int s) {

    auto start = time_ms();

    std::allocator<Person> alloc;
    //allocate memory pool
    Person * memPool = alloc.allocate(r);

    int e = 0;

    for (int i = 0; i < r; i++) {
        //retrieve address from memory pool
        Person* p = memPool + i;

        //construct object
        alloc.construct(p, s);

        e += p->age;

        //destroy object
        alloc.destroy(p);
    }

    //deallocate memory pool
    alloc.deallocate(memPool, r);

    auto end = time_ms();
    auto took = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
    printf("Alloc method Took : %lld ms\n", took.count());
    return took.count();
}
int run_alloc_reused(int r, int s) {

    auto start = time_ms();

    std::allocator<Person> alloc;
    Person* memAddr = alloc.allocate(1);

    int e = 0;

    for (int i = 0; i < r; i++) {
        Person* p = memAddr;

        alloc.construct(p, s);

        e += p->age;

        alloc.destroy(p);
    }

    alloc.deallocate(memAddr, r);

    auto end = time_ms();
    auto took = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
    printf("Reused Alloc method Took : %lld ms\n", took.count());
    return took.count();
}


int main(int argc, char** argv) {

    /*
    int r = atoi(argv[1]);
    int s = atoi(argv[2]);

    printf("Inputs : %d %d\n", r, s);
    printf("Integer type size : %ld\n", sizeof(int));

    */

    int shouldRun = 1;

    while(shouldRun)
    {
        int r = 0;
        int s = 0;

        std::cout << "input r" << '\n';
        std::cin >> r;
        std::cout << "input s" << '\n';
        std::cin >> s;

        long long sum = 0;
        long long min = 1L << 32U;
        long long max = -1;

        long long sum2 = 0;
        long long min2 = 1L << 32U;
        long long max2 = -1;

        int ts = 10;
        for (int i = 0; i < ts; i++) {
            long long t = run(r, s);
            long long t2 = run_alloc(r, s);

            if (t < min) min = t;
            if (t > max) max = t;
            sum += t;

            if (t2 < min2) min2 = t2;
            if (t2 > max2) max2 = t2;
            sum2 += t2;
        }
        double avg = ((double)sum) / ts;
        double avg2 = ((double)sum2) / ts;
        printf("Min : %lld, max : %lld, average : %f\n", min, max, avg);
        printf("Min2 : %lld, max2 : %lld, average2 : %f\n", min2, max2, avg2);

        std::cout << "input 0 to exit, 1 to continue" << '\n';
        std::cin >> shouldRun;
    }
}
Enter fullscreen mode Exit fullscreen mode

Some other changes I made to the code

  • used std::chrono for measuring time because its cross-platform while the method you used was not

  • used std::iostream to handle inputing values at startup time instead of using main() arguments

Edit:made some corrections to the code

Edit2: also I think that allocation and deallocation shouldn't even be included in the time measurement since GC allocations and deallocations are likely done at startup time

Thread Thread
 
taqmuraz profile image
Taqmuraz

Sorry, I wasn't online. I appreciate your efforts, I am already reading your code

Thread Thread
 
taqmuraz profile image
Taqmuraz

Case with 'std::allocator' looks better than others, because it has abstraction over test case. I must test how fast it is and compare with other allocation tests. Probably I will update contents of my article. Thank you again for your code :)