Skip to content

Commit

Permalink
Eliminate "construct on first use" overhead
Browse files Browse the repository at this point in the history
By depending on initialization order in same compilation unit.
This finishes the circular dependency refactoring.
  • Loading branch information
dspinellis committed Jul 19, 2024
1 parent 8aa286e commit 7ed5386
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 77 deletions.
5 changes: 3 additions & 2 deletions src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ OBJBASE=eclass.o fchar.o filedetails.o fileid.o pdtoken.o pltoken.o debug.o \
error.o fdep.o fcall.o call.o idquery.o query.o funquery.o \
logo.o workdb.o obfuscate.o sql.o md5.o os.o pager.o \
option.o filequery.o mcall.o filemetrics.o funmetrics.o ctconst.o \
dirbrowse.o html.o fileutils.o gdisplay.o globobj.o ctag.o timer.o
dirbrowse.o html.o fileutils.o gdisplay.o globobj.o ctag.o timer.o \
static_init.o

# monitor.o

Expand All @@ -63,7 +64,7 @@ CFILES=md5.c attr.cpp call.cpp cscout.cpp ctag.cpp ctconst.cpp \
logo.cpp macro.cpp mcall.cpp metrics.cpp obfuscate.cpp option.cpp os.cpp \
pager.cpp pdtoken.cpp pltoken.cpp ptoken.cpp query.cpp simple_cpp.cpp \
sql.cpp stab.cpp tchar.cpp timer.cpp token.cpp tokid.cpp \
tokmap.cpp type.cpp workdb.cpp
tokmap.cpp type.cpp workdb.cpp static_init.cpp

HEADERS=attr.h call.h compiledre.h cpp.h ctag.h ctconst.h ctoken.h \
debug.h defs.h dirbrowse.h eclass.h error.h eval.h fcall.h fchar.h fdep.h \
Expand Down
25 changes: 2 additions & 23 deletions src/filedetails.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,27 +57,6 @@
#include "md5.h"
#include "os.h"

/*
* Return the map from file id to file details, controlling the order of its
* construction through the "Construct on First Use" idiom.
*/
FI_id_to_details &
Filedetails::get_i2d()
{
static FI_id_to_details i2d;
return i2d;
}

/*
* Return a map of files that are exact duplicates, controlling the order
* of its construction through the "Construct on First Use" idiom.
*/
FI_hash_to_ids &
Filedetails::get_identical_files()
{
static FI_hash_to_ids identical_files;
return identical_files;
}

Filedetails::Filedetails(string n, bool r, const FileHash &h) :
name(n),
Expand Down Expand Up @@ -194,7 +173,7 @@ Filedetails::set_hand_edited()
void
Filedetails::clear_all_visited()
{
for (FI_id_to_details::iterator i = get_i2d().begin(); i != get_i2d().end(); i++)
for (FI_id_to_details::iterator i = i2d.begin(); i != i2d.end(); i++)
i->clear_visited();
}

Expand Down Expand Up @@ -248,7 +227,7 @@ unify_file_identifiers(const set<Fileid> &fs)
void
Filedetails::unify_identical_files(void)
{
for (FI_hash_to_ids::const_iterator i = get_identical_files().begin(); i != get_identical_files().end(); i++)
for (FI_hash_to_ids::const_iterator i = identical_files.begin(); i != identical_files.end(); i++)
if (i->second.size() > 1)
unify_file_identifiers(i->second);
}
25 changes: 10 additions & 15 deletions src/filedetails.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ using namespace std;
* in order to keep *values.begin() invariant.
* This property is used by tokid unique for returning unique tokids
*/
typedef vector<unsigned char> FileHash;
typedef map <FileHash, set<Fileid> > FI_hash_to_ids;

