Advertisement

Questions about the Event Manager

Started by August 19, 2021 05:57 PM
20 comments, last by h8CplusplusGuru 3 years, 3 months ago

It depends on the compiler from my experience. MSVC is always adding the vtable pointer to types which have a destructor, even if it isn't virtual

Which event manager are you using?

Advertisement

Shaarigan said:

It depends on the compiler from my experience. MSVC is always adding the vtable pointer to types which have a destructor, even if it isn't virtual

Not any MSVC since at least C++11. I just double checked (using both my local MSVC as well as multiple previous version in godbolt), not sure what you were exactly getting or how you were getting it, but thats definately not happening in the last few years.

EDIT: Just so we are talking about the same thing:

class Foo
{
	~Foo(void) = default;
};

class Foo2 :
	public Foo {};

class Bar
{
	virtual ~Bar(void);
};

class Bar2 :
	public Bar {};


int main()
{
	static_assert(sizeof(Foo) == 1);
	static_assert(sizeof(Foo2) == 1);

	static_assert(sizeof(Bar) == sizeof(void*));
	static_assert(sizeof(Bar2) == sizeof(void*));
}

You are actually saying that when you last tested it, the second assertion would have failed for you, right?

@Shaarigan

Shaarigan said:
You can easily check this with a sizeof call, just saying, in the end it is your implementation

Hmmm, what u mean by this? Allocate data using an Allocator and in size param use sizeof(EventEntityCreate) instead of new?

Shaarigan said:
Did you read the file all at once when loading from the main-thread as well?

Can't remember now, because the world loader wasn't implemented by me (we are using some old source), I remember it open the file, and started to use “fread” everywhere, I changed it by reading the whole file into a buffer and read from that buffer what was needed (I might try using a Stream and reading by chunks), but, to avoid those issues, I read the file in a second thread, and the main one, when the WorldManager tick, just ask if buffer is full, if it already has the whole file in memory, it will start the loading stuff.

