Update performance is influenced by the similar factors as Insert Performance. The main factors include: configuration, disk access times, complexity of objects.
The following chapters provide some simple tests showing the influence of the above-mentioned factors. The test results are provided for Toshiba Sattelite Pro A120 notebook with 1,5Gb RAM 120GB ATA drive running on Vista and may be different on a different environment.
The following Item class is used in most of the tests:
UpdatePerformanceBenchmark.cs: Item
private void Update(Object item)
{
objectContainer.Store(item);
}
// end Update
private void RunDifferentObjectsTest()
{
System.Console.WriteLine("Update test with different objects");
int objectsToUpdate = 90;
int updated = objectsToUpdate;
InitDifferentObjectsTest();
Clean();
System.Console.WriteLine(" - primitive object with int field");
Open(Configure());
StoreSimplest();
IObjectSet result = objectContainer.QueryByExample(null);
StartTimer();
for (int i = 0; i < objectsToUpdate; i++)
{
if (result.HasNext())
{
SimplestItem item = (SimplestItem)result.Next();
item._id = 1;
Update(item);
}
else
{
updated = i;
break;
}
}
StopTimer("Updated " + updated + " items");
Close();
Clean();
Open(Configure());
System.Console.WriteLine(" - object with string field");
Store();
updated = objectsToUpdate;
result = objectContainer.QueryByExample(null);
StartTimer();
for (int i = 0; i < objectsToUpdate; i++)
{
if (result.HasNext())
{
Item item = (Item)result.Next();
item._name = "Updated";
Update(item);
}
else
{
updated = i;
break;
}
}
StopTimer("Updated " + updated + " items");
Close();
Clean();
Open(Configure());
System.Console.WriteLine(" - object with StringBuilder field");
StoreWithStringBuilder();
updated = objectsToUpdate;
result = objectContainer.QueryByExample(null);
StartTimer();
for (int i = 0; i < objectsToUpdate; i++)
{
if (result.HasNext())
{
ItemWithStringBuilder item = (ItemWithStringBuilder)result.Next();
item._name = new StringBuilder("Updated");
Update(item);
}
else
{
updated = i;
break;
}
}
StopTimer("Updated " + updated + " items");
Close();
Clean();
Open(Configure());
System.Console.WriteLine(" - object with int array field");
StoreWithArray();
updated = objectsToUpdate;
result = objectContainer.QueryByExample(null);
StartTimer();
for (int i = 0; i < objectsToUpdate; i++)
{
if (result.HasNext())
{
ItemWithArray item = (ItemWithArray)result.Next();
item._id = new int[] { 1, 2, 3 };
Update(item);
}
else
{
updated = i;
break;
}
}
StopTimer("Updated " + updated + " items");
Close();
Clean();
Open(Configure());
System.Console.WriteLine(" - object with ArrayList field");
StoreWithArrayList();
updated = objectsToUpdate;
result = objectContainer.QueryByExample(null);
StartTimer();
for (int i = 0; i < objectsToUpdate; i++)
{
if (result.HasNext())
{
ItemWithArrayList item = (ItemWithArrayList)result.Next();
item._ids = new ArrayList();
Update(item);
}
else
{
updated = i;
break;
}
}
StopTimer("Updated " + updated + " items");
Close();
}
// end RunDifferentObjectsTest
private void RunIndexTest()
{
System.Console.WriteLine("Update test for objects with and without indexed fields");
int objectsToUpdate = 100;
Init();
System.Console.WriteLine("Updating " + objectsToUpdate + " of " + _count + " objects");
Clean();
Open(Configure());
Store();
UpdateItems(objectsToUpdate);
Close();
Clean();
Init();
System.Console.WriteLine("Updating " + objectsToUpdate + " of " + _count + " objects with indexed field");
Open(ConfigureIndexTest());
Store();
UpdateItems(objectsToUpdate);
Close();
}
// end RunIndexTest
private void Init()
{
_count = 1000;
_depth = 90;
_isClientServer = false;
}
// end Init
private void InitDifferentObjectsTest()
{
_count = 1000;
_depth = 1;
_isClientServer = false;
}
// end InitDifferentObjectsTest
private void InitForClientServer()
{
_count = 1000;
_depth = 90;
_isClientServer = true;
_host = "localhost";
}
// end InitForClientServer
private void InitForRamDriveTest()
{
_count = 30000;
_depth = 1;
_filePath = "r:\\performance.db4o";
_isClientServer = false;
}
// end InitForRamDriveTest
private void InitForHardDriveTest()
{
_count = 10000;
_depth = 3;
_filePath = "performance.db4o";
_isClientServer = false;
}
// end InitForHardDriveTest
private void InitForCommitTest()
{
_count = 10000;
_commitInterval = 1000;
_depth = 3;
_isClientServer = false;
}
// end InitForCommitTest
private void Clean()
{
File.Delete(_filePath);
}
// end Clean
private IConfiguration Configure()
{
IConfiguration config = Db4oFactory.NewConfiguration();
// using MemoryIoAdapter improves the performance
// by replacing the costly disk IO operations with
// memory access
config.Io(new MemoryIoAdapter());
return config;
}
// end Configure
private IConfiguration ConfigureTP()
{
IConfiguration config = Db4oFactory.NewConfiguration();
// With Transparent Persistence enabled only modified
// objects are written to disk. This allows to achieve
// better performance
config.ObjectClass(typeof(Item)).CascadeOnUpdate(true);
return config;
}
// end ConfigureTP
private IConfiguration ConfigureCascade()
{
IConfiguration config = Db4oFactory.NewConfiguration();
// CascadeOnUpdate can be a performance-killer for
// deep object hierarchies
config.ObjectClass(typeof(Item)).CascadeOnUpdate(true);
return config;
}
// end ConfigureCascade
private IConfiguration ConfigureIndexTest()
{
IConfiguration config = Db4oFactory.NewConfiguration();
config.Io(new MemoryIoAdapter());
config.ObjectClass(typeof(Item)).ObjectField("_name").Indexed(true);
return config;
}
// end ConfigureIndexTest
private IConfiguration ConfigureForCommitTest()
{
IConfiguration config = Db4oFactory.NewConfiguration();
// the Commit information is physically written
// and in the correct order
config.FlushFileBuffers(true);
return config;
}
// end ConfigureForCommitTest
private IConfiguration ConfigureClientServer()
{
IConfiguration config = Db4oFactory.NewConfiguration();
config.ClientServer().SingleThreadedClient(true);
return config;
}
// end ConfigureClientServer
private IConfiguration ConfigureDriveTest()
{
IConfiguration config = Db4oFactory.NewConfiguration();
config.FlushFileBuffers(true);
return config;
}
// end ConfigureDriveTest
private void Store()
{
StartTimer();
for (int i = 0; i < _count; i++)
{
Item item = new Item("level" + i, null);
for (int j = 1; j < _depth; j++)
{
item = new Item("level" + i + "/" + j, item);
}
objectContainer.Store(item);
}
objectContainer.Commit();
StopTimer("Store " + TotalObjects() + " objects");
}
// end Store
private void StoreActivatableItems()
{
StartTimer();
for (int i = 0; i < _count; i++)
{
ActivatableItem item = new ActivatableItem("level" + i, null);
for (int j = 1; j < _depth; j++)
{
item = new ActivatableItem("level" + i + "/" + j, item);
}
objectContainer.Store(item);
}
objectContainer.Commit();
StopTimer("Store " + TotalObjects() + " objects");
}
// end StoreActivatableItems
private void StoreInherited()
{
StartTimer();
for (int i = 0; i < _count; i++)
{
ItemDerived item = new ItemDerived("level" + i, null);
for (int j = 1; j < _depth; j++)
{
item = new ItemDerived("level" + i + "/" + j, item);
}
objectContainer.Store(item);
}
objectContainer.Commit();
StopTimer("Store " + TotalObjects() + " objects");
}
// end StoreInherited
private void StoreWithStringBuilder()
{
StartTimer();
for (int i = 0; i < _count; i++)
{
ItemWithStringBuilder item = new ItemWithStringBuilder(new StringBuilder("level" + i), null);
for (int j = 1; j < _depth; j++)
{
item = new ItemWithStringBuilder(new StringBuilder("level" + i + "/" + j), item);
}
objectContainer.Store(item);
}
objectContainer.Commit();
StopTimer("Store " + TotalObjects() + " objects");
}
// end StoreWithStringBuilder
private void StoreSimplest()
{
StartTimer();
for (int i = 0; i < _count; i++)
{
SimplestItem item = new SimplestItem(i, null);
for (int j = 1; j < _depth; j++)
{
item = new SimplestItem(i, item);
}
objectContainer.Store(item);
}
objectContainer.Commit();
StopTimer("Store " + TotalObjects() + " objects");
}
// end StoreSimplest
private void StoreWithArray()
{
StartTimer();
int[] array = new int[] { 1, 2, 3, 4 };
for (int i = 0; i < _count; i++)
{
int[] id = new int[] { 1, 2, 3, 4 };
ItemWithArray item = new ItemWithArray(id, null);
for (int j = 1; j < _depth; j++)
{
int[] id1 = new int[] { 1, 2, 3, 4 };
item = new ItemWithArray(id1, item);
}
objectContainer.Store(item);
}
objectContainer.Commit();
StopTimer("Store " + TotalObjects() + " objects");
}
// end StoreWithArray
private void StoreWithArrayList()
{
StartTimer();
ArrayList idList = new ArrayList();
idList.Add(1);
idList.Add(2);
idList.Add(3);
idList.Add(4);
for (int i = 0; i < _count; i++)
{
ArrayList ids = new ArrayList();
ids.AddRange(idList);
ItemWithArrayList item = new ItemWithArrayList(ids, null);
for (int j = 1; j < _depth; j++)
{
ArrayList ids1 = new ArrayList();
ids1.AddRange(idList);
item = new ItemWithArrayList(ids1, item);
}
objectContainer.Store(item);
}
objectContainer.Commit();
StopTimer("Store " + TotalObjects() + " objects");
}
// end StoreWithArrayList
private int TotalObjects()
{
return _count * _depth;
}
// end TotalObjects
private void Open(IConfiguration config)
{
if (_isClientServer)
{
int port = TCP ? PORT : 0;
string user = "db4o";
string password = user;
objectServer = Db4oFactory.OpenServer(_filePath, port);
objectServer.GrantAccess(user, password);
objectContainer = TCP ? Db4oFactory.OpenClient(_host, port, user,
password) : objectServer.OpenClient();
}
else
{
objectContainer = Db4oFactory.OpenFile(config, _filePath);
}
}
// end Open
private void Close()
{
objectContainer.Close();
if (_isClientServer)
{
objectServer.Close();
}
}
//end Close
private void StartTimer()
{
_startTime = DateTime.Now.Ticks;
}
// end StartTimer
private void StopTimer(string message)
{
long stop = DateTime.Now.Ticks;
long duration = stop - _startTime;
System.Console.WriteLine(message + ": " + duration + " ticks");
}
// end StopTimer
public class Item
{
public string _name;
public Item _child;
public Item()
{
}
public Item(string name, Item child)
{
_name = name;
_child = child;
}
}
More Reading: