Tag - performance
Entries feed
2012, Tuesday February 14
By A.Bouchez on 2012, Tuesday February 14, 21:39 - mORMot Framework
Here is the definition of "cache", as stated by Wikipedia:
In computer engineering, a cache is a component that transparently
stores data so that future requests for that data can be served faster. The
data that is stored within a cache might be values that have been computed
earlier or duplicates of original values that are stored elsewhere. If
requested data is contained in the cache (cache hit), this request can be
served by simply reading the cache, which is comparatively faster. Otherwise
(cache miss), the data has to be recomputed or fetched from its original
storage location, which is comparatively slower. Hence, the greater the number
of requests that can be served from the cache, the faster the overall system
performance becomes.
To be cost efficient and to enable an efficient use of data, caches are
relatively small. Nevertheless, caches have proven themselves in many areas of
computing because access patterns in typical computer applications have
locality of reference. References exhibit temporal locality if data is
requested again that has been recently requested already. References exhibit
spatial locality if data is requested that is physically stored close to data
that has been requested already.
In our ORM framework, since performance was one goal since the beginning,
cache has been implemented at four levels:
- Statement cache for reuse of SQL prepared statements, and bound parameters
on the fly - note that this cache is available not only for the
SQlite3 database engine, but also for any external engine;
- Global JSON result cache at the database level, which is flushed globally
on any
INSERT / UPDATE;
- Tuned record cache at the CRUD/RESTful level for specified tables or
records on the server side;
- Tuned record cache at the CRUD/RESTful level for specified tables or
records on the client side.
Continue reading...
2012, Thursday February 9
By A.Bouchez on 2012, Thursday February 9, 10:54 - Pascal Programing
Some Delphi users even do not know the existence of the
SetString function.
As stated by the official
documentation:
procedure SetString(var S: String; Buffer: PChar; Length: Integer);
For a long string variable, SetString sets S to reference a newly allocated
string of the given length. If the Buffer parameter is not nil, SetString then
copies Len characters from Buffer into the string; otherwise, the content of
the new string is left uninitialized. If there is not enough memory available
to create the string, an EOutOfMemory exception is raised. Following a call to
SetString, S is guaranteed to reference a unique string (a string with a
reference count of one).
Some have
noticed that in our libraries, I sometimes use SetString
instead of SetLength.
When the string is already allocated, it could be faster to use
SetString, if you are sure that you will overwrite the string
content.
Continue reading...
2012, Tuesday January 17
By A.Bouchez on 2012, Tuesday January 17, 23:02 - Open Source libraries
The Open Source SynDBExplorer tool
has been enhanced these days.
Main new features are:
- Execution of the current selected text (if any) instead of the whole memo
content;
- "Exec & Export" new button, for direct export to file.
I really like the selection execution feature - this speed up SQL
process a lot, and allow to switch from one statement to another.
And the new exporting features are opening new possibilities.
Continue reading...
2011, Thursday December 8
By A.Bouchez on 2011, Thursday December 8, 10:09 - Pascal Programing
Among all trolling subject in forums, you'll find out the
great Garbage Collection theme.
Fashion languages rely on it. At the core of the .Net and Java framework,
and all scripting languages (like JavaScript, Perl, Python or Ruby), you'll
find a Garbage Collector. New developers, just released from schools, do
learn about handling memory only in theory, and just can't understand how is
memory allocated - we all have seen such rookies involved in Delphi code
maintenance, leaking memory as much as they type. In fact, most of them did not
understood how a computer works. I warned you this will be a trolling
subject.
And, in Delphi, there is no such collector. We handle memory in
several ways:
- Creating static variables - e.g. on the stack, inside a
class
or globally;
- Creating objects with
class instances allocated on heap - in
at least three ways: with a try..finally Free block, with a
TComponent ownership model in the VCL, or by using
an interface (which creates an hidden try..finally
Free block);
- Creating reference-counted variables, i.e.
string, array
of, interface or variant kind of
variables.
It is a bit complex, but it is also deadly powerful. You have several memory
allocation models at hand, which can be very handy if you want to tune your
performance and let program scale. Just like manual recycling at home will save
the planet. Some programmers will tell you that it's a waste of cell brain,
typing and time. Linux kernel gurus would not say so, I'm afraid.
Then came the big Apple company, which presented its new ARC model
(introduced in Mac OS X 10.7 Lion) as a huge benefit for
Objective-C in comparison with the Garbage Collection model. And
let's face it: this ARC just sounds like the Delphi memory model.
Continue reading...
2011, Wednesday November 23
By A.Bouchez on 2011, Wednesday November 23, 08:37 - Pascal Programing
Luigi Sandon wrote on
Embarcadero's forum:
And then you ask yourself: "why use a native compiler if its code may be
even slower than jitted one?". Hope the new developers will also develop better
and faster code - and not viceversa.
Embarcadero is just following the Wirth's law slower than others:
"Software is getting slower more rapidly than hardware becomes faster"
Speed is only a matter of compiler for mathematical computing intensive
tasks.
Most of the time, in real apps (like business apps), the main speed issue is
more the framework size (and the number of dll invoked), memory consumption,
and general design (e.g. how caching and SQL are written).
Delphi, Java or .Net can do slow apps.
Delphi, Java or .Net can do fast apps.
You can do small and fast stand-alone apps with Delphi, running from Windows
2000 to Windows 8.
It is not possible with Java nor .Net.
This is the main difference IMHO with native code and JIT - about memory
use, ease of distribution and no need of an external runtime framework.
Continue reading...
2011, Tuesday November 8
By A.Bouchez on 2011, Tuesday November 8, 21:51 - Pascal Programing
The currency type is the standard Delphi type to be used when
storing and handling monetary values. It will avoid any rounding problems, with
4 decimals precision. It is able to safely store numbers in the range
-922337203685477.5808 .. 922337203685477.5807. Should be enough for your pocket
change.
As stated by the official Delphi documentation:
Currency is a fixed-point data type that minimizes rounding errors in
monetary calculations. On the Win32 platform, it is stored as a scaled 64-bit
integer with the four least significant digits implicitly representing decimal
places. When mixed with other real types in assignments and expressions,
Currency values are automatically divided or multiplied by 10000.
In fact, this type matches the corresponding OLE and
.Net implementation of currency, and the one used by
most database providers (when it comes to money, a dedicated type is worth the
cost in a "rich man's world"). It is still implemented the same in the
Win64 platform (since XE 2). The Int64 binary
representation of the currency type (i.e. value*10000
as accessible via PInt64(aCurrencyValue)^) is a safe and fast
implementation pattern.
In our framework, we tried to avoid any unnecessary conversion to float
values when dealing with currency values. Some dedicated functions
have been implemented for fast and secure access to currency
published properties via RTTI, especially when converting values to or from
JSON text. Using the Int64 binary representation can be not only
faster, but also safer: you will avoid any rounding problem which may be
introduced by the conversion to a float type. Rounding issues are a nightmare
to track - it sounds safe to have a framework handling natively a
currency type from the ground up.
Continue reading...
2011, Sunday August 28
By A.Bouchez on 2011, Sunday August 28, 08:57 - Pascal Programing
Writing working multi-threaded code is not easy - it's even
hard, as as a Delphi expert just wrote
in his blog.
In fact, the first step into multi-thread application development could
be:
"protect your shared variables with locks (aka critical sections), because
you are not sure that the data you read/write is the same for all threads".
The CPU per-core cache is just one of the possible issues, which will lead
into reading wrong values. Another issue which may lead into race condition is
two threads writing to a resource at the same time: it's impossible to know
which value will be stored afterward.
Continue reading...
2011, Saturday August 20
By A.Bouchez on 2011, Saturday August 20, 10:24 - Open Source libraries
We already shipped a sophisticated set of logging classes some month ago.
Since then, its features have been enhanced, and the system has been deeply
interfaced with our main ORM framework. Now almost all low-level or high-level
operations can be logged on request.
But since the log files tend to be huge (for instance, if you set the
logging for our unitary tests, the 6,000,000 unitary tests creates a 280 MB log
file), a log viewer was definitively in need.
Continue reading...
2011, Sunday August 7
By A.Bouchez on 2011, Sunday August 7, 09:46 - mORMot Framework
Our downloadable documentation
has been enhanced, and contains now a description about the main feature of
the near-to-come 1.15 version, i.e. "database agnosticism".
The core database of our mORMot / SQLite3 framework uses the
SQLite3 library, which is a Free, Secure, Zero-Configuration,
Server-less, Single Stable Cross-Platform Database File database engine.
As stated below, you can use any other database access layer, if you wish. A
fast in-memory engine is included, and can be used instead or together with the
SQLite3 engine. Since revision 1.15 of the framework you may be able
to access any remote database, and use one or more OleDB (or
Oracle) connections to store your precious ORM objects. The
SQlite3 will be used as the main SQL engine, able to JOIN all those
tables, thanks to its Virtual Table unique feature.
Continue reading...
2011, Monday July 4
By A.Bouchez on 2011, Monday July 4, 06:09 - SQLite3 Framework
If you want to implement an HTTP client access in your application, you may
consider several choices:
- Use the provided
Indy components;
- Use third-party components like Synapse, ICS or
your own WinSock-based wrapper;
- Use WinINet;
- Use WinHTTP.
For our ORM, we tried to avoid external dependencies, and did not have the
need of all Indy's features and overhead.
We fist wrote our own WinSock wrapper, then tried out
WinInet.
When used on our testing benchmark, we found out that WinINet was
dead slow.
Then we tried WinHTTP, the new API provided by Microsoft, and we found
out this was blazing fast. As fast as direct WinSock access,
without the need of writing all the wrapper code.
Continue reading...
2011, Sunday July 3
By A.Bouchez on 2011, Sunday July 3, 20:12 - SQLite3 Framework
Here is what wikipedia states at http://en.wikipedia.org/wiki/Shared_nothing_architecture:
A shared nothing architecture (SN) is a distributed computing
architecture in which each node is independent and self-sufficient, and there
is no single point of contention across the system. People typically contrast
SN with systems that keep a large amount of centrally-stored state information,
whether in a database, an application server, or any other similar single point
of contention.
This is just one approach of "sharding". Sharding is indeed related to a
shared nothing architecture - once sharded, each shard can live in a totally
separate logical schema instance.
"I sharded, therefore it scales"...
You can do this in Delphi... and opens a new world of scaling
opportunities... Just as Google, Facebook, or
eBay do...
Continue reading...
2011, Friday July 1
By A.Bouchez on 2011, Friday July 1, 09:09 - mORMot Framework
That's it, our SynOleDB unit seems alive and running well.
OLE DB (Object Linking and Embedding, Database, sometimes written
as OLEDB or OLE-DB) is an API designed by Microsoft for accessing data from a
variety of sources in a uniform manner. It was designed as a higher-level
replacement for, and successor to, ODBC, extending its feature set to support a
wider variety of non-relational databases, such as object databases and
spreadsheets that do not necessarily implement SQL.
SynOleDB unit implementation has been made with several points in
mind:
- Tested with SQL Server 2008 R2 and Oracle 11g providers
from Microsoft and Oracle;
- Ability to be truly Unicode, even with pre-Unicode version of Delphi (like
Delphi 7 or 2007);
- Could access any local or remote Database, from any version of Delphi,
since it doesn't use the DB.pas unit or any related part of the VCL
(even the Delphi 7 personal or the Turbo Explorer editions), just for
free;
- Handle NULL or BLOB content for parameters and results;
- Avoid most memory copy or unnecessary allocation: we tried to access the
data directly from the retrieved data buffer, just as given from
OleDB;
- Was therefore designed to achieve the best performance possible: most time
is spent in OleDB: the code layer added to the OleDB customer is very
thin;
- True OOP architecture, to be used with any OleDB provider (allowing custom
parameters or such), and even without OleDB (in the future, direct access to
any DB client could be used);
- Could be safely used in a multi-threaded application/server (with one
TOleDBConnection per thread);
- Allow parameter bindings of requests, with fast access to any parameter or
column name (thanks to
TDynArrayHashed);
- Late binding of column values in Delphi code;
- Direct JSON content creation, with no temporary data copy nor
allocation;
- Designed to be used with our
mORMot ORM, but could be used stand-alone (a full Delphi 7 client
executable is just about 200 KB), or even in any existing Delphi application,
thanks to a
TQuery-like wrapper.
Continue reading...
2011, Thursday June 16
By A.Bouchez on 2011, Thursday June 16, 21:55 - Pascal Programing
After a
question on StackOverflow, I wanted to comment about the speed of generated
code by diverse Delphi compiler versions.
Since performance matters when we write general purpose libraries like ours,
we have some feedback to propose:
Continue reading...
2011, Tuesday June 7
By A.Bouchez on 2011, Tuesday June 7, 22:45 - Pascal Programing
In order to let our TSynLog logging class intercept all
exceptions, we use the low-level global RtlUnwindProc pointer,
defined in System.pas.
Alas, under Delphi 5, this global RtlUnwindProc variable is not
existing. The code calls directly the RtlUnWind Windows API
function, with no hope of custom interception.
Two solutions could be envisaged:
- Modify the
Sytem.pas source code, adding the new
RtlUnwindProc variable, just like Delphi 7;
- Patch the assembler code, directly in the process memory.
The first solution is simple. Even if compiling System.pas is a
bit more difficult than compiling other units, we already made that for our
Enhanced
RTL units. But you'll have to change the whole build chain in order to
use your custom System.dcu instead of the default one. And some
third-party units (only available in .dcu form) may not like the
fast that the System.pas interface changed...
So we used the second solution: change the assembler code in the running
process memory, to let call our RtlUnwindProc variable instead of
the Windows API.
Continue reading...
By A.Bouchez on 2011, Tuesday June 7, 21:40 - Pascal Programing
For our ORM, we needed a class variable to be available for each
TSQLRecord class type.
This variable is used to store the properties of this class type, i.e. the
database Table properties (e.g. table and column names and types) associated
with a particular TSQLRecord class, from which all our ORM objects
inherit.
The class var statement was not enough for us:
- It's not available on earlier Delphi versions, and we try to have our
framework work with Delphi 6-7 up to XE;
- This class var instance will be shared by all classes inheriting
from the class where it is defined - and we need ONE instance PER class type,
not ONE instance for ALL
We needed to find another way to implement this class
variable.
An unused VMT slot in the class type description was identified, then each
class definition was patched in the process memory to contain our class
variable.
Continue reading...
2011, Thursday June 2
By A.Bouchez on 2011, Thursday June 2, 16:42 - SQLite3 Framework
The SQLite3 engine defines some standard SQL functions, like
abs() min() max() or upper().
A complete list is available at http://www.sqlite.org/lang_corefunc.html
One of the greatest SQLite3 feature is the ability to define custom
SQL functions in high-level language. In fact, its C API allows to implement
new functions which may be called within a SQL query. In other database engine,
such functions are usually named UDF (for User Defined Functions).
Our framework allows you to add easily such custom functions, directly from
Delphi classes.
Continue reading...
By A.Bouchez on 2011, Thursday June 2, 16:16 - SQLite3 Framework
When it deals with parsing some (textual) content, two directions are
usually envisaged. In the XML world, you have usually to make a choice
between:
- A DOM parser, which creates an in-memory tree structure of objects mapping
the XML nodes;
- A SAX parser, which reads the XML content, then call pre-defined
events for each XML content element.
In fact, DOM parsers use internally a SAX parser to read the XML content.
Therefore, with the overhead of object creation and their property
initialization, DOM parsers are typically three to five times slower than SAX.
But, DOM parsers are much more powerful for handling the data: as soon as it's
mapped in native objects, code can access with no time to any given node,
whereas a SAX-based access will have to read again the whole XML content.
Most JSON parser available in Delphi use a DOM-like approach. For instance,
the DBXJSON unit included
since Delphi 2010 or the SuperObject or DWS libraries create a
class instance mapping each JSON node.
In a JSON-based Client-Server ORM like ours, profiling shows that a lot of
time is spent in JSON parsing, on both Client and Server side. Therefore, we
tried to optimize this part of the library.
Continue reading...
By A.Bouchez on 2011, Thursday June 2, 15:18 - SQLite3 Framework
When use the so-called BATCH sequences?
In a standard Client-Server architecture, especially with the common
understanding (and most implementations) of a RESTful service, any Add /
Update / Delete method call requires a back and forth flow to then from
the remote server.
In case of a remote connection via the Internet (or a slow network), you
could have some 100 ms of latency: it's just the "ping" timing, i.e. the time
spent for your IP packet to go to the server, then back to you.
If you are making a number of such calls (e.g. add 1000 records), you'll
have 100*1000 ms = 100 s = 1:40 min just because of this network latency!
The BATCH sequence allows you to regroup those statements into just ONE
remote call. Internally, it builds a JSON stream, then post this stream at once
to the server. Then the server answers at once, after having performed all the
modifications.
Continue reading...
By A.Bouchez on 2011, Thursday June 2, 15:00 - SQLite3 Framework
An R-Tree is a special index that is designed for doing range queries.
R-Trees are most commonly used in geospatial systems where each entry is a
rectangle with minimum and maximum X and Y coordinates. Given a query
rectangle, an R-Tree is able to quickly find all entries that are contained
within the query rectangle or which overlap the query rectangle.
This idea is easily extended to three dimensions for use in CAD
systems. R-Trees also find use in time-domain range look-ups. For example,
suppose a database records the starting and ending times for a large number of
events. A R-Tree is able to quickly find all events, for example, that were
active at any time during a given time interval, or all events that started
during a particular time interval, or all events that both started and ended
within a given time interval. And so forth. See http://www.sqlite.org/rtree.html
Since the 2010-06-25 source code repository update, the RTREE extension is
compiled by default within all supplied .obj files of the
framework.
A dedicated ORM class, named TSQLRecordRTree, is available to
create such tables. It inherits from TSQLRecordVirtual, like the
other virtual tables types (e.g. TSQLRecordFTS3 or
our custom virtual
tables).
Continue reading...
2011, Friday May 20
By A.Bouchez on 2011, Friday May 20, 21:02 - Pascal Programing
How to make your software run fast, especially in a
multi-threaded architecture?
We tried to remove the Memory Manager scaling problems in our SynScaleMM. It worked as expected in a
multi-threaded server environment. Scaling is much better than FastMM4, for
some critical tests. But it's not ready for production yet...
To be honest, the Memory Manager is perhaps not the bigger bottleneck
in Multi-Threaded applications.
Here are some (not dogmatic, just from experiment and knowledge of low-level
Delphi RTL) advice if you want to write FAST multi-threaded application in
Delphi.
Continue reading...