// Details we keep for each included file for a given includer
Expand Down Expand Up @@ -95,7 +94,7 @@ class Filedetails {
vector <bool> processed_lines;;
FileIncMap includes; // Files we include
FileIncMap includers; // Files that include us
FileHash hash; // MD5 hash for the file's contents
FileHash hash; // MD5 hash for the file's contents
int ipath_offset; // Offset in the include file path where this file was found
Fileidset runtime_uses; // Files whose global objects this file uses at runtime
Fileidset runtime_used_by; // Files that use at runtime this file's global objects
Expand All @@ -107,11 +106,8 @@ class Filedetails {
string contents; // Original contents, if hand-edited
bool visited; // For calculating transitive closures

// Get map from id to file details
static FI_id_to_details& get_i2d();

// Return map of files that are exact duplicates
static FI_hash_to_ids& get_identical_files();
static FI_id_to_details i2d; // From id to file details
static FI_hash_to_ids identical_files;// Files that are exact duplicates
public:
Attributes attr; // The projects this file participates in
FileMetrics pre_cpp_metrics; // File's metrics before cpp
Expand All @@ -123,16 +119,16 @@ class Filedetails {

// Return the instance associated with the specified id
static Filedetails &get_instance(Fileid fi) {
return get_i2d()[fi.get_id()];
return i2d[fi.get_id()];
}

static FI_id_to_details::size_type get_i2d_map_size() {
return get_i2d().size();
return i2d.size();
}

// Add a new instance with the specified ctor values
static void add_instance(string n, bool r, const FileHash &hash) {
get_i2d().emplace_back(n, r, hash);
static void add_instance(string n, bool r, const FileHash &h) {
i2d.emplace_back(n, r, h);
}

// Unify identifiers of files that are exact copies
Expand All @@ -142,9 +138,8 @@ class Filedetails {
static void clear_all_visited();

// Add fi to the set of files that have the identical hash
static void add_identical_file(const FileHash &hash, Fileid fi) {
auto &file_set = get_identical_files()[hash];
file_set.insert(fi);
static void add_identical_file(FileHash hash, Fileid fi) {
identical_files[hash].insert(fi);
}

const string& get_name() const { return name; }
Expand Down Expand Up @@ -276,7 +271,7 @@ class Filedetails {
;
// Return the set of files that are the same as this (including this)
static const Fileidset & get_identical_files(Fileid id) {
return get_identical_files()[get_instance(id).get_filehash()];
return identical_files[get_instance(id).get_filehash()];
}

// Return the set of files that we depend on for runtime objects
Expand Down
31 changes: 4 additions & 27 deletions src/fileid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,29 +59,6 @@

int Fileid::counter; // To generate ids
list <string> Fileid::ro_prefix; // Read-only prefix
//
/*
* Return the anonymous id, controlling the order of its construction
* through the "Construct on First Use" idiom.
*/
Fileid
Fileid::get_anonymous()
{
static Fileid anonymous(Fileid("ANONYMOUS", 0));
return anonymous;
}

/*
* Return the map from unique name to id, controlling the order of its
* construction through the "Construct on First Use" idiom.
*/
FI_uname_to_id &
Fileid::get_u2i()
{
static FI_uname_to_id u2i;
return u2i;
}


#ifdef WIN32
// Return the canonical representation of a WIN32 filename character
Expand Down Expand Up @@ -128,7 +105,7 @@ Fileid::Fileid(const string &name)
string sid(get_uniq_fname_string(name.c_str()));
FI_uname_to_id::const_iterator uni;

if ((uni = get_u2i().find(sid)) != get_u2i().end()) {
if ((uni = u2i.find(sid)) != u2i.end()) {
// Filename exists; our id is the one from the map
id = uni->second;
} else {
Expand All @@ -137,7 +114,7 @@ Fileid::Fileid(const string &name)
unsigned char *h = MD5File(name.c_str());
FileHash hash(h, h + 16);

get_u2i()[sid] = id = counter++;
u2i[sid] = id = counter++;
Filedetails::add_instance(fpath, is_readonly(name.c_str()), hash);

Filedetails::add_identical_file(hash, *this);
Expand All @@ -146,10 +123,10 @@ Fileid::Fileid(const string &name)
cout << "Fileid(" << name << ") = " << id << "\n";
}

// Used for initialization and testing; not for real files
// User for initialization and testing; not for real files
Fileid::Fileid(const string &name, int i)
{
get_u2i()[name] = i;
u2i[name] = i;
Filedetails::add_instance(name, true, FileHash());
id = i;
Filedetails::add_identical_file(FileHash(), *this);
Expand Down
16 changes: 6 additions & 10 deletions src/fileid.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,15 @@
#include <list>
#include <map>
#include <set>
#include <string>
#include <vector>

using namespace std;

class Fchar;
class FileMetrics;

typedef vector<unsigned char> FileHash;
typedef map <string, int> FI_uname_to_id;

/*
Expand All @@ -49,19 +51,13 @@ class Fileid {
int id; // One global unique id per workspace file

static int counter; // To generate ids
// Return map from unique name to id
static FI_uname_to_id& get_u2i();
static FI_uname_to_id u2i; // From unique name to id

// Construct a new Fileid given a name and id value
// Only used internally for creating the anonymous id
Fileid(const string& name, int id);

/*
* Return an anonymous id, controlling the order of its construction
* through the "Construct on First Use" idiom.
*/
static Fileid get_anonymous();

// An anonymous id
static Fileid anonymous;
// The prefix for read-only files
static list <string> ro_prefix;
// And a function to check fnames against it
Expand All @@ -73,7 +69,7 @@ class Fileid {
// Create it without any checking from an integer
Fileid(int i) : id(i) {}
// Construct an anonymous Fileid
Fileid() { *this = get_anonymous(); };
Fileid() { *this = Fileid::anonymous; };
// Return the full file path of a given id
const string& get_path() const;
const string get_fname() const;
Expand Down
60 changes: 60 additions & 0 deletions src/static_init.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* (C) Copyright 2024 Diomidis Spinellis
*
* This file is part of CScout.
*
* CScout is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CScout is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with CScout. If not, see <http://www.gnu.org/licenses/>.
*
*
* Statically initialized values. C++ only guarantees the initialization
* order of values that reside in the same compilation unit (from top to
* bottom). Therefore, define and initialize all related elements here
* in the appropriate order.
*
* The correct order of initialization can be also automatically
* guaranted with the "Construct on First Use" idiom, at the cost
* however of one non-inlinable function call for each access.
* The best wall clock time for cscout -c awk.cs with the current approach
* approach is 0.473 s vs 0.484 s for the Construct on First Use approach.
*
*/

#include "fileid.h"
#include "filedetails.h"

/*
* For the following their order is established as follows:
* 1. anoynmous object is initialized via the constructor
* Fileid::Fileid(const string &name, int i) which:
* 1.1 uses u2i
* 1.2 calls Filedetails::add_instance(name, true, FileHash()) using i2d
* 1.3 calls Filedetails::add_identical_file(FileHash(), *this) using
* Filedetails::identical_files
* Initialize from inside (1.X) to outside.
*/

// Map from unique name to id
FI_uname_to_id Fileid::u2i;

// Map from id to file details; first element is anonymous
FI_id_to_details Filedetails::i2d;

// Files that are exact duplicates
FI_hash_to_ids Filedetails::identical_files;

/*
* An object used creating Fileids with an empty initializer,
* which is required for initially empty Tokids.
*/
Fileid Fileid::anonymous = Fileid("ANONYMOUS", 0);

0 comments on commit 7ed5386

Please sign in to comment.