Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RDY] Ad hoc string identifiers for terrain + generic id caching (refactoring) #15977

Merged
merged 21 commits into from
Apr 20, 2016

Conversation

codemime
Copy link
Contributor

Rationale:

  • Provide convenient access to terrain objects using the existing tool (string_id<T>)

  • Make usage of the generic_factory class (or its derivative) possible for handling terrain. The current terrain code looks pretty old (probably it's mostly a bequest from the original game).

  • Get templated loading of ids from JSON files

  • Get rid of the hard-coded terrain id tables (at least try to)

  • And the last, but not least. While working on JSONize gates #15945 I realised how convenient it would be to cache int ids inside string ids (see the d22c736 - there's an ugly attempt, but it reveals the intent. dc1dbba088c1f756a62edfe420cda9640a527ec4 - the same code after). Since we don't remove existing prototype objects separately, we're in position to kill two birds with one stone:

    1. Reduce hashmap lookup
    2. Make the code nicer without any harm to performance
    

    Edit: even if we do delete them, there's still a good way to cache ids.

Done:

  • IDs are in place and (as far as I can tell) work fine, but still require some testing
  • I added filename and linenumber to the displayed debugmsg() for testing purposes (will revert the commit later if it's not desirable) Reverted and made a separate extended PR
  • Integer ids are retreived from string ids using the existing map lookup for the time being cached value
  • ter_t and furn_t now have different identifier types (same plain strings for furniture --> another PR later)
  • string_ids can be compared with standart strings (no need to construct an object for that)
  • Identifiers are passed by reference where former strings were passed by value
  • Things are partly encapsulated~~, but there's still some work to do~~.

TODO:

  • Get rid of the remaining terfind()'s
  • Hide the rest of the termap inside mapdata.cpp and don't export it
  • Initialize optional string ids inside constructor init lists
  • Implement int_id<T> caching
  • Get rid of the obsolete loadid
  • Other code improvements.

@@ -7853,9 +7843,9 @@ void map::draw_rough_circle_ter( ter_id type, int x, int y, int rad )
}, x, y, rad );
}

void map::draw_rough_circle_ter( std::string type, int x, int y, int rad )
void map::draw_rough_circle_ter( ter_str_id &type, int x, int y, int rad )
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please make those references const.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've done it already (as a part of 'Other code improvements'), but didn't push the unfinished commit yet.

@BevapDin
Copy link
Contributor

BevapDin commented Apr 3, 2016

The whole caching of int id looks like premature optimization to me. I doubt it's worth the effort. Have you measured that this is really a problem?

Besides that, it looks good.

@codemime
Copy link
Contributor Author

codemime commented Apr 3, 2016

I have and I'm convinced, that it's worth the spent effort. I'll describe my point more in detail in a summary a bit later. Thank you for the review.

@BevapDin
Copy link
Contributor

BevapDin commented Apr 3, 2016

Regarding id caching: another issue would be invalidating those cached ids when loading a new game. Some string ids are statically allocated, this would mean their cached ids are allocated statically as well. They are not reset when a new world is loaded, but loading new world data may change the mapping of string to int ids.

The int ids are sequentially (based on the load order), imagine a world 1 with mod A and mod B (loading in that order) and a world 2 with mod B and mod A (in that order).

In world 1, the terrain from mod A gets id 100,101, ... and from mod B would get 200,201, ... (example numbers). In world 2, the mod B is loaded first and its terrain would now get ids 100,101, ...

@codemime codemime force-pushed the cache_ter_str_id branch 3 times, most recently from b289bc9 to aa14509 Compare April 4, 2016 23:25
@codemime
Copy link
Contributor Author

codemime commented Apr 4, 2016

Summary (apart from the stated above):

Correct me if I'm wrong, but currently we have two equal copies of each loaded ter_t object (one in termap and another one in terlist). Same for the furniture. This PR eliminates duplication for the terrain.

Using cached int is approx 50-70 times faster, than hashmap lookup (~25 times faster if it needs to recheck the index, but it shall only occur when objects are deleted/reloaded). We don't need to worry about performance loss anymore (previous loadid was addressing this problem), just pass a string_id variable which can be converted to int/obj pretty fast after the first usage. The cached int has no overhead and is protected against invalidation:

// the first condition checks int range, the second checks matching
if( tid.is_valid() && terlist[tid].id == *this ) { ...

Also, I don't see any reason why we should distinguish empty-ids ("") and null-ids ("t_null" etc). It's just a source of complexity and confusion:

if( !ter.open.empty() && ter.open != "t_null" ) { ...

Any optional id can be initially set to NULL_ID, which means emptiness. Then any id which is not NULL_ID is a valid non-empty identifier. The whole condition can be rewritten as:

if( ter.open.is_null() ) { ...
// or even
if( ter.open ) { ... // omit is_null() as BevapDin proposed

My next goal is making the generic_factoryclass responsible for the routine functions and cache-management (concrete identifiers may or may not use it). If they use it, they'd just let the generic_factory do the job:

template<>
bool string_id<ter_t>::is_valid() const
{
    return terrain_factoy.is_valid( *this );
}
template<>
int_id<ter_t> string_id<ter_t>::id() const
{
    return terrain_factoy.get_int_id( *this );
}

Also, forthcoming PRs will decouple terrain and furniture further.

@codemime codemime changed the title [WIP][CR] Ad hoc string identifiers for terrain with generic caching (refactoring) [RDY] Ad hoc string identifiers for terrain + generic id caching (refactoring) Apr 4, 2016
@codemime
Copy link
Contributor Author

Anything else to do before it's mergeable? A couple of my next PRs depend on it.

@Coolthulhu
Copy link
Contributor

Has a merge conflict, I assume due to the astyling

@Coolthulhu
Copy link
Contributor

I'll mark it as a priority to make it stand out despite being "buried" under other PRs.

If you have something important depending on it, you can branch it out of this one and note in the PR that you did that.

@Coolthulhu Coolthulhu added the (P2 - High) High priority (for ex. important bugfixes) label Apr 16, 2016
@BevapDin BevapDin self-assigned this Apr 16, 2016
@@ -3,6 +3,8 @@

#include <string>
#include <type_traits>
#include <memory>
#include "debug.h"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are these includes needed here? I can't see anything in this files that uses their content.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well spotted. Thanks. I had added them for debugging purposes, but then forgot to delete.

@BevapDin BevapDin removed their assignment Apr 16, 2016
@BevapDin
Copy link
Contributor

It looks fined, besides the strange includes. Are they really needed, it compiles and runs fine without them, for me.

codemime added 19 commits April 19, 2016 01:06
This commit doesn't aim introducing full-blown constructors for structs other than 'ter_t'
…timisation)

Why was it made that way? Same for the furniture, but I won't touch it here.
Also, if a terrain string id is not found in the map and the id should be
converted to represent the right index, then let it be converted only once
and put the converted value into the map.
Add the validity check and allow constructing an 'int_id' from a string. It simplifies the syntax and should be mostly harmless.
It's meant to be generic and the 'generic_factory' should take care of the caching. Therefore as soon as it's done, no need to copy-paste the boilerplate code for every class.
@codemime
Copy link
Contributor Author

codemime commented Apr 18, 2016

Rebased against master and represents changes after the latest @BevapDin's review.
@Coolthulhu Thanks for the label.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
(P2 - High) High priority (for ex. important bugfixes)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants