Profile cover photo
Profile photo
David Heffernan
programming, cycling, gin
programming, cycling, gin
About
Communities and Collections
View all
Posts

I'm wondering if somebody with access to Tokyo source code could check something for me before I submit a bug report. I just want to be sure that the defect still exists.

In Vcl.Imaging.GIFImg, in my XE7 version there is this function:

function TDIBWriter.GetScanline(Row: integer): pointer;
begin
{$ifdef PIXELFORMAT_SLOW}
NeedDIB;

if (FDIBBits = nil) then
Error(sNoDIB);
with FDIBInfo^.bmiHeader do
begin
if (Row < 0) or (Row >= Height) then
raise EInvalidGraphicOperation.Create(SScanLine);
GDIFlush;

if biHeight > 0 then // bottom-up DIB
Row := biHeight - Row - 1;
Result := PByte(Cardinal(FDIBBits) + Cardinal(Row) * AlignBit(biWidth, biBitCount, 32));
end;
{$else}
Result := FBitmap.ScanLine[Row];
{$endif}
end;

The sub-expression Cardinal(FDIBBits) is a pointer truncation bug under the 64 bit compiler. That sub-expression should be NativeUInt(FDIBBits).

I wonder if Emba have done any top down memory allocation testing.

Anyway, would it be possible for somebody with access to the latest Tokyo source code to check whether or not the defect is still present.

Thanks!

Post has attachment
Update: It's more complicated than I realised. The compiler will only skip evaluation of arguments if it is sure that there can be no side effects. For instance, for a direct memory read. If the argument invokes a function then it will be evaluated.

So I have created a storm in error. This post is a lot of FUD. Sorry.

---------

Inline functions are not guaranteed to evaluate their arguments exactly once.

This means that the compiler's choice on whether or not to inline a function can change the observable behaviour.

X := IfThen(b, funcWithSideEffect1, funcWithSideEffect2)

Only one of the side effects will be performed if the function is inlined. If the compiler doesn't inline then both are performed.

I consider this a defect of design. Has inlining in Delphi always been this way?