Shaarigan said:
What games usually do is to put everything into a huge archive kind of file and then use memory mapped I/= to the file in order to have fast access to different portions of it. It is quiet simple, you just need some kind of chunking which padds the file to 64k boundaries and some indexing header. Then you load the mapping and create views (in Windows, Unix doesn't need them) to the chunks you need and read everything as a plain byte pointer.

I don't mind doing something like that, but, do u have any code example of it? We use some system similar to DOOM pak files, we have one header with the file info and in the body's we have all the files, each body can be max of 100MB and each file inside max of 20MB. For example: if animations take 200MB and 100 files, we will have animation1 - 100MB with 50 files and animation2 - 100MB 50 files.

Also the game is 32-bit, and we already use 1 - 1.5GB of the max RAM of 2GB, I don't know if something like memory mapped could be possible.

PD: @juliean I will prob use the vtable tho, for the system I'm getting the EventType with a virtual “virtual EventID GetEventType() const = 0” and override it every child. (I could prob remove that one tho, by creating a new variable like EventID m_eventId and pass it through the constructor).

Rewaz said:
PD: @juliean I will prob use the vtable tho, for the system I'm getting the EventType with a virtual “virtual EventID GetEventType() const = 0” and override it every child. (I could prob remove that one tho, by creating a new variable like EventID m_eventId and pass it through the constructor).

Yeah, I do it both ways depending on the exact use case. Having a variable is a bit faster, especially if you only need the destructor for that. I usually go with a virtual method if the class already has a vtable, and a member-variable if it hasn't. I'd say you can generally refactor later (as long as you use a method like CRTP to handle the actual genertion of the ID in the derived classes), if you ever find its too slow.

Shaarigan said:
If you inherit from EventData then the memory is allocated for EventData, the vtable pointer for virtual function calls which is added to inherited types even if you don't use any inheritance per se, the compiler creates an implicit call to the base class constructor and finally the space needed for the overloaded class type as well. You can easily check this with a sizeof call, just saying, in the end it is your implementation

Hmm, what do you propose? How can I allocate the data for the event?

Advertisement

Juliean said:
not sure what you were exactly getting or how you were getting it, but thats definately not happening in the last few years.

Have seen that on Rextester, which I use to double check core code against different compilers. Maybe I have to try Godbolt as well

Rewaz said:
I remember it open the file, and started to use “fread” everywhere

As far as I know, FILE (so the stream used to fread from) already has a buffer. What size it has and how it advances the content, I don't know.

We declared a base class for streams (as everything in our engine is based on data streams) and the file stream maintains a circular buffer which is of a default size of 128 byte and filled once it is empty by a call to the OS API.

Rewaz said:
do u have any code example of it? We use some system similar to DOOM pak files, we have one header with the file info and in the body's we have all the files, each body can be max of 100MB and each file inside max of 20MB

I dig through my SVN and found the old implementation of my tool. However, it is a bit large so I'll try to upload it to my GitHub later this day. It however is similar to Bethesda's BSA files. We were packing Assets along their file path from a certain base directory, for example /World/Main/Data.map, create an FNV32b hash from that and store it as that Assets's ID along some other information, for example flags like encryption or compression. The location was then calculated from the chunk number plus an offset in bytes. Everything maxed to 64k per chunk and 4k per section, so every section fills an entire virtual memory page and every chunk fills the disk cache.

All assets were read from disk and sorted so that they fit best into chunks without overlapping if it was small enougth to not fill an entire chunk. The remaining bytes were then filled with trash data so chunks were always filled up entirely. This made it safer when encrypting and more reliable on compression.

The file was encrypted and/or signed if needed, for example to prevent people from modding the game.

Rewaz said:
Also the game is 32-bit, and we already use 1 - 1.5GB of the max RAM of 2GB, I don't know if something like memory mapped could be possible.

32 bit has 4GB of addressable space at max, not 2GB, that said, memmory mapped I/O isn't placing a file in RAM directly, it allocates virtual memory pages which the OS swaps in and out of RAM on demand. Otherwise our database solution wouldn't be possible to have an addressable space of up to 5TB.

2GB of data in RAM sounds quiet much to me. Do you have a dedicated graphics unit or an onboard one, which shares RAM and VRAM?

Rewaz said:
Hmm, what do you propose? How can I allocate the data for the event?

If you don't need inheritance, don't use it and instead have a container type which takes a template (like vector) so memory is allocated coherent and you just pass a pointer to that element or an index or something. You can then clear the container if you're done with processing the current batch of events

Shaarigan said:
I dig through my SVN and found the old implementation of my tool. However, it is a bit large so I'll try to upload it to my GitHub later this day. It however is similar to Bethesda's BSA files. We were packing Assets along their file path from a certain base directory, for example /World/Main/Data.map, create an FNV32b hash from that and store it as that Assets's ID along some other information, for example flags like encryption or compression. The location was then calculated from the chunk number plus an offset in bytes. Everything maxed to 64k per chunk and 4k per section, so every section fills an entire virtual memory page and every chunk fills the disk cache.

Thanks, I will check that one!

Shaarigan said:
As far as I know, FILE (so the stream used to fread from) already has a buffer. What size it has and how it advances the content, I don't know. We declared a base class for streams (as everything in our engine is based on data streams) and the file stream maintains a circular buffer which is of a default size of 128 byte and filled once it is empty by a call to the OS API.

Hmm, I created a base class Stream, and another child which is like StreamFileBuffer, it has a 4KB buffer. When it's read, I will call fread with another 4KB for the buffer until file is completely read.

Shaarigan said:
32 bit has 4GB of addressable space at max, not 2GB, that said, memmory mapped I/O isn't placing a file in RAM directly, it allocates virtual memory pages which the OS swaps in and out of RAM on demand. Otherwise our database solution wouldn't be possible to have an addressable space of up to 5TB.

Yes, but Windows (OS) takes 2GB of RAM, so only 2GB are free for us to use (AFAIK).

Shaarigan said:
2GB of data in RAM sounds quiet much to me. Do you have a dedicated graphics unit or an onboard one, which shares RAM and VRAM?

Nono, I have a GPU, my CPU doesn't have an iGPU.

Shaarigan said:
If you don't need inheritance, don't use it and instead have a container type which takes a template (like vector) so memory is allocated coherent and you just pass a pointer to that element or an index or something. You can then clear the container if you're done with processing the current batch of events

So something like the vector, resize it, and use data() to get the memory?

For your delegates why not std function with lambdas?

This topic is closed to new replies.

Advertisement