add copy-on-write semantics to amount.cc; cuts object creation by 5x

This commit is contained in:
John Wiegley 2004-08-21 03:23:17 -04:00
parent 06ac87ab20
commit fb91d6f21e
6 changed files with 238 additions and 132 deletions

290
amount.cc
View file

@ -1,18 +1,64 @@
#include "ledger.h" #include "ledger.h"
#include "binary.h"
#include "error.h" #include "error.h"
#include "util.h" #include "util.h"
#include "gmp.h" #include <deque>
#define MPZ(x) ((MP_INT *)(x)) #include "gmp.h"
namespace ledger { namespace ledger {
#ifdef DEBUG_ENABLED
static int ctors = 0;
static int dtors = 0;
#endif
struct amount_t::bigint_t {
mpz_t val;
unsigned int ref;
unsigned int index;
bigint_t() : ref(1), index(0) {
mpz_init(val);
#ifdef DEBUG_ENABLED
ctors++;
#endif
}
bigint_t(mpz_t _val) : ref(1), index(0) {
mpz_init_set(val, _val);
#ifdef DEBUG_ENABLED
ctors++;
#endif
}
~bigint_t() {
assert(ref == 0);
mpz_clear(val);
#ifdef DEBUG_ENABLED
dtors++;
#endif
}
};
#ifdef DEBUG_ENABLED
static struct ctor_dtor_info {
~ctor_dtor_info() {
DEBUG_CLASS("ledger.amount.bigint");
DEBUG_PRINT_("bigint_t ctor count = " << ctors);
DEBUG_PRINT_("bigint_t dtor count = " << dtors);
}
} __info;
#endif
#define MPZ(x) ((x)->val)
static mpz_t temp;
static mpz_t divisor; static mpz_t divisor;
static mpz_t true_value; static mpz_t true_value;
static struct init_amounts { static struct init_amounts {
init_amounts() { init_amounts() {
mpz_init(temp);
mpz_init(divisor); mpz_init(divisor);
mpz_init(true_value); mpz_init(true_value);
mpz_set_ui(true_value, 1); mpz_set_ui(true_value, 1);
@ -21,6 +67,7 @@ static struct init_amounts {
~init_amounts() { ~init_amounts() {
mpz_clear(true_value); mpz_clear(true_value);
mpz_clear(divisor); mpz_clear(divisor);
mpz_clear(temp);
} }
#endif #endif
} initializer; } initializer;
@ -80,73 +127,91 @@ static void mpz_round(mpz_t out, mpz_t value, int value_prec, int round_prec)
} }
amount_t::amount_t(const bool value) amount_t::amount_t(const bool value)
: quantity(NULL), commodity(NULL)
{ {
if (value) { if (value) {
commodity = commodity_t::null_commodity; quantity = new bigint_t(true_value);
precision = 0; precision = 0;
quantity = new MP_INT; commodity = commodity_t::null_commodity;
mpz_init_set(MPZ(quantity), true_value); } else {
quantity = NULL;
precision = 0;
commodity = NULL;
} }
} }
amount_t::amount_t(const int value) amount_t::amount_t(const int value)
: quantity(NULL), commodity(NULL)
{ {
if (value != 0) { if (value != 0) {
_init(); _init();
commodity = commodity_t::null_commodity;
precision = 0;
mpz_set_si(MPZ(quantity), value); mpz_set_si(MPZ(quantity), value);
precision = 0;
commodity = commodity_t::null_commodity;
} else {
quantity = NULL;
precision = 0;
commodity = NULL;
} }
} }
amount_t::amount_t(const unsigned int value) amount_t::amount_t(const unsigned int value)
: quantity(NULL), commodity(NULL)
{ {
if (value != 0) { if (value != 0) {
_init(); _init();
commodity = commodity_t::null_commodity;
precision = 0;
mpz_set_ui(MPZ(quantity), value); mpz_set_ui(MPZ(quantity), value);
precision = 0;
commodity = commodity_t::null_commodity;
} else {
quantity = NULL;
precision = 0;
commodity = NULL;
} }
} }
amount_t::amount_t(const double value) amount_t::amount_t(const double value)
: quantity(NULL), commodity(NULL)
{ {
if (value != 0.0) { if (value != 0.0) {
_init(); _init();
commodity = commodity_t::null_commodity;
// jww (2004-08-20): How do I calculate?
precision = 0;
mpz_set_d(MPZ(quantity), value); mpz_set_d(MPZ(quantity), value);
// jww (2004-08-20): How do I calculate this?
precision = 0;
commodity = commodity_t::null_commodity;
} else {
quantity = NULL;
precision = 0;
commodity = NULL;
} }
} }
void amount_t::_clear() void amount_t::_release()
{ {
mpz_clear(MPZ(quantity)); if (--quantity->ref == 0)
delete (MP_INT *) quantity; delete quantity;
} }
void amount_t::_init() void amount_t::_init()
{ {
quantity = new MP_INT; quantity = new bigint_t;
mpz_init(MPZ(quantity)); }
void amount_t::_dup()
{
if (quantity->ref > 1) {
bigint_t * q = new bigint_t(MPZ(quantity));
_release();
quantity = q;
}
} }
void amount_t::_copy(const amount_t& amt) void amount_t::_copy(const amount_t& amt)
{ {
if (quantity) { if (quantity)
mpz_set(MPZ(quantity), MPZ(amt.quantity)); _release();
} else {
quantity = new MP_INT; quantity = amt.quantity;
mpz_init_set(MPZ(quantity), MPZ(amt.quantity)); quantity->ref++;
}
commodity = amt.commodity; commodity = amt.commodity;
precision = amt.precision; precision = amt.precision;
assert(commodity);
} }
amount_t& amount_t::operator=(const std::string& value) amount_t& amount_t::operator=(const std::string& value)
@ -167,29 +232,30 @@ amount_t& amount_t::operator=(const char * value)
// assignment operator // assignment operator
amount_t& amount_t::operator=(const amount_t& amt) amount_t& amount_t::operator=(const amount_t& amt)
{ {
if (amt.quantity) { if (amt.quantity)
_copy(amt); _copy(amt);
} else { else if (quantity)
commodity = amt.commodity; _clear();
precision = amt.precision;
}
return *this; return *this;
} }
amount_t& amount_t::operator=(const bool value) amount_t& amount_t::operator=(const bool value)
{ {
if (! value) { if (! value) {
if (quantity) { if (quantity)
_clear(); _clear();
quantity = NULL;
commodity = NULL;
precision = 0;
}
} else { } else {
commodity = commodity_t::null_commodity; commodity = commodity_t::null_commodity;
precision = 0; precision = 0;
quantity = new MP_INT; if (! quantity) {
mpz_init_set(MPZ(quantity), true_value); _init();
}
else if (quantity->ref > 1) {
_release();
_init();
}
mpz_set(MPZ(quantity), true_value);
} }
return *this; return *this;
} }
@ -197,15 +263,18 @@ amount_t& amount_t::operator=(const bool value)
amount_t& amount_t::operator=(const int value) amount_t& amount_t::operator=(const int value)
{ {
if (value == 0) { if (value == 0) {
if (quantity) { if (quantity)
_clear(); _clear();
quantity = NULL;
commodity = NULL;
precision = 0;
}
} else { } else {
commodity = commodity_t::null_commodity; commodity = commodity_t::null_commodity;
precision = 0; precision = 0;
if (! quantity) {
_init();
}
else if (quantity->ref > 1) {
_release();
_init();
}
mpz_set_si(MPZ(quantity), value); mpz_set_si(MPZ(quantity), value);
} }
return *this; return *this;
@ -214,15 +283,18 @@ amount_t& amount_t::operator=(const int value)
amount_t& amount_t::operator=(const unsigned int value) amount_t& amount_t::operator=(const unsigned int value)
{ {
if (value == 0) { if (value == 0) {
if (quantity) { if (quantity)
_clear(); _clear();
quantity = NULL;
commodity = NULL;
precision = 0;
}
} else { } else {
commodity = commodity_t::null_commodity; commodity = commodity_t::null_commodity;
precision = 0; precision = 0;
if (! quantity) {
_init();
}
else if (quantity->ref > 1) {
_release();
_init();
}
mpz_set_ui(MPZ(quantity), value); mpz_set_ui(MPZ(quantity), value);
} }
return *this; return *this;
@ -231,16 +303,19 @@ amount_t& amount_t::operator=(const unsigned int value)
amount_t& amount_t::operator=(const double value) amount_t& amount_t::operator=(const double value)
{ {
if (value == 0.0) { if (value == 0.0) {
if (quantity) { if (quantity)
_clear(); _clear();
quantity = NULL;
commodity = NULL;
precision = 0;
}
} else { } else {
commodity = commodity_t::null_commodity; commodity = commodity_t::null_commodity;
// jww (2004-08-20): How do I calculate? // jww (2004-08-20): How do I calculate?
precision = 0; precision = 0;
if (! quantity) {
_init();
}
else if (quantity->ref > 1) {
_release();
_init();
}
mpz_set_d(MPZ(quantity), value); mpz_set_d(MPZ(quantity), value);
} }
return *this; return *this;
@ -252,6 +327,8 @@ void amount_t::_resize(int prec)
if (prec == precision) if (prec == precision)
return; return;
_dup();
if (prec < precision) { if (prec < precision) {
mpz_ui_pow_ui(divisor, 10, precision - prec); mpz_ui_pow_ui(divisor, 10, precision - prec);
mpz_tdiv_q(MPZ(quantity), MPZ(quantity), divisor); mpz_tdiv_q(MPZ(quantity), MPZ(quantity), divisor);
@ -272,6 +349,8 @@ amount_t& amount_t::operator OP(const amount_t& amt) \
_init(); \ _init(); \
commodity = amt.commodity; \ commodity = amt.commodity; \
precision = amt.precision; \ precision = amt.precision; \
} else { \
_dup(); \
} \ } \
\ \
if (commodity != amt.commodity) \ if (commodity != amt.commodity) \
@ -298,8 +377,10 @@ DEF_OPERATOR(-=, mpz_sub)
// unary negation // unary negation
amount_t& amount_t::negate() amount_t& amount_t::negate()
{ {
if (quantity) if (quantity) {
_dup();
mpz_ui_sub(MPZ(quantity), 0, MPZ(quantity)); mpz_ui_sub(MPZ(quantity), 0, MPZ(quantity));
}
return *this; return *this;
} }
@ -454,12 +535,10 @@ amount_t::operator bool() const
return mpz_sgn(MPZ(quantity)) != 0; return mpz_sgn(MPZ(quantity)) != 0;
} else { } else {
assert(commodity); assert(commodity);
mpz_t temp; mpz_set(temp, MPZ(quantity));
mpz_init_set(temp, MPZ(quantity));
mpz_ui_pow_ui(divisor, 10, precision - commodity->precision); mpz_ui_pow_ui(divisor, 10, precision - commodity->precision);
mpz_tdiv_q(temp, temp, divisor); mpz_tdiv_q(temp, temp, divisor);
bool zero = mpz_sgn(temp) == 0; bool zero = mpz_sgn(temp) == 0;
mpz_clear(temp);
return ! zero; return ! zero;
} }
} else { } else {
@ -481,6 +560,8 @@ amount_t& amount_t::operator*=(const amount_t& amt)
if (! amt.quantity || ! quantity) if (! amt.quantity || ! quantity)
return *this; return *this;
_dup();
mpz_mul(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity)); mpz_mul(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity));
precision += amt.precision; precision += amt.precision;
@ -495,6 +576,8 @@ amount_t& amount_t::operator/=(const amount_t& amt)
if (! amt.quantity) if (! amt.quantity)
throw amount_error("Divide by zero"); throw amount_error("Divide by zero");
_dup();
// Increase the value's precision, to capture fractional parts after // Increase the value's precision, to capture fractional parts after
// the divide. // the divide.
mpz_ui_pow_ui(divisor, 10, amt.precision + 6); mpz_ui_pow_ui(divisor, 10, amt.precision + 6);
@ -514,6 +597,7 @@ amount_t amount_t::round(int prec) const
return *this; return *this;
} else { } else {
amount_t temp = *this; amount_t temp = *this;
temp._dup();
mpz_round(MPZ(temp.quantity), MPZ(temp.quantity), mpz_round(MPZ(temp.quantity), MPZ(temp.quantity),
precision, prec == -1 ? commodity->precision : prec); precision, prec == -1 ? commodity->precision : prec);
return temp; return temp;
@ -591,9 +675,6 @@ std::ostream& operator<<(std::ostream& out, const amount_t& amt)
std::list<std::string> strs; std::list<std::string> strs;
char buf[4]; char buf[4];
mpz_t temp;
mpz_init(temp);
for (int powers = 0; true; powers += 3) { for (int powers = 0; true; powers += 3) {
if (powers > 0) { if (powers > 0) {
mpz_ui_pow_ui(divisor, 10, powers); mpz_ui_pow_ui(divisor, 10, powers);
@ -622,8 +703,6 @@ std::ostream& operator<<(std::ostream& out, const amount_t& amt)
printed = true; printed = true;
} }
mpz_clear(temp);
} }
if (amt.commodity->precision) { if (amt.commodity->precision) {
@ -696,7 +775,8 @@ void amount_t::parse(std::istream& in)
std::string quant; std::string quant;
unsigned int flags = COMMODITY_STYLE_DEFAULTS;; unsigned int flags = COMMODITY_STYLE_DEFAULTS;;
if (! quantity) if (quantity)
_release();
_init(); _init();
char c = peek_next_nonws(in); char c = peek_next_nonws(in);
@ -771,64 +851,80 @@ void amount_t::parse(std::istream& in)
delete[] buf; delete[] buf;
} }
// If necessary, amounts may be recorded in a binary file textually.
// This offers little advantage, and requires binary<->decimal
// conversion each time the file is saved or loaded.
//
//#define WRITE_AMOUNTS_TEXTUALLY
static char buf[4096]; static char buf[4096];
static int index = 0;
void amount_t::write_quantity(std::ostream& out) const void amount_t::write_quantity(std::ostream& out) const
{ {
unsigned short len; char byte;
if (quantity) {
#ifdef WRITE_AMOUNTS_TEXTUALLY if (! quantity) {
mpz_get_str(buf, 10, MPZ(quantity)); byte = 0;
len = std::strlen(buf); out.write(&byte, sizeof(byte));
#else return;
}
if (quantity->index == 0) {
quantity->index = ++index;
byte = 1;
out.write(&byte, sizeof(byte));
std::size_t size; std::size_t size;
mpz_export(buf, &size, 1, sizeof(int), 0, 0, MPZ(quantity)); mpz_export(buf, &size, 1, sizeof(int), 0, 0, MPZ(quantity));
len = size * sizeof(int); unsigned short len = size * sizeof(int);
#endif
out.write((char *)&len, sizeof(len)); out.write((char *)&len, sizeof(len));
if (len) { if (len) {
out.write(buf, len); out.write(buf, len);
#ifndef WRITE_AMOUNTS_TEXTUALLY
char negative = mpz_sgn(MPZ(quantity)) < 0 ? 1 : 0; byte = mpz_sgn(MPZ(quantity)) < 0 ? 1 : 0;
out.write(&negative, sizeof(negative)); out.write(&byte, sizeof(byte));
#endif
out.write((char *)&precision, sizeof(precision)); out.write((char *)&precision, sizeof(precision));
} }
} else { } else {
len = 0; assert(quantity->ref > 1);
out.write((char *)&len, sizeof(len));
// Since this value has already been written, we simply write
// out a reference to which one it was.
byte = 2;
out.write(&byte, sizeof(byte));
out.write((char *)&quantity->index, sizeof(quantity->index));
} }
} }
void amount_t::read_quantity(std::istream& in) void amount_t::read_quantity(std::istream& in)
{ {
assert(! quantity);
char byte;
in.read(&byte, sizeof(byte));
if (byte == 0)
return;
if (byte == 1) {
_init();
bigints.push_back(quantity);
unsigned short len; unsigned short len;
in.read((char *)&len, sizeof(len)); in.read((char *)&len, sizeof(len));
if (len) {
in.read(buf, len); in.read(buf, len);
if (! quantity) mpz_import(MPZ(quantity), len / sizeof(int), 1, sizeof(int), 0, 0, buf);
_init();
#ifdef WRITE_AMOUNTS_TEXTUALLY
buf[len] = '\0';
mpz_set_str(MPZ(quantity), buf, 10);
#else
char negative; char negative;
in.read(&negative, sizeof(negative)); in.read(&negative, sizeof(negative));
mpz_import(MPZ(quantity), len / sizeof(int), 1, sizeof(int), 0, 0, buf);
if (negative) if (negative)
mpz_neg(MPZ(quantity), MPZ(quantity)); mpz_neg(MPZ(quantity), MPZ(quantity));
#endif
in.read((char *)&precision, sizeof(precision)); in.read((char *)&precision, sizeof(precision));
} else { } else {
if (quantity) unsigned int index;
_clear(); in.read((char *)&index, sizeof(index));
quantity = NULL; assert(index <= bigints.size());
quantity = bigints[index - 1];
quantity->ref++;
} }
} }

View file

@ -13,15 +13,24 @@ class commodity_t;
class amount_t class amount_t
{ {
typedef void * base_type;
void _init(); void _init();
void _copy(const amount_t& amt); void _copy(const amount_t& amt);
void _clear(); void _release();
void _dup();
void _resize(int prec); void _resize(int prec);
void _clear() {
if (quantity)
_release();
quantity = NULL;
commodity = NULL;
precision = 0;
}
public: public:
base_type quantity; // amount, to MAX_PRECISION struct bigint_t;
bigint_t * quantity; // amount, to MAX_PRECISION
unsigned short precision; unsigned short precision;
commodity_t * commodity; commodity_t * commodity;
@ -40,14 +49,14 @@ class amount_t
if (amt.quantity) { if (amt.quantity) {
_copy(amt); _copy(amt);
} else { } else {
commodity = amt.commodity; precision = 0;
precision = amt.precision; commodity = NULL;
} }
} }
amount_t(const std::string& value) { amount_t(const std::string& value) : quantity(NULL) {
parse(value); parse(value);
} }
amount_t(const char * value) { amount_t(const char * value) : quantity(NULL) {
parse(value); parse(value);
} }
amount_t(const bool value); amount_t(const bool value);
@ -58,7 +67,7 @@ class amount_t
// destructor // destructor
~amount_t() { ~amount_t() {
if (quantity) if (quantity)
_clear(); _release();
} }
// assignment operator // assignment operator

View file

@ -1,13 +1,7 @@
#include "ledger.h" #include "ledger.h"
#include "binary.h" #include "binary.h"
#include <vector>
#include <fstream>
#include <sstream>
#include <cstring>
#include <ctime> #include <ctime>
#include <cctype>
#include <sys/stat.h> #include <sys/stat.h>
#define TIMELOG_SUPPORT 1 #define TIMELOG_SUPPORT 1
@ -26,11 +20,11 @@ bool binary_parser_t::test(std::istream& in) const
return magic == binary_magic_number; return magic == binary_magic_number;
} }
static std::vector<account_t *> accounts; static std::deque<account_t *> accounts;
static account_t::ident_t ident; static account_t::ident_t ident;
static std::vector<commodity_t *> commodities; static std::deque<commodity_t *> commodities;
static commodity_t::ident_t c_ident; static commodity_t::ident_t c_ident;
std::deque<amount_t::bigint_t *> bigints;
#if RELEASE_LEVEL >= ALPHA #if RELEASE_LEVEL >= ALPHA
#define read_binary_guard(in, id) { \ #define read_binary_guard(in, id) { \
@ -258,6 +252,7 @@ unsigned int read_binary_journal(std::istream& in,
accounts.clear(); accounts.clear();
commodities.clear(); commodities.clear();
bigints.clear();
return count; return count;
} }

View file

@ -3,6 +3,8 @@
#include "parser.h" #include "parser.h"
#include <deque>
namespace ledger { namespace ledger {
class binary_parser_t : public parser_t class binary_parser_t : public parser_t
@ -16,6 +18,8 @@ class binary_parser_t : public parser_t
const std::string * original_file = NULL); const std::string * original_file = NULL);
}; };
extern std::deque<amount_t::bigint_t *> bigints;
void write_binary_journal(std::ostream& out, void write_binary_journal(std::ostream& out,
journal_t * journal, journal_t * journal,
strings_list * files = NULL); strings_list * files = NULL);