Following on from the recent discussion (https://plus.google.com/118000060693226156516/posts/cb8ZMNrvHuf) on C# performance (compared to Delphi), I updated my floating point LU decomposition test code to compare Delphi code with C#. XE7 on the Delphi side, VS2015 on the C# side. Running on an laptop with an i7-7500U processor because that's what is at hand right now. Targeting x64 with default release config settings on both environments.

Delphi code

program LUperf;

{$APPTYPE CONSOLE}
{$POINTERMATH ON}

uses
System.Diagnostics,
System.Win.Crtl;

function LinearIndex(i, j, d: Integer): Integer;
begin
Result := i * (d + 1) + j;
end;

procedure PascalLUDecomposeBandedReal(A, Al: PDouble; Index: PInteger; N, Bandwidth: Integer); cdecl;
var
d: Double;
i, j, k, l, iidx, kidx, Alidx: Integer;
mm: Integer;
tmp1, tmp2: Double;
begin
mm := 1+2*Bandwidth;
l := Bandwidth;
for i := 1 to Bandwidth do begin
iidx := LinearIndex(i, 0, mm);
for j := Bandwidth+2-i to mm do begin
A[iidx + j - l] := A[iidx + j];
end;
dec(l);
for j := mm-l to mm do begin
A[iidx + j] := 0.0;
end;
end;
d := 1.0;
l := Bandwidth;
for k := 1 to N do begin
kidx := LinearIndex(k, 0, mm);
tmp1 := A[kidx + 1];
i := k;
if l<N then begin
inc(l);
end;
for j := k+1 to l do begin
if abs(A[LinearIndex(j, 1, mm)])>abs(tmp1) then begin
tmp1 := A[LinearIndex(j, 1, mm)];
i := j;
end;
end;
Index[k] := i;
if tmp1=0.0 then begin
A[kidx + 1] := 1.0e-20;
end;
if i<>k then begin
iidx := LinearIndex(i, 0, mm);
d := -d;
for j := 1 to mm do begin
tmp1 := A[kidx + j];
A[kidx + j] := A[iidx + j];
A[iidx + j] := tmp1
end;
end;
tmp2 := 1.0/A[kidx + 1];
Alidx := k*(Bandwidth-1);
for i := k+1 to l do begin
iidx := LinearIndex(i, 0, mm);
tmp1 := A[iidx + 1]*tmp2;
Al[Alidx + i] := tmp1;
for j := 2 to mm do begin
A[iidx + j - 1] := A[iidx + j]-tmp1*A[kidx + j];
end;
A[iidx + mm] := 0.0;
end;
end;
end;

type
TLUDecomposeBandedRealProc = procedure(A, Al: PDouble; Index: PInteger; N, Bandwidth: Integer); cdecl;

procedure Test(Proc: TLUDecomposeBandedRealProc);
const
N = 5000;
Bandwidth = 8;
IterationCount = 5000;
var
i, j, mm: Integer;
Input, A, Al: TArray<Double>;
Index: TArray<Integer>;
Stopwatch: TStopwatch;
begin
mm := 1+2*Bandwidth;
SetLength(Input, (1+N)*(1+mm));
for i := 1 to N do begin
for j := 1 to mm do begin
Input[LinearIndex(i, j, mm)] := 0.5 + i - j;
end;
end;
SetLength(Al, (1+N)*(1+BandWidth));
SetLength(Index, 1+N);

SetLength(A, Length(Input));

Stopwatch := TStopwatch.StartNew;
for i := 1 to IterationCount do begin
Move(Pointer(Input)^, Pointer(A)^, Length(A)*SizeOf(A[0]));
Proc(PDouble(A), PDouble(Al), PInteger(Index), N, Bandwidth);
end;
Writeln(Stopwatch.ElapsedMilliseconds);
end;

var
i: Integer;

begin
for i := 1 to 5 do begin
Test(PascalLUDecomposeBandedReal);
end;
Readln;
end.

C# code

using System;
using System.Diagnostics;
using System.Text;

namespace ConsoleApplication1
{
class Program
{
static int LinearIndex(int i, int j, int d)
{
return i * (d + 1) + j;
}

static void PascalLUDecomposeBandedReal(double[] A, double[] Al, int[] Index, int N, int Bandwidth)
{
var mm = 1 + 2 * Bandwidth;
var l = Bandwidth;
for (int i = 1; i <= Bandwidth; i++)
{
var iidx = LinearIndex(i, 0, mm);
for (int j = Bandwidth + 2 - i; j <= mm; j++)
{
A[iidx + j - l] = A[iidx + j];
};
l--;
for (int j = mm - l; j <= mm; j++)
{
A[iidx + j] = 0.0;
}
}
var d = 1.0;
l = Bandwidth;
for (int k = 1; k < +N; k++)
{
var kidx = LinearIndex(k, 0, mm);
var tmp1 = A[kidx + 1];
var i = k;
if (l < N) {
l++;
}
for (int j = k + 1; j <= l; j++)
{
if (Math.Abs(A[LinearIndex(j, 1, mm)]) > Math.Abs(tmp1))
{
tmp1 = A[LinearIndex(j, 1, mm)];
i = j;
}
}
Index[k] = i;
if (tmp1 == 0.0)
{
A[kidx + 1] = 1.0e-20;
}
if (i != k)
{
var iidx = LinearIndex(i, 0, mm);
d = -d;
for (int j = 1; j <= mm; j++)
{
tmp1 = A[kidx + j];
A[kidx + j] = A[iidx + j];
A[iidx + j] = tmp1;
}
}
var tmp2 = 1.0 / A[kidx + 1];
var Alidx = k * (Bandwidth - 1);
for (i = k + 1; i <= l; i++)
{
var iidx = LinearIndex(i, 0, mm);
tmp1 = A[iidx + 1] * tmp2;
Al[Alidx + i] = tmp1;
for (int j = 2; j <= mm; j++)
{
A[iidx + j - 1] = A[iidx + j] - tmp1 * A[kidx + j];
}
A[iidx + mm] = 0.0;
}
}
}

static void Test()
{
const int N = 5000;
const int Bandwidth = 8;
const int IterationCount = 5000;

var mm = 1 + 2 * Bandwidth;
var Input = new double[(1 + N) * (1 + mm)];
for (int i = 1; i <= N; i++)
{
for (int j = 1; j <= mm; j++)
{
Input[LinearIndex(i, j, mm)] = 0.5 + i - j;
}
}
var Al = new double[(1 + N) * (1 + Bandwidth)];
var Index = new int[1 + N];
var A = new double[Input.Length];

var sw = new Stopwatch();
sw.Start();
for (int i = 1; i <= IterationCount; i++)
{
Array.Copy(Input, A, A.Length);
PascalLUDecomposeBandedReal(A, Al, Index, N, Bandwidth);
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
}

static void Main(string[] args)
{
for (int i = 1; i <= 5; i++)
{
Test();
}
Console.ReadLine();
}
}
}

Delphi output:

10861
11139
12534
10689
10676

C# output:

13576
11602
8964
11109
10375

It was interesting to see how variable the output was.

Still, we can see that the Delphi and C# compilers perform similarly to each other. Back at my original post (https://plus.google.com/103246155735524926641/posts/jnSYfsLzc94) we see that the MS C++ compiler produces code 2.5 times faster. The Delphi compiler could achieve that, if it was well written.

I know that this is just one benchmark, and I suspect that it has some flaws. I still regard it as disappointing that the Delphi compiler fails to produce code anywhere close to the attainable efficiency.

Post has attachment
Fun program for the day:

{$APPTYPE CONSOLE}

uses
System.SysUtils,
System.IOUtils;

procedure Main;
var
Ch: Char;
begin
for Ch := low(Ch) to high(Ch) do
if TPath.HasValidPathChars(Ch, False)<>TPath.HasValidPathChars(Ch, True) then
Writeln('different at #' + IntToStr(ord(Ch)));
Writeln('finished');
end;

begin
Main;
Readln;
end.

See https://stackoverflow.com/questions/45346525/why-tpath-hasvalidpathchars-accepts-as-valid-char-in-a-path

This irks me. Why can't I write my equations in natural form?

program WhyDoEmbarcaderoNotCareAboutPerformance;

{$APPTYPE CONSOLE}

uses
System.Diagnostics;

const
RepCount = 25000;

var
Data: array [0..8191] of Double;

procedure Main;
var
i, j, rep: Integer;
Stopwatch: TStopwatch;
begin
Writeln('/2.0');
for j := 1 to 5 do begin
for i := low(Data) to high(Data) do begin
Data[i] := 666*i;
end;

Stopwatch := TStopwatch.StartNew;
for rep := 0 to RepCount-1 do begin
for i := low(Data) to high(Data) do begin
Data[i] := Data[i]/2.0;
end;
end;
Writeln(Stopwatch.ElapsedTicks);
end;

Writeln('*0.5');
for j := 1 to 5 do begin
for i := low(Data) to high(Data) do begin
Data[i] := 666*i;
end;

Stopwatch := TStopwatch.StartNew;
for rep := 0 to RepCount-1 do begin
for i := low(Data) to high(Data) do begin
Data[i] := 0.5*Data[i];
end;
end;
Writeln(Stopwatch.ElapsedTicks);
end;
end;

begin
Main;
Readln;
end.

class function TCloudSHA256Authentication.GetStreamToHashSHA256Hex(const Content: TStream): string;
var
LBytes : TBytes;
Hash: THashSHA2;
begin
LBytes := TBytesStream(Content).Bytes;
//Hash bytes
Hash := THashSHA2.Create;
Hash.Update(LBytes);
Result := Hash.HashAsString;
end;

I can't quite believe the cast on the first line...... How could this ever come to pass? Are there really no standards left at Embarcadero? Who writes this code? Who reviews it? Do they know what quality means? 

And all that before we get onto the concept of requiring the entire stream to be in contiguous memory before contemplating hashing it. A WTF in itself, but paling in comparison to that travesty of a cast. 

http://stackoverflow.com/questions/43476183/delphi-thashsha2-return-a-wrong-sha256-on-huge-file

Based on a recent post here, it looks like the new Linux compiler uses the x87 unit for floating point. Naturally this won't perform well. If it is true.

I don't have the compiler. Can anyone confirm or refute this? 

Post has attachment
Stack Overflow developer survey results have been published, in case anyone here is interested and has not seen them.

http://stackoverflow.com/insights/survey/2017

I want to annotate some of my enumerated types with human facing names. So I want to write:

type
[Names('Explicit time domain', 'Implicit time domain', 'Frequency domain')]
TDynamicsSolutionMethod = (dsmExplicit, dsmImplicit, dsmFrequencyDomain);

I'm disappointed that I end up with an attribute that looks like this:

type
NamesAttribute = class(TCustomAttribute)
private
FValues: TArray<string>;
public
property Values: TArray<string> read FValues;
published
constructor Create(
const Value0: string='';
const Value1: string='';
const Value2: string='';
const Value3: string='';
const Value4: string='';
const Value5: string='';
const Value6: string='';
const Value7: string='';
const Value8: string='';
const Value9: string='';
const Value10: string='';
const Value11: string='';
const Value12: string='';
const Value13: string='';
const Value14: string=''
);
end;

constructor StringArrayAttribute.Create(
const Value0: string;
const Value1: string;
const Value2: string;
const Value3: string;
const Value4: string;
const Value5: string;
const Value6: string;
const Value7: string;
const Value8: string;
const Value9: string;
const Value10: string;
const Value11: string;
const Value12: string;
const Value13: string;
const Value14: string
);
var
Index, Count: Integer;
TempArray: array [0..14] of string;
begin
TempArray[0] := Value0;
TempArray[1] := Value1;
TempArray[2] := Value2;
TempArray[3] := Value3;
TempArray[4] := Value4;
TempArray[5] := Value5;
TempArray[6] := Value6;
TempArray[7] := Value7;
TempArray[8] := Value8;
TempArray[9] := Value9;
TempArray[10] := Value10;
TempArray[11] := Value11;
TempArray[12] := Value12;
TempArray[13] := Value13;
TempArray[14] := Value14;
Count := 0;
for Index := 0 to high(TempArray) do begin
if TempArray[Index]<>'' then begin
Count := Index+1;
end;
end;
FValues := TArray.Copy(TempArray, 0, Count);
end;

Does anybody know a better way?

Mildly frustrating, just in a handful of units in my code base, but error reporting gets confused about line numbers:

[dcc32 Hint] OrcGraphics.pas(108035): H2164 Variable 'saVertCentred' is declared but never used in 'TViewPort.DrawLabel'

This is in a unit with 6000 lines ......

I'm using XE7. Has anyone else seen this?
Wait while more posts are being loaded