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

296
amount.cc
View file

@ -1,18 +1,64 @@
#include "ledger.h"
#include "binary.h"
#include "error.h"
#include "util.h"
#include "gmp.h"
#include <deque>
#define MPZ(x) ((MP_INT *)(x))
#include "gmp.h"
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 true_value;
static struct init_amounts {
init_amounts() {
mpz_init(temp);
mpz_init(divisor);
mpz_init(true_value);
mpz_set_ui(true_value, 1);
@ -21,6 +67,7 @@ static struct init_amounts {
~init_amounts() {
mpz_clear(true_value);
mpz_clear(divisor);
mpz_clear(temp);
}
#endif
} 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)
: quantity(NULL), commodity(NULL)
{
if (value) {
commodity = commodity_t::null_commodity;
quantity = new bigint_t(true_value);
precision = 0;
quantity = new MP_INT;
mpz_init_set(MPZ(quantity), true_value);
commodity = commodity_t::null_commodity;
} else {
quantity = NULL;
precision = 0;
commodity = NULL;
}
}
amount_t::amount_t(const int value)
: quantity(NULL), commodity(NULL)
{
if (value != 0) {
_init();
commodity = commodity_t::null_commodity;
precision = 0;
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)
: quantity(NULL), commodity(NULL)
{
if (value != 0) {
_init();
commodity = commodity_t::null_commodity;
precision = 0;
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)
: quantity(NULL), commodity(NULL)
{
if (value != 0.0) {
_init();
commodity = commodity_t::null_commodity;
// jww (2004-08-20): How do I calculate?
precision = 0;
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));
delete (MP_INT *) quantity;
if (--quantity->ref == 0)
delete quantity;
}
void amount_t::_init()
{
quantity = new MP_INT;
mpz_init(MPZ(quantity));
quantity = new bigint_t;
}
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)
{
if (quantity) {
mpz_set(MPZ(quantity), MPZ(amt.quantity));
} else {
quantity = new MP_INT;
mpz_init_set(MPZ(quantity), MPZ(amt.quantity));
}
if (quantity)
_release();
quantity = amt.quantity;
quantity->ref++;
commodity = amt.commodity;
precision = amt.precision;
assert(commodity);
}
amount_t& amount_t::operator=(const std::string& value)
@ -167,29 +232,30 @@ amount_t& amount_t::operator=(const char * value)
// assignment operator
amount_t& amount_t::operator=(const amount_t& amt)
{
if (amt.quantity) {
if (amt.quantity)
_copy(amt);
} else {
commodity = amt.commodity;
precision = amt.precision;
}
else if (quantity)
_clear();
return *this;
}
amount_t& amount_t::operator=(const bool value)
{
if (! value) {
if (quantity) {
if (quantity)
_clear();
quantity = NULL;
commodity = NULL;
precision = 0;
}
} else {
commodity = commodity_t::null_commodity;
precision = 0;
quantity = new MP_INT;
mpz_init_set(MPZ(quantity), true_value);
if (! quantity) {
_init();
}
else if (quantity->ref > 1) {
_release();
_init();
}
mpz_set(MPZ(quantity), true_value);
}
return *this;
}
@ -197,15 +263,18 @@ amount_t& amount_t::operator=(const bool value)
amount_t& amount_t::operator=(const int value)
{
if (value == 0) {
if (quantity) {
if (quantity)
_clear();
quantity = NULL;
commodity = NULL;
precision = 0;
}
} else {
commodity = commodity_t::null_commodity;
precision = 0;
if (! quantity) {
_init();
}
else if (quantity->ref > 1) {
_release();
_init();
}
mpz_set_si(MPZ(quantity), value);
}
return *this;
@ -214,15 +283,18 @@ amount_t& amount_t::operator=(const int value)
amount_t& amount_t::operator=(const unsigned int value)
{
if (value == 0) {
if (quantity) {
if (quantity)
_clear();
quantity = NULL;
commodity = NULL;
precision = 0;
}
} else {
commodity = commodity_t::null_commodity;
precision = 0;
if (! quantity) {
_init();
}
else if (quantity->ref > 1) {
_release();
_init();
}
mpz_set_ui(MPZ(quantity), value);
}
return *this;
@ -231,16 +303,19 @@ amount_t& amount_t::operator=(const unsigned int value)
amount_t& amount_t::operator=(const double value)
{
if (value == 0.0) {
if (quantity) {
if (quantity)
_clear();
quantity = NULL;
commodity = NULL;
precision = 0;
}
} else {
commodity = commodity_t::null_commodity;
// jww (2004-08-20): How do I calculate?
precision = 0;
if (! quantity) {
_init();
}
else if (quantity->ref > 1) {
_release();
_init();
}
mpz_set_d(MPZ(quantity), value);
}
return *this;
@ -252,6 +327,8 @@ void amount_t::_resize(int prec)
if (prec == precision)
return;
_dup();
if (prec < precision) {
mpz_ui_pow_ui(divisor, 10, precision - prec);
mpz_tdiv_q(MPZ(quantity), MPZ(quantity), divisor);
@ -272,6 +349,8 @@ amount_t& amount_t::operator OP(const amount_t& amt) \
_init(); \
commodity = amt.commodity; \
precision = amt.precision; \
} else { \
_dup(); \
} \
\
if (commodity != amt.commodity) \
@ -298,8 +377,10 @@ DEF_OPERATOR(-=, mpz_sub)
// unary negation
amount_t& amount_t::negate()
{
if (quantity)
if (quantity) {
_dup();
mpz_ui_sub(MPZ(quantity), 0, MPZ(quantity));
}
return *this;
}
@ -454,12 +535,10 @@ amount_t::operator bool() const
return mpz_sgn(MPZ(quantity)) != 0;
} else {
assert(commodity);
mpz_t temp;
mpz_init_set(temp, MPZ(quantity));
mpz_set(temp, MPZ(quantity));
mpz_ui_pow_ui(divisor, 10, precision - commodity->precision);
mpz_tdiv_q(temp, temp, divisor);
bool zero = mpz_sgn(temp) == 0;
mpz_clear(temp);
return ! zero;
}
} else {
@ -481,6 +560,8 @@ amount_t& amount_t::operator*=(const amount_t& amt)
if (! amt.quantity || ! quantity)
return *this;
_dup();
mpz_mul(MPZ(quantity), MPZ(quantity), MPZ(amt.quantity));
precision += amt.precision;
@ -495,6 +576,8 @@ amount_t& amount_t::operator/=(const amount_t& amt)
if (! amt.quantity)
throw amount_error("Divide by zero");
_dup();
// Increase the value's precision, to capture fractional parts after
// the divide.
mpz_ui_pow_ui(divisor, 10, amt.precision + 6);
@ -514,6 +597,7 @@ amount_t amount_t::round(int prec) const
return *this;
} else {
amount_t temp = *this;
temp._dup();
mpz_round(MPZ(temp.quantity), MPZ(temp.quantity),
precision, prec == -1 ? commodity->precision : prec);
return temp;
@ -591,9 +675,6 @@ std::ostream& operator<<(std::ostream& out, const amount_t& amt)
std::list<std::string> strs;
char buf[4];
mpz_t temp;
mpz_init(temp);
for (int powers = 0; true; powers += 3) {
if (powers > 0) {
mpz_ui_pow_ui(divisor, 10, powers);
@ -622,8 +703,6 @@ std::ostream& operator<<(std::ostream& out, const amount_t& amt)
printed = true;
}
mpz_clear(temp);
}
if (amt.commodity->precision) {
@ -696,8 +775,9 @@ void amount_t::parse(std::istream& in)
std::string quant;
unsigned int flags = COMMODITY_STYLE_DEFAULTS;;
if (! quantity)
_init();
if (quantity)
_release();
_init();
char c = peek_next_nonws(in);
if (std::isdigit(c) || c == '.' || c == '-') {
@ -771,64 +851,80 @@ void amount_t::parse(std::istream& in)
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 int index = 0;
void amount_t::write_quantity(std::ostream& out) const
{
unsigned short len;
if (quantity) {
#ifdef WRITE_AMOUNTS_TEXTUALLY
mpz_get_str(buf, 10, MPZ(quantity));
len = std::strlen(buf);
#else
char byte;
if (! quantity) {
byte = 0;
out.write(&byte, sizeof(byte));
return;
}
if (quantity->index == 0) {
quantity->index = ++index;
byte = 1;
out.write(&byte, sizeof(byte));
std::size_t size;
mpz_export(buf, &size, 1, sizeof(int), 0, 0, MPZ(quantity));
len = size * sizeof(int);
#endif
unsigned short len = size * sizeof(int);
out.write((char *)&len, sizeof(len));
if (len) {
out.write(buf, len);
#ifndef WRITE_AMOUNTS_TEXTUALLY
char negative = mpz_sgn(MPZ(quantity)) < 0 ? 1 : 0;
out.write(&negative, sizeof(negative));
#endif
byte = mpz_sgn(MPZ(quantity)) < 0 ? 1 : 0;
out.write(&byte, sizeof(byte));
out.write((char *)&precision, sizeof(precision));
}
} else {
len = 0;
out.write((char *)&len, sizeof(len));
assert(quantity->ref > 1);
// 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)
{
unsigned short len;
in.read((char *)&len, sizeof(len));
if (len) {
assert(! quantity);
char byte;
in.read(&byte, sizeof(byte));
if (byte == 0)
return;
if (byte == 1) {
_init();
bigints.push_back(quantity);
unsigned short len;
in.read((char *)&len, sizeof(len));
in.read(buf, len);
if (! quantity)
_init();
#ifdef WRITE_AMOUNTS_TEXTUALLY
buf[len] = '\0';
mpz_set_str(MPZ(quantity), buf, 10);
#else
mpz_import(MPZ(quantity), len / sizeof(int), 1, sizeof(int), 0, 0, buf);
char negative;
in.read(&negative, sizeof(negative));
mpz_import(MPZ(quantity), len / sizeof(int), 1, sizeof(int), 0, 0, buf);
if (negative)
mpz_neg(MPZ(quantity), MPZ(quantity));
#endif
in.read((char *)&precision, sizeof(precision));
} else {
if (quantity)
_clear();
quantity = NULL;
unsigned int index;
in.read((char *)&index, sizeof(index));
assert(index <= bigints.size());
quantity = bigints[index - 1];
quantity->ref++;
}
}

View file

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

View file

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

View file

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

View file

@ -81,6 +81,19 @@ namespace ledger {
std::ostream * debug_stream = &std::cerr;
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 {
init_streams() {
// 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 bool free_debug_stream;
inline 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;
}
bool _debug_active(const char * const cls);
#define DEBUG_CLASS(cls) static const char * const _debug_cls = (cls)