View file

@ -81,6 +81,19 @@ namespace ledger {
std::ostream * debug_stream = &std::cerr; std::ostream * debug_stream = &std::cerr;
bool free_debug_stream = false; bool free_debug_stream = false;
bool _debug_active(const char * const cls) {
if (char * debug = std::getenv("DEBUG_CLASS")) {
static const char * error;
static int erroffset;
static int ovec[30];
static pcre * class_regexp = pcre_compile(debug, PCRE_CASELESS,
&error, &erroffset, NULL);
return pcre_exec(class_regexp, NULL, cls, std::strlen(cls),
0, 0, ovec, 30) >= 0;
}
return false;
}
static struct init_streams { static struct init_streams {
init_streams() { init_streams() {
// If debugging is enabled and DEBUG_FILE is set, all debugging // If debugging is enabled and DEBUG_FILE is set, all debugging

13
debug.h
View file

@ -68,18 +68,7 @@ extern std::ostream * warning_stream;
extern std::ostream * debug_stream; extern std::ostream * debug_stream;
extern bool free_debug_stream; extern bool free_debug_stream;
inline bool _debug_active(const char * const cls) { bool _debug_active(const char * const cls);
if (char * debug = std::getenv("DEBUG_CLASS")) {
static const char * error;
static int erroffset;
static int ovec[30];
static pcre * class_regexp = pcre_compile(debug, PCRE_CASELESS,
&error, &erroffset, NULL);
return pcre_exec(class_regexp, NULL, cls, std::strlen(cls),
0, 0, ovec, 30) >= 0;
}
return false;
}
#define DEBUG_CLASS(cls) static const char * const _debug_cls = (cls) #define DEBUG_CLASS(cls) static const char * const _debug_cls = (cls)