Рассмотрите следующий код:
class A
{
B* b; // an A object owns a B object
A() : b(NULL) { } // we don't know what b will be when constructing A
void calledVeryOften(…)
{
if (b)
delete b;
b = new B(param1, param2, param3, param4);
}
};
Моя цель: Я должен максимизировать производительность, которая, в этом случае, означает минимизировать выделения объема памяти.
Очевидная вещь сделать здесь состоит в том, чтобы измениться B* b;
к B b;
. Я вижу две проблемы с этим подходом:
b
в конструкторе. Так как я не знаю что b
будет, это означает, что я должен передать фиктивные значения конструктору B. Который, IMO, ужасно.calledVeryOften()
, Я должен буду сделать что-то вроде этого: b = B(…)
, который является неправильным по двум причинам: b
не будет назван.b
, тогда деструктор временного экземпляра назовут. Копии и вызова деструктора можно было избежать. Хуже, называя деструктор мог очень хорошо привести к нежелательному поведению.Таким образом, что решения, я должен избегать использования new
? Следует иметь в виду что:
Мне понравился ответ Клаима, поэтому я написал его очень быстро. Я не претендую на идеальную правильность, но мне это кажется неплохим. (т.е. единственное тестирование, которое он имеет, - это образец main
ниже)
Это общий ленивый инициализатор. Пространство для объекта выделяется один раз, и объект начинается с нуля. Затем вы можете создать
, перезаписав предыдущие объекты, без выделения новой памяти.
Он реализует все необходимые конструкторы, деструктор, копирование / присваивание, свопинг, ядда-ядда. Вот и все:
#include <cassert>
#include <new>
template <typename T>
class lazy_object
{
public:
// types
typedef T value_type;
typedef const T const_value_type;
typedef value_type& reference;
typedef const_value_type& const_reference;
typedef value_type* pointer;
typedef const_value_type* const_pointer;
// creation
lazy_object(void) :
mObject(0),
mBuffer(::operator new(sizeof(T)))
{
}
lazy_object(const lazy_object& pRhs) :
mObject(0),
mBuffer(::operator new(sizeof(T)))
{
if (pRhs.exists())
{
mObject = new (buffer()) T(pRhs.get());
}
}
lazy_object& operator=(lazy_object pRhs)
{
pRhs.swap(*this);
return *this;
}
~lazy_object(void)
{
destroy();
::operator delete(mBuffer);
}
// need to make multiple versions of this.
// variadic templates/Boost.PreProccesor
// would help immensely. For now, I give
// two, but it's easy to make more.
void create(void)
{
destroy();
mObject = new (buffer()) T();
}
template <typename A1>
void create(const A1 pA1)
{
destroy();
mObject = new (buffer()) T(pA1);
}
void destroy(void)
{
if (exists())
{
mObject->~T();
mObject = 0;
}
}
void swap(lazy_object& pRhs)
{
std::swap(mObject, pRhs.mObject);
std::swap(mBuffer, pRhs.mBuffer);
}
// access
reference get(void)
{
return *get_ptr();
}
const_reference get(void) const
{
return *get_ptr();
}
pointer get_ptr(void)
{
assert(exists());
return mObject;
}
const_pointer get_ptr(void) const
{
assert(exists());
return mObject;
}
void* buffer(void)
{
return mBuffer;
}
// query
const bool exists(void) const
{
return mObject != 0;
}
private:
// members
pointer mObject;
void* mBuffer;
};
// explicit swaps for generality
template <typename T>
void swap(lazy_object<T>& pLhs, lazy_object<T>& pRhs)
{
pLhs.swap(pRhs);
}
// if the above code is in a namespace, don't put this in it!
// specializations in global namespace std are allowed.
namespace std
{
template <typename T>
void swap(lazy_object<T>& pLhs, lazy_object<T>& pRhs)
{
pLhs.swap(pRhs);
}
}
// test use
#include <iostream>
int main(void)
{
// basic usage
lazy_object<int> i;
i.create();
i.get() = 5;
std::cout << i.get() << std::endl;
// asserts (not created yet)
lazy_object<double> d;
std::cout << d.get() << std::endl;
}
В вашем случае просто создайте члена в своем классе: lazy_object
и все готово. Никаких ручных выпусков или создания копий-конструкторов, деструкторов и т. Д. Обо всем позаботятся в вашем красивом небольшом повторно используемом классе. :)
Устранена необходимость в векторе, это должно сэкономить немного места и все такое.
Здесь используются align_storage
и alignment_of
для использования стека вместо кучи. Я использовал boost , но эта функция существует как в TR1, так и в C ++ 0x. Мы теряем возможность копирования, а значит и свопинга.
#include <boost/type_traits/aligned_storage.hpp>
#include <cassert>
#include <new>
template <typename T>
class lazy_object_stack
{
public:
// types
typedef T value_type;
typedef const T const_value_type;
typedef value_type& reference;
typedef const_value_type& const_reference;
typedef value_type* pointer;
typedef const_value_type* const_pointer;
// creation
lazy_object_stack(void) :
mObject(0)
{
}
~lazy_object_stack(void)
{
destroy();
}
// need to make multiple versions of this.
// variadic templates/Boost.PreProccesor
// would help immensely. For now, I give
// two, but it's easy to make more.
void create(void)
{
destroy();
mObject = new (buffer()) T();
}
template <typename A1>
void create(const A1 pA1)
{
destroy();
mObject = new (buffer()) T(pA1);
}
void destroy(void)
{
if (exists())
{
mObject->~T();
mObject = 0;
}
}
// access
reference get(void)
{
return *get_ptr();
}
const_reference get(void) const
{
return *get_ptr();
}
pointer get_ptr(void)
{
assert(exists());
return mObject;
}
const_pointer get_ptr(void) const
{
assert(exists());
return mObject;
}
void* buffer(void)
{
return mBuffer.address();
}
// query
const bool exists(void) const
{
return mObject != 0;
}
private:
// types
typedef boost::aligned_storage<sizeof(T),
boost::alignment_of<T>::value> storage_type;
// members
pointer mObject;
storage_type mBuffer;
// non-copyable
lazy_object_stack(const lazy_object_stack& pRhs);
lazy_object_stack& operator=(lazy_object_stack pRhs);
};
// test use
#include <iostream>
int main(void)
{
// basic usage
lazy_object_stack<int> i;
i.create();
i.get() = 5;
std::cout << i.get() << std::endl;
// asserts (not created yet)
lazy_object_stack<double> d;
std::cout << d.get() << std::endl;
}
И поехали.
Просто оформируйте память, необходимую для B (через пул или вручную) и повторно используйте его каждый раз, когда вы удаляете / новое вместо перераспределения каждый раз.
Пример:
class A
{
B* b; // an A object owns a B object
bool initialized;
public:
A() : b( malloc( sizeof(B) ) ), initialized(false) { } // We reserve memory for b
~A() { if(initialized) destroy(); free(b); } // release memory only once we don't use it anymore
void calledVeryOften(…)
{
if (initialized)
destroy();
create();
}
private:
void destroy() { b->~B(); initialized = false; } // hand call to the destructor
void create( param1, param2, param3, param4 )
{
b = new (b) B( param1, param2, param3, param4 ); // in place new : only construct, don't allocate but use the memory that the provided pointer point to
initialized = true;
}
};
В некоторых случаях пул или objectPool могут быть лучшей реализацией одной и той же идеи.
Стоимость строительства / разрушения будет тогда только зависимым от конструктора и деструктора класса B.
Как насчет выделения памяти для B один раз (или для его наибольшего возможного варианта) и используя Размещение Новое ?
A будет хранить Char Memb [SizeOf (Big Gestb)];
и B *
. Конечно, вам нужно будет вручную вызвать деструкторы, но память не будет выделена / освобождена.
void* p = memB;
B* b = new(p) SomeB();
...
b->~B(); // explicit destructor call when needed.
Если B
корректно реализует его оператор назначения копирования b = b ( ...)
Не следует вызывать любой деструктор на b
. Это самое очевидное решение вашей проблемы.
Если, однако, B
B не может быть соответствующим образом «по умолчанию» инициализирован вами может сделать что-то вроде этого. Я бы только рекомендовал этот подход как последний курорт, так как очень трудно получить безопасную. Непроверенные, и очень, вероятно, с угловыми случаями исключения Bugs:
// Used to clean up raw memory of construction of B fails
struct PlacementHelper
{
PlacementHelper() : placement(NULL)
{
}
~PlacementHelper()
{
operator delete(placement);
}
void* placement;
};
void calledVeryOften(....)
{
PlacementHelper hp;
if (b == NULL)
{
hp.placement = operator new(sizeof(B));
}
else
{
hp.placement = b;
b->~B();
b = NULL; // We can't let b be non-null but point at an invalid B
}
// If construction throws, hp will clean up the raw memory
b = new (placement) B(param1, param2, param3, param4);
// Stop hp from cleaning up; b points at a valid object
hp.placement = NULL;
}
Также можно использовать БД Berkley. Он хорошо работает с программами C и реализует дерево B +.
-121--3995725-попробуйте добавить атрибут [Авторизовать] к методам действия
-121--2663127-Вы уверены, что выделение памяти является узким местом? Является ли конструктор B тривиально быстрым?
Если выделение памяти является реальной проблемой, то размещение новых или некоторых других решений здесь вполне может помочь.
Если типы и диапазоны парама [1.. 4] являются разумными, а конструктор B «тяжелым», вы также можете рассмотреть возможность использования кэшированного набора B. Это предполагает, что вам фактически разрешено иметь более одного за раз, что он не является фронтальным ресурсом, например.
ERM, есть ли какой-то причиной, вы не можете это сделать?
A() : b(new B()) { }
void calledVeryOften(…)
{
b->setValues(param1, param2, param3, param4);
}
(или установить их индивидуально, поскольку у вас нет доступа к B
класс - эти ценности DO имеют мутатор-методы, верно?)
Как и другие уже предложили: попробуйте разместить новый ..
Вот полный пример:
#include <new>
#include <stdio.h>
class B
{
public:
int dummy;
B (int arg)
{
dummy = arg;
printf ("C'Tor called\n");
}
~B ()
{
printf ("D'tor called\n");
}
};
void called_often (B * arg)
{
// call D'tor without freeing memory:
arg->~B();
// call C'tor without allocating memory:
arg = new(arg) B(10);
}
int main (int argc, char **args)
{
B test(1);
called_often (&test);
}
Просто возьмите кучу ранее использованных B и используйте их повторно.
Предпочтительной альтернативой скриптлетам является язык выражения JSTL; здесь - хороший обзор. Вам нужно будет добавить tallib так:
<%@ taglib uri='http://java.sun.com/jsp/jstl/core' prefix='c' %>
В качестве примера, JSTL предоставляет кучу неявных объектов, которые дают вам материал, который вам нужен; требуется pageContext.request
.
Таким образом, можно заменить <% request.getRequestURI% >
на $ {pageContext.request.requestURI}
.
Можно выполнить условия с помощью < c: if >
тэги.
В первую очередь - вы правы, что вам нужно использовать класс java.sql.Timestamp, так как класс java.sql.Date явно не представляет определенное время дней (скорее, он пытается представить полночь по Гринвичу).
Что касается вашей ошибки - вы не предоставили достаточно информации, чтобы сделать что-то большее, чем догадаться (для фактического определения причины потребуется посмотреть как конфигурацию Hibernate, так и класс). Однако , если вы просто изменили класс поля в классе Java, то, конечно, вам также придется обновить сопоставление Hibernate. Если вы не сделали последнее, это, вероятно, приведет к вашему несоответствию. Попробуйте явно указать type = «timestamp»
для соответствующего сопоставления.
EDIT: Если вы используете аннотации, вы обновили аннотацию для этого свойства на @ Temporal (TemporalType.TIMESTAMP)
? Если вы этого не сделали, то вам нужно будет (а если вы это сделали, вы должны были так сказать: -)).
Быстрая проверка утверждения Мартина Йорка о том, что это преждевременная оптимизация, и что новые/удаленные оптимизированы далеко за пределы способности простых программистов улучшить. Очевидно, что вопроснику придется успеть свой собственный код, чтобы увидеть, помогает ли ему избегание нового/удаления, но мне кажется, что для определенных классов и его использования это будет иметь большое значение:
#include <iostream>
#include <vector>
int g_construct = 0;
int g_destruct = 0;
struct A {
std::vector<int> vec;
A (int a, int b) : vec((a*b) % 2) { ++g_construct; }
~A() {
++g_destruct;
}
};
int main() {
const int times = 10*1000*1000;
#if DYNAMIC
std::cout << "dynamic\n";
A *x = new A(1,3);
for (int i = 0; i < times; ++i) {
delete x;
x = new A(i,3);
}
#else
std::cout << "automatic\n";
char x[sizeof(A)];
A* yzz = new (x) A(1,3);
for (int i = 0; i < times; ++i) {
yzz->~A();
new (x) A(i,3);
}
#endif
std::cout << g_construct << " constructors and " << g_destruct << " destructors\n";
}
$ g++ allocperf.cpp -oallocperf -O3 -DDYNAMIC=0 -g && time ./allocperf
automatic
10000001 constructors and 10000000 destructors
real 0m7.718s
user 0m7.671s
sys 0m0.030s
$ g++ allocperf.cpp -oallocperf -O3 -DDYNAMIC=1 -g && time ./allocperf
dynamic
10000001 constructors and 10000000 destructors
real 0m15.188s
user 0m15.077s
sys 0m0.047s
Это примерно то, что я ожидал: код в стиле GMan (destruct/placement new) занимает вдвое больше времени, и, предположительно, делает вдвое больше распределения. Если векторный член A заменен на int, то код в стиле GMan занимает долю секунды. Это GCC 3.
$ g++-4 allocperf.cpp -oallocperf -O3 -DDYNAMIC=1 -g && time ./allocperf
dynamic
10000001 constructors and 10000000 destructors
real 0m5.969s
user 0m5.905s
sys 0m0.030s
$ g++-4 allocperf.cpp -oallocperf -O3 -DDYNAMIC=0 -g && time ./allocperf
automatic
10000001 constructors and 10000000 destructors
real 0m2.047s
user 0m1.983s
sys 0m0.000s
Но я не уверен в этом: теперь удаление/новая версия занимает в три раза больше времени, чем уничтожение/размещение новой версии.
[Edit: Я думаю, я понял это - GCC 4 быстрее на 0-размерных векторах, фактически вычитая постоянное время из обеих версий кода. При изменении (a * b)% 2
на (a * b)% 2 + 1
восстанавливается отношение времени 2:1: 3,7s против 7,5]
Обратите внимание, что я не предпринял никаких специальных шагов для правильного выравнивания массива стека, но при печати адреса он выровнен по 16.
Кроме того, -g не влияет на время. Я оставил его случайно после того, как посмотрел на обломок, чтобы проверить, что -O3 не полностью снял петлю. Указатели назвали yzz, потому что поиск «y» прошел не так хорошо, как я надеялся. Но я только что сбежал без этого.
См. ответы на CSS/JavaScript Используйте Div для серого на странице .
-121--3853742-Сначала необходимо разделить прочитанное здесь и там о транзакциях SQL Server на 2 отдельных случая: локальный и распределенный.
Локальные транзакции SQL :
Распределенные транзакции :
Таким образом, когда клиент создает .Net TransacityScope и под этой областью транзакции выполняет несколько запросов на одном сервере, все эти запросы являются локальными транзакциями, зарегистрированными в распределенной транзакции. Простой пример:
class Program
{
static string sqlBatch = @"
set nocount on;
declare @i int;
set @i = 0;
while @i < 100000
begin
insert into test (a) values (replicate('a',100));
set @i = @i+1;
end";
static void Main(string[] args)
{
try
{
TransactionOptions to = new TransactionOptions();
to.IsolationLevel = IsolationLevel.ReadCommitted;
using (TransactionScope scp = new TransactionScope(TransactionScopeOption.Required, to))
{
using (SqlConnection connA = new SqlConnection(Settings.Default.connString))
{
connA.Open();
using (SqlConnection connB = new SqlConnection(Settings.Default.connString))
{
connB.Open();
SqlCommand cmdA = new SqlCommand(sqlBatch, connA);
SqlCommand cmdB = new SqlCommand(sqlBatch, connB);
IAsyncResult arA = cmdA.BeginExecuteNonQuery();
IAsyncResult arB = cmdB.BeginExecuteNonQuery();
WaitHandle.WaitAll(new WaitHandle[] { arA.AsyncWaitHandle, arB.AsyncWaitHandle });
cmdA.EndExecuteNonQuery(arA);
cmdB.EndExecuteNonQuery(arB);
}
}
scp.Complete();
}
}
catch (Exception e)
{
Console.Error.Write(e);
}
}
}
Создайте фиктивную тестовую таблицу
create table test (id int not null identity(1,1) primary key, a varchar(100));
и запустите код в образце. Вы увидите, что оба запроса выполняются параллельно, каждый из которых вставляет 100 000 строк в таблицу, затем оба фиксируют, когда область транзакции завершена. Таким образом, проблемы, которые вы видите, не связаны ни с SQL Server, ни с TransacityScope, они могут легко справиться с описанным сценарием. Более того, код очень прост и прост, и нет необходимости создавать зависимые транзакции, клонировать или продвигать транзакции.
Обновлено
Использование явных потоков и зависимых транзакций:
private class ThreadState
{
public DependentTransaction Transaction {get; set;}
public EventWaitHandle Done {get; set;}
public SqlConnection Connection { get; set; }
}
static void Main(string[] args)
{
try
{
TransactionOptions to = new TransactionOptions();
to.IsolationLevel = IsolationLevel.ReadCommitted;
using (TransactionScope scp = new TransactionScope(TransactionScopeOption.Required, to))
{
ThreadState stateA = new ThreadState
{
Transaction = Transaction.Current.DependentClone(DependentCloneOption.BlockCommitUntilComplete),
Done = new AutoResetEvent(false),
Connection = new SqlConnection(Settings.Default.connString),
};
stateA.Connection.Open();
ThreadState stateB = new ThreadState
{
Transaction = Transaction.Current.DependentClone(DependentCloneOption.BlockCommitUntilComplete),
Done = new AutoResetEvent(false),
Connection = new SqlConnection(Settings.Default.connString),
};
stateB.Connection.Open();
ThreadPool.QueueUserWorkItem(new WaitCallback(Worker), stateA);
ThreadPool.QueueUserWorkItem(new WaitCallback(Worker), stateB);
WaitHandle.WaitAll(new WaitHandle[] { stateA.Done, stateB.Done });
scp.Complete();
//TODO: dispose the open connections
}
}
catch (Exception e)
{
Console.Error.Write(e);
}
}
private static void Worker(object args)
{
Debug.Assert(args is ThreadState);
ThreadState state = (ThreadState) args;
try
{
using (TransactionScope scp = new TransactionScope(state.Transaction))
{
SqlCommand cmd = new SqlCommand(sqlBatch, state.Connection);
cmd.ExecuteNonQuery();
scp.Complete();
}
state.Transaction.Complete();
}
catch (Exception e)
{
Console.Error.WriteLine(e);
state.Transaction.Rollback();
}
finally
{
state.Done.Set();
}
}
-121--4321020- Я бы пошел с boost:: scoped _ ptr здесь:
class A: boost::noncopyable
{
typedef boost::scoped_ptr<B> b_ptr;
b_ptr pb_;
public:
A() : pb_() {}
void calledVeryOften( /*…*/ )
{
pb_.reset( new B( params )); // old instance deallocated
// safely use *pb_ as reference to instance of B
}
};
Нет необходимости в ручном деструкторе, A
не является копируемым, как это должно быть в вашем оригинальном коде, не утечка памяти на
Я бы предложил переосмыслить дизайн, хотя если вам нужно очень часто перераспределять какой-то внутренний государственный объект. Ознакомьтесь с параметрами Flyweight и State узоров.