Search code examples
c#delphi-7filestream

C# writing to binary file taking much longer than Delphi's


I'm converting a Delphi app to C#. One part of the program requires writing many packed records in Delphi, or structs in C#, to a binary file. These files are around 400mb. The Delphi version I have completes writing to the file around 8 seconds, whereas the C# version takes around 800 seconds. What could I do to improve speed in the C# version?

Here's the Delphi version:

CONST
   RecordSize= 128;
   HeaderSize=128;

TESTrec = packed record
     now: TDateTime;
     //other fields here
     end;

TESThdrrec = packed record
    now: TDateTime;
    Firsttime: TDateTime;
    Lasttime: TDateTime;
    //other fields listed here
    end;

Function TRampBuildForm.CreateTest(FilePath:String;StartTime:TDateTime;EndTime:TDateTime):Boolean;
var
    Records:Int64;
    CurrentOffSet:Int64;
    CurrentTime:TDateTime;
    NewRec:TESTrec;
    NewHeader:TESThdrrec;
    ix:Integer;
    percent:Integer;

begin
    Try
      RampStream:=TFileStream.Create(FilePath,fmCreate OR fmShareExclusive );
    except
      ShowMessage('cannot create Ramp '+Rampname+chr(13)+'Check to see if file is open');
      CreateTest:=false;
    end;
    FillChar(NewHeader,HeaderSize,0);
    NewHeader.now:=Now;
    NewHeader.Firsttime:=StartTime;
    NewHeader.LastTime:= EndTime;
    FirstAllowableTime:=StartTime;
    LastAllowableTime:=EndTime;

    Records:= Round((EndTime-StartTime)/ONE_SECOND)+1;
    RampStream.Write(NewHeader,HeaderSize);

    FillChar(NewRec,RecordSize,0);
    label8.Caption:='Expanding ';
    Progressbar1.Position:=0;
    CurrentTime:=StartTime;
    percent:=0;
    refresh;
    Application.ProcessMessages;
    For ix:= 1 to Records do
    begin
        if (ix*100) div Records > percent then
        begin
            percent:= (ix*100) div Records;
            Progressbar1.position:=percent;
            refresh;
            Application.ProcessMessages
        end;
        NewRec.Now:=CurrentTime;
        RampStream.Write(NewRec,RecordSize);
        CurrentTime:=CurrentTime + ONE_SECOND;
    end;
    ProgressBar1.Position:=100;
    refresh;

    CreateTest:=True;
    RampStream.Free;
end;

My C# version:

const int RecordSize = 128;
const int HeaderSize = 128;

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct Testrec
{
    public double now;
    //other fields here
}

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct TESThdrrec
{
    public double now;
    public double Firsttime;
    public double Lasttime;
    //other fields here
}

private Boolean CreateTest(string FilePath, double StartTime, double EndTime)
        {
            long Records;
            long CurrentOffSet;
            double CurrentTime;
            TESTrec NewRec = new TESTrec();
            TESThdrrec NewHeader = new TESThdrrec();
            int ix;
            int percent;

            NewHeader.now = System.DateTime.Now.ToOADate();
            NewHeader.Firsttime = StartTime;
            NewHeader.Lasttime = EndTime;
            FirstAllowableTime = StartTime;
            LastAllowableTime = EndTime;

            if (!File.Exists(FilePath)) //if file doesn't exist
            {
                try
                {
                    using (RampStream = new FileStream(FilePath, FileMode.Create)) 
                    {
                        byte[] buffer = GetBytes2(NewHeader); //puts struct into byte array
                        RampStream.Write(buffer, 0, HeaderSize); //write byte array to file
                    }
                }
                catch (Exception e)
                {
                    System.Windows.MessageBox.Show("Cannot create file" + RampName + Environment.NewLine + "Error Message: " + e);
                }
            }

            Records = (long)(Math.Round((EndTime - StartTime) / ONE_SECOND) + 1);

            RampInfo.Content = "Expanding ";
            ProgressBar1.Value = 0;
            CurrentTime = StartTime;
            percent = 0;

            //TAKING LONG TIME HERE!!
            for (ix = 1; ix <= Records; ix++)
            {
                if (((ix * 100) / Records) > percent)
                {
                    percent = (ix * 100) / (int)Records;
                }
                NewRec.now = CurrentTime;
                using (RampStream = new FileStream(FilePath, FileMode.Open)) 
                {
                    byte[] buffer = GetBytes2(NewRec); //puts struct into byte array
                    RampStream.Write(buffer, 0, RecordSize); //write byte array to file
                }
                //RampStream.Write(NewRec, RecordSize);
                CurrentTime = CurrentTime + ONE_SECOND;
            }

            ProgressBar1.Value = 100;

            RampStream.Close();
            return true;
        }

There is a progress bar being updated in the for loop, which is where the code is getting hung up on. I'm not refreshing it or using the FillChar equivalent that the Delphi version is using, but I don't think that would impact this?


Solution

  • You are opening and closing the stream for every write! Open the file once at the start of the writing loop, and close it at the end of the loop (like you do in the old code).

    (You'll want to make sure you are still using a using statement to close the file.)