-
Content Count
246 -
Joined
-
Last visited
-
Days Won
11
Posts posted by Primož Gabrijelčič
-
-
2 minutes ago, Zacherl said:You will need a lock prefix only, if you have concurrent threads reading, modifying (e.g. incrementing by one) and writing values.
Yes, of course. My question was related to one thread reading and one thread writing.
-
49 minutes ago, Primož Gabrijelčič said:If there is a writer - does it have to be writing with 'lock' prefix or no?
To answer myself - writer doesn't need to use 'lock'. The proof is here: https://github.com/gabr42/GpDelphiCode/tree/master/MemAtomic
The code runs tests with 1/2/4/8 byte data on offsets from 0 to 127 (relative to a well-aligned memory block). It writes out all offsets where reads/writes were not atomic.
32-bit
1:
2: 63 127
4: 61 62 63 125 126 127
8: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 1272/4 bytes: access is not atomic when straddling a cache line
8 bytes: access is never atomic
64-bit
1:
2: 63 127
4: 61 62 63 125 126 127
8: 57 58 59 60 61 62 63 121 122 123 124 125 126 1272/4/8 bytes: access is not atomic when straddling a cache line
-
4 hours ago, Zacherl said:- 2/4/8 byte reads are atomic, if executed on a P6+ and fitting in a single cache line (any sane compiler should use correct alignments by itself)
Can you please specify what exactly do you mean with this statement?
If nobody is writing to the memory then the statement is obviously true. It doesn't matter whether reads are atomic or not - the data will always be correct.
If there is a writer - does it have to be writing with 'lock' prefix or no?
-
Measurements from my i7:
Basically the same as on the Xeon. A bit larger speed difference between 32-bit DWORD and 64-bit QWORD. Remember - smaller is faster.
-
I put together a simple test measuring aligned vs. unaligned access speed. You can find it here: https://github.com/gabr42/GpDelphiCode/tree/master/MemSpeed
The code simply runs in a tight loop and does the following 1 million times:
pData^ := {$IFDEF X64}$F0F0F0F0F0F0F0F0{$ELSE}$F0F0F0F0{$ENDIF};
pData^ := {$IFDEF X64}$0F0F0F0F0F0F0F0F{$ELSE}$0F0F0F0F{$ENDIF};
All this is repeated for each offset in a 1024-byte buffer.
All above is repeated ten times.
Memory is allocated with VirtualAlloc which gives back nicely aligned blocks.
At the end, the shortest time for each offset is logged into a file of your choosing. I was only interested in relative differences so I did nothing to convert data to "real" time unit.
Warning: The code needs more than a minute to run on my slow Xeon.
If I graph the result in Excel, I get this:
There's basically no difference (besides the noise - all the junk I got installed on Windows was running along the test program).
There's no difference over the whole 1024-byte range. QWORD access in 64-bit is slightly faster than DWORD access in 32-bit and that's that.
Any contribution to the code will be welcome.
-
32 minutes ago, Zacherl said:And unaligned memory access is not slow on modern processors.
Can you please give me some references that proof your statement
I can confirm (from experience) that this is indeed true. I cannot find any definitive document about that, but it looks like since 2011/12 unaligned access doesn't hurt very much (at least on Intel platform).
Some 3rd party posts that confirm my finding:
https://lemire.me/blog/2012/05/31/data-alignment-for-speed-myth-or-reality/
https://www.reddit.com/r/programming/comments/2la6qc/data_alignment_for_speed_myth_or_reality/
- 1
-
Smart Mobile Studio 3.0.1 is released
This is the first release since 3.0. Biggest new feature is TW3LeafletMap, which lets you use OpenStreetMap. As it does not need API keys (like TW3GoogleMaps), it’s really fast and easy to use:
- Create a project
- Add a TW3LeafletMap -control on the form
- Set AutoCreateMap to true on the map control
Please see the changelog at the end of this post.
Installers
Portable installation
Smart Mobile Studio can also be downloaded with the SmartUpdate utility. Select the MASTER -channel for the official version.
Changes since 3.0
RTL
-
EventManager
- Add procedure AllowDefaultAction, that can be called from OnClick when the default action should happen. For example: To let the virtual keyboard to pop up from OnTouch.
-
Bug fixes
- Native scrolling was prevented if scrolling was done from an unknown element.
- Prevent an extra OnClick from being fired on mobile devices.
- TW3ListView: Bug fix to resizing of items.
- Bug fixes to GeoLocation. Also update the Geolocation demo.
- Deprecate PhoneGapAPI’s JGeolocation functions. SmartCL.GeoLocation.pas should be used instead.
- Fix slider so that OnMouseDown, OnMouseUp and OnMOuseMove can be used.
- TW3TabControl Tab’s OnShow was sent twice for the first tab
- SmartCL.RegEx moved to System.RegEx. Also fixed TW3RegEx.Exec to return a proper empty array instead of nil.
- Bug fix to Chart: TSeriesData.Refresh now also updates the X axis
- SmartCL.Effects: Properly handle padding and margins while doing effects.
- Fix to css-files for selected text in TW3Edit and TW3Memo
-
TW3Grid
- Added TW3ImageColumn
- Add Alignment-property to TW3ColumnHeader
- Added a new OnCellControlCreated-event, which fires when a button, toggle, progress bar or image is created. Makes it possible to change properties of the control easily.
-
Added support for OpenStreetMap using the Leaflet library
- New control: TW3LeafletMap
- New featured demo: LeafletMap
IDE/Compiler
- Fixed search path compilation issues
- Relative and absolute paths are working now
- Compiler is updated when search path is modified in options
- $I will look for include file in the project folder first
- $R supports absolute paths, wildcards, folder name extension and ($Libraries) macro
- Fix exceptions in Search
- Upgrade to UPX 3.95
- 3
-
Indeed, I got the last line wrong. Glad that you fixed it!
-
HotKeyMod := ( TextToShortcut( edHotKey.Text ) ); if cbALT.Checked then HotKeyMod := ( HotKeyMod or HOTKEYF_ALT ); if cbCTRL.Checked then HotKeyMod := ( HotKeyMod or HOTKEYF_CONTROL ); if cbSHIFT.Checked then HotKeyMod := ( HotKeyMod or HOTKEYF_SHIFT ); SLI.HotKey := (HotKeyMod SHL 8) OR (SLI.HotKey AND $FF);
- 1
-
I always execute the Set Tab Order expert by clicking on the GExperts menu and the clicking on the expert. No keyboard, no popup.
-
I'm using your experimental version 1.3.9.59 in Berlin, if that's of any help.
As I was using quite old GExperts, I now tested with 1.3.10.63 experimental and with 1.3.11.64 experimental. Works fine in both.
I have recorded a short video showing how Set Tab Order works for me (and how it had always worked).
https://www.dropbox.com/s/t4p06tm58q4pj92/settab.mp4?dl=0
IIRC, this way of setting tab order was actually the primary mode for this expert in the beginning. Delphi had its clumsy Edit, Tab order and GExperts added the "select all, activate expert" expert. IIRC in the beginning there was no dialog - tab order got set and that was that. (I could be mistaken, of course.)
-
I don't understand what "reverse order" you are talking about.
- Click on first control, shift-click on the second control, shift-click on the third control.
- GExperts, Set Tab Order
- Controls are listed in the order I clicked them: 1, 2, 3.
-
What are you actually asking? Defuse what?
-
I almost exclusively use the "select controls in the desired order, activate the expert" mode. Been using it since who-knows-when.
-
12 hours ago, Dmitry Arefiev said:But if at least 1 managed field ...
Then the initalization process is called and it uses RTTI and it is slow, indeed. But it makes no difference in almost any use-case on this planet. (Except for the few cases where it makes a big difference. 🙂 )
-
uses Spring; var ms := Shared.New(TStringList.Create); ...
- 1
-
16 hours ago, Mr. E said:Maybe there need to be a new entry in the OTL FAQ because I don't find any reference (I wasn't searching thoroughly).
This is mentioned in the book: http://www.omnithreadlibrary.com/book/chap10.html#howto-com
P.S. Make sure that you don't call any VCL code from your background threads!
- 1
-
Run it in the debugger and check where this exception pops up.
-
Using configuration records and operators to reduce number of overloaded methods
in Tips / Blogs / Tutorials / Videos
Posted
When writing libraries you sometimes want to provide users (that is, programmers) with a flexible API. If a specific part of your library can be used in different ways, you may want to provide multiple overloaded methods accepting different combinations of parameters.
For example, IOmniPipeline interface from OmniThreadLibrary implements three overloaded Stage functions.
function Stage(pipelineStage: TPipelineSimpleStageDelegate; taskConfig: IOmniTaskConfig = nil): IOmniPipeline; overload; function Stage(pipelineStage: TPipelineStageDelegate; taskConfig: IOmniTaskConfig = nil): IOmniPipeline; overload; function Stage(pipelineStage: TPipelineStageDelegateEx; taskConfig: IOmniTaskConfig = nil): IOmniPipeline; overload;
Delphi’s own System.Threading is even worse. In class TParallel, for example, there are 32 overloads of the &Forclass function. Thirty two! Not only it is hard to select appropriate function; it is also hard to decode something useful from the code completion tip. Check the image below – can you tell which overloaded version I’m trying to call? Me neither!
Because of all that, it is usually good to minimize number of overloaded methods. We can do some work by adding default parameters, but sometimes this doesn’t help. Today I’d like to present an alternative solution – configuration records and operator overloading. To simplify things, I’ll present a mostly made-up problem. You can download it from github.
An example
type TConnector = class public procedure SetupBridge(const url1, url2: string); overload; procedure SetupBridge(const url1, proto2, host2, path2: string); overload; procedure SetupBridge(const proto1, host1, path1, proto2, host2, path2: string); overload; // procedure SetupBridge(const proto1, host1, path1, url2: string); overload; end;
This class expects two URL parameters but allows the user to provide them in different forms – either as a full URL (for example, ‘http://www.thedelphigeek.com/index.html’) or as (protocol, host, path) triplets (for example, ‘http’, ‘www.thedelphigeek.com’, ‘index.html’). Besides the obvious problem of writing – and maintaining – four overloads this code exhibits another problem. We simply cannot provide all four alternatives to the user!
The problem lies in the fact that the second and fourth (commented out) overload both contain four string parameters. Delphi doesn’t allow that – and for a good reason! If we could define both at the same time, the compiler would have absolutely no idea which method to call if we write SetupBridge(‘1’, ‘2’, ‘3’, ‘4’). Both versions would be equally valid candidates!
So – strike one. We cannot even write the API that we would like to provide. Even worse – the user may get confused and may expect that we did provide the fourth version and they try to use it. Like this:
conn := TConnector.Create; try conn.SetupBridge('http://www.thedelphigeek.com/index.html', 'http://bad.horse/'); conn.SetupBridge('http://www.thedelphigeek.com/index.html', 'http', 'bad.horse', ''); conn.SetupBridge('http', 'www.thedelphigeek.com', 'index.html', 'http', 'bad.horse', ''); // this compiles, ouch: conn.SetupBridge('http', 'www.thedelphigeek.com', 'index.html', 'http://bad.horse/'); finally FreeAndNil(conn); end;
Although the last call to SetupBridge compiles, it does something that user doesn’t expect. The code calls the second SetupBridge overload and sets url 1 to ‘http’ and url 2 to (‘www.thedelphigeek.com’, ‘index.html’, ‘http://bad.horse/’). Strike two. The output of the program proves that (all ‘1:’ lines should be equal, as should be all ‘2:’ lines):
Last but not least – the API is not very good. When we need to pass lots of configuration to a method, it is better to pack the configuration into meaningful units. So – strike three and out. Let’s rewrite the code!
A solution
Records are good solution for packing configuration into meaningful units. Let’s try and rewrite the API to use record-based configuration.
TURL = record end; TConnector2 = class public procedure SetupBridge(const url1, url2: TURL); end;
Much better. Just one overload! Still, there’s a problem of putting information inside the TURL record.
I could add a bunch of properties and write:
url1.Proto := 'http'; url1.Host := 'www.thedelphigeek.com'; url1.Path := 'index.html'; url2.URL := 'http://bad.horse/'; conn2.SetupBridge(url1, url2);
Clumsy. I have to declare two variables and type lots of code. No.
I could also create two constructors and write:
conn2.SetupBridge(TURL.Create('http', 'www.thedelphigeek.com', 'index.html'), TURL.Create('http://bad.horse/')); conn2.SetupBridge(TURL.Create('http://www.thedelphigeek.com/index.html'), TURL.Create('http://bad.horse/'));
That looks better, but still – in the second SetupBridge call both TURL.Create calls look completely out of place. Do I have to pull back and rewrite my API like this?
TConnector = class public procedure SetupBridge(const url1, url2: string); overload; procedure SetupBridge(const url1: string; const url2: TURL); overload; procedure SetupBridge(const url1, url2: TURL); overload; procedure SetupBridge(const url1: TURL; const url2: string); overload; end;
Well, yes, this is a possibility. It solves the problem of supporting all four combinations and it nicely puts related information into one unit. Still, we can do better. Operators to the rescue!
I’m quite happy with the Create approach for providing an information triplet. it is the other variant – the one with just a single URL parameter – that I would like to simplify. I would just like to provide a simple string when the URL is in one piece.
To support that, we only have to add an Implicit operator which converts a string into a TURL record. (Another one converting TURL into a string is also helpful as it simplifies the use of TURL inside the TConnector class.)
Here is full implementation for TURL:
TURL = record strict private FUrl: string; public constructor Create(const proto, host, path: string); class operator Implicit(const url: string): TURL; class operator Implicit(const url: TURL): string; end; constructor TURL.Create(const proto, host, path: string); begin FURL := proto + '://' + host + '/' + path; end; class operator TURL.Implicit(const url: string): TURL; begin Result.FURL := url; end; class operator TURL.Implicit(const url: TURL): string; begin Result := url.FURL; end;
Simple, isn’t it? The implementation uses the fact that TConnector has no need to access separate URL components. It is quite happy with the concatenated version, created in the TURL.Create.
This allows us to provide parameters in a way that is – at least for me – a good compromise. It allows for a (relatively) simple use and the implementation is also (relatively) simple:
conn2 := TConnector2.Create; try conn2.SetupBridge('http://www.thedelphigeek.com/index.html', 'http://bad.horse/'); conn2.SetupBridge('http://www.thedelphigeek.com/index.html', TURL.Create('http', 'bad.horse', '')); conn2.SetupBridge(TURL.Create('http', 'www.thedelphigeek.com', 'index.html'), TURL.Create('http', 'bad.horse', '')); // this works as expected: conn2.SetupBridge(TURL.Create('http', 'www.thedelphigeek.com', 'index.html'), 'http://bad.horse/'); finally FreeAndNil(conn2); end;
The output from the program shows that everything is OK now: