• Hallo liebe Userinnen und User,

    nach bereits längeren Planungen und Vorbereitungen sind wir nun von vBulletin auf Xenforo umgestiegen. Die Umstellung musste leider aufgrund der Serverprobleme der letzten Tage notgedrungen vorverlegt werden. Das neue Forum ist soweit voll funktionsfähig, allerdings sind noch nicht alle der gewohnten Funktionen vorhanden. Nach Möglichkeit werden wir sie in den nächsten Wochen nachrüsten. Dafür sollte es nun einige der Probleme lösen, die wir in den letzten Tagen, Wochen und Monaten hatten. Auch der Server ist nun potenter als bei unserem alten Hoster, wodurch wir nun langfristig den Tank mit Bytes vollgetankt haben.

    Anfangs mag die neue Boardsoftware etwas ungewohnt sein, aber man findet sich recht schnell ein. Wir wissen, dass ihr alle Gewohnheitstiere seid, aber gebt dem neuen Board eine Chance.
    Sollte etwas der neuen oder auch gewohnten Funktionen unklar sein, könnt ihr den "Wo issn da der Button zu"-Thread im Feedback nutzen. Bugs meldet ihr bitte im Bugtracker, es wird sicher welche geben die uns noch nicht aufgefallen sind. Ich werde das dann versuchen, halbwegs im Startbeitrag übersichtlich zu halten, was an Arbeit noch aussteht.

    Neu ist, dass die Boardsoftware deutlich besser für Mobiltelefone und diverse Endgeräte geeignet ist und nun auch im mobilen Style alle Funktionen verfügbar sind. Am Desktop findet ihr oben rechts sowohl den Umschalter zwischen hellem und dunklem Style. Am Handy ist der Hell-/Dunkelschalter am Ende der Seite. Damit sollte zukünftig jeder sein Board so konfigurieren können, wie es ihm am liebsten ist.


    Die restlichen Funktionen sollten eigentlich soweit wie gewohnt funktionieren. Einfach mal ein wenig damit spielen oder bei Unklarheiten im Thread nachfragen. Viel Spaß im ngb 2.0.

C# Programm wird immer langsamer

KcDaRookie

Temporär Suspendiert :D

Registriert
14 Juli 2013
Beiträge
401
Tachchen Zusammen,

Ich hab einen txt2pdf Converter in C# der txt-Dateien aus dem input-Ordner als PDF im output-Ordner speichert.
Dazu benutze ich die PdfSharp Library, und das klappt auch ganz gut, nur wird der Converter immer langsamer.
Bei weniger als 1000 Dateien merkt man das nicht so, ich müsste aber bisschen über 100.000 txt-Dateien umwandeln.

Nur weiß ich nicht wo genau das Problem liegt. Ich glaube, der Arbeitsspeicher wird immer weiter zugemüllt und hab da den StreamReader im Verdacht, aber den close und dispose ich nach jeder Datei.
Habt ihr noch eine Idee, was man optimieren könnte?

[src=csharp]using System;
using System.IO;
using System.Reflection;
using PdfSharp.Drawing;
using PdfSharp.Pdf;

using System.Windows.Forms;

namespace WindowsFormsApplication4
{
public partial class Form1 : Form
{
public static int filecount;
public Form1()
{
//eingebettete pdfsharp.dll wird hier eingelesen
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
string resourceName = new AssemblyName(args.Name).Name + ".dll";
string resource = Array.Find(this.GetType().Assembly.GetManifestResourceNames(), element => element.EndsWith(resourceName));

using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resource))
{
Byte[] assemblyData = new Byte[stream.Length];
stream.Read(assemblyData, 0, assemblyData.Length);
return Assembly.Load(assemblyData);
}
};
InitializeComponent();
//überprüfen ob der input ordner existiert und dateien enthält
invoiceamount();
}

private void invoiceamount()
{
if (!Directory.Exists("input"))
{
label1.Text = "No input folder found.";
button1.Enabled = false;
button1.Refresh();
}
else
{
filecount = Directory.GetFiles("input").Length;
if (filecount < 1)
{
label1.Text = "No files in input folder.";
button1.Enabled = false;
button1.Refresh();
}
else
{
label1.Text = "File count: " + filecount;
}
}
}

private void button1_Click(object sender, EventArgs e)
{
startconvert();
}

private void startconvert()
{
if (!Directory.Exists("output"))
{
Directory.CreateDirectory("output");
}
int processed = 0;
string[] fileEntries = Directory.GetFiles("input");
foreach (string fileName in fileEntries)
{
//files an converter weiterreichen
convert(fileName);
processed++;
if (processed % 10 == 0)
{
label2.Text = "Processed: " + processed.ToString();
label2.Refresh();
}
}
}

private void convert(string input)
{
{
//hier wird mit dem SR die Textdatei durchgegangen und bei einem form feed eine neue PDF Seite gestartet
//Ich hab nur den Font und das Seitenlayout etwas angepasst, der Originalcode kommt von hier http://forum.pdfsharp.de/viewtopic.php?f=8&t=3072

const double LINE_SPACING = 6.8;
const double FONT_SIZE = 7.2;
const int MAX_LINES = 112;
StreamReader sr = new StreamReader(input, System.Text.Encoding.Default);
PdfDocument document = new PdfDocument();
XFont font = new XFont("Courier New", FONT_SIZE, XFontStyle.Regular, null);
string textAfterFormFeed = "";
while (sr.Peek() >= 0)
{
PdfPage page = document.AddPage();
XGraphics gfx = XGraphics.FromPdfPage(page);
bool formFeed = false;
string buffer = "";
double pos = 30;
int lines = 0;
if (textAfterFormFeed != "")
{
gfx.DrawString(textAfterFormFeed, font, XBrushes.Black, new XRect(12, pos, page.Width, page.Height), XStringFormats.TopLeft);
pos = pos + LINE_SPACING;
lines++;
textAfterFormFeed = "";
}
while (sr.Peek() >= 0 && !formFeed)
{
buffer = sr.ReadLine();
if (buffer.IndexOf("\x0c") >= 0)
{
formFeed = true;
if (buffer.IndexOf("\x0c") + 1 != buffer.Length)
textAfterFormFeed = buffer.Substring(buffer.IndexOf("\x0c") + 1, buffer.Length - buffer.IndexOf("\x0c") - 1);
if (buffer.IndexOf("\x0c") > 0)
gfx.DrawString(buffer.Substring(0, buffer.IndexOf("\x0c")), font, XBrushes.Black, new XRect(12, pos, page.Width, page.Height), XStringFormats.TopLeft);
}
else
gfx.DrawString(buffer, font, XBrushes.Black, new XRect(12, pos, page.Width, page.Height), XStringFormats.TopLeft);
pos = pos + LINE_SPACING;
lines++;
if (lines > MAX_LINES)
formFeed = true;
}
}
sr.Close();
sr.Dispose();
string output = input.Replace("input\\", "");
output = output.Replace(".txt", "");
document.Save("output\\" + output + ".pdf");
document.Close();
}
}
}


}
[/src]
 

Timon3

Team ModMii

Registriert
17 Juli 2013
Beiträge
499
Mir fällt beim Drübergucken nichts besonderes auf, außer, dass du jedes Mal die Font neu reinlädst - die kannst du doch auch nur ein mal laden und immer wieder verwenden.

Ansonsten solltest du mal mit einem Memory Profiler das Programm Debuggen. Der sollte dir anzeigen, wo genau der Speicher verbraucht wird. Bist du dir denn sicher, dass ein Memory Leak vorliegt? Das kannst du ja einfach mal über einen Debugger (oder den Task-Manager) verfolgen.
 

KcDaRookie

Temporär Suspendiert :D

Registriert
14 Juli 2013
Beiträge
401
  • Thread Starter Thread Starter
  • #3
Hmm seltsam, gestern ging die Process Memory Kurve bei Visual Studio hoch bis 128mb und hat dann nach der letzten Datei alles freigegeben.
Ich wollte einen Screenshot der Kurve machen, bevor ich den Font auf statisch umstelle... und jetzt bleibt der bei 30mb und läuft mit konstanter Geschwindigkeit durch.
Dauert jetzt auch nur noch die Hälfte der Zeit.
Keine Ahnung woran das lag :unknown:
Danke aber für den Tipp den Font nicht ständig neu anzusprechen :D
 

Timon3

Team ModMii

Registriert
17 Juli 2013
Beiträge
499
Die logische Lösung ist jetzt, immer ein Screenshot-Tool mitlaufen zu lassen, damit sich der Memory Leak nicht traut aufzutreten ;)
 

Ta Lun

Neu angemeldet

Registriert
15 Mai 2018
Beiträge
12
Wenn das wieder auftreten sollte, kannst DU mal versuchen den Streamreader in ein using zu kapseln.
Wenn Du grundsätzlich ein wenig mehr performance rausholen willst, würde ich mit einem Backroundworker arbeiten und das verarbeiten auf mehrere Threads verteilen.
Auch würde ich immer try/Catch verwenden.
 

KcDaRookie

Temporär Suspendiert :D

Registriert
14 Juli 2013
Beiträge
401
  • Thread Starter Thread Starter
  • #6
@Ta Lun:Eigentlich kann ich Beiträge in denen "DU" groß geschrieben wird echt nicht leiden, das ist eine unschöne Abart die DU dir unbedingt abgewöhnen solltest. Das klingt so möchtegern-Elitär :confused:

Das mit dem Using bzw. Try-Catch(-Finally) bringt mir nur weitere Vorteile bei der Fehlerbehandlung, nicht bei Performance. Natürlich sollte man das nutzen wo möglich, aber da ich hier quasi keine fehlerhaften Eingaben tätigen kann, hab ich das weggelassen.

Ja, Backgroundworker hatte ich mir angeguckt, sahen mir aber irgendwie zu aufwändig aus.
Hab das aber jetzt dann doch mal in Angriff genommen und 6 BW erstellt, einer Verteilt die Jobs an die anderen 5 und die konvertieren.
Hatte dann noch kurzzeit ein Problem, dass die BW das Processed-Label nicht updaten durften, aber das hab ich auch in den Griff bekommen.
Das Ganze sieht jetzt noch unaufgeräumter aus, aber es klappt:
[src=csharp]using System;
using System.IO;
using System.Reflection;
using PdfSharp.Drawing;
using PdfSharp.Pdf;

using System.Windows.Forms;

namespace WindowsFormsApplication4
{
public partial class Form1 : Form
{
public static int filecount;
const double LINE_SPACING = 6.8;
const double FONT_SIZE = 7.2;
public static XFont font;
public static int processed = 0;
public Form1()
{
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
string resourceName = new AssemblyName(args.Name).Name + ".dll";
string resource = Array.Find(this.GetType().Assembly.GetManifestResourceNames(), element => element.EndsWith(resourceName));

using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resource))
{
Byte[] assemblyData = new Byte[stream.Length];
stream.Read(assemblyData, 0, assemblyData.Length);
return Assembly.Load(assemblyData);
}
};
InitializeComponent();
invoiceamount();
}

private void invoiceamount()
{
if (!Directory.Exists("input"))
{
label1.Text = "No input folder found.";
button1.Enabled = false;
button1.Refresh();
}
else
{
filecount = Directory.GetFiles("input").Length;
if (filecount < 1)
{
label1.Text = "No files in input folder.";
button1.Enabled = false;
button1.Refresh();
}
else
{
label1.Text = "File count: " + filecount;
font = new XFont("Courier New", FONT_SIZE, XFontStyle.Regular, null);
}
}
}

private void button1_Click(object sender, EventArgs e)
{
backgroundWorker2.RunWorkerAsync();
}

private void convert(string input)
{
{
StreamReader sr = new StreamReader(input, System.Text.Encoding.Default);
PdfDocument document = new PdfDocument();
string textAfterFormFeed = "";
while (sr.Peek() >= 0 || textAfterFormFeed != "")
{
PdfPage page = document.AddPage();
XGraphics gfx = XGraphics.FromPdfPage(page);
bool formFeed = false;
string buffer = "";
double pos = 30;
int lines = 0;
if (textAfterFormFeed != "")
{
gfx.DrawString(textAfterFormFeed, font, XBrushes.Black, new XRect(12, pos, page.Width, page.Height), XStringFormats.TopLeft);
pos = pos + LINE_SPACING;
lines++;
textAfterFormFeed = "";
}
while (sr.Peek() >= 0 && !formFeed)
{
buffer = sr.ReadLine();
if (buffer != "")
{
if (buffer[0] =='\x0c')
{
formFeed = true;
textAfterFormFeed = buffer.Substring(1);
}
else
gfx.DrawString(buffer, font, XBrushes.Black, new XRect(12, pos, page.Width, page.Height), XStringFormats.TopLeft);
pos = pos + LINE_SPACING;
lines++;
}
}
}
sr.Close();
sr.Dispose();
string output = input.Replace("input\\", "");
output = output.Replace(".txt", "");
document.Save("output\\" + output + ".pdf");
document.Close();
}
}

private void backgroundWorker2_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
processed = 0;
if (!Directory.Exists("output"))
{
Directory.CreateDirectory("output");
}
foreach (string fileName in Directory.GetFiles("input"))
{
while (true)
{
if (!backgroundWorker1.IsBusy)
{
backgroundWorker1.RunWorkerAsync(fileName);
break;
}
if (!backgroundWorker3.IsBusy)
{
backgroundWorker3.RunWorkerAsync(fileName);
break;
}
if (!backgroundWorker4.IsBusy)
{
backgroundWorker4.RunWorkerAsync(fileName);
break;
}
if (!backgroundWorker5.IsBusy)
{
backgroundWorker5.RunWorkerAsync(fileName);
break;
}
if (!backgroundWorker6.IsBusy)
{
backgroundWorker6.RunWorkerAsync(fileName);
break;
}
}
}
}

private void backgroundWorker2_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{

}

delegate void SetTextCallback();

private void SetText()
{
if (this.label2.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { });
}
else
{
processed++;
if (processed%1000 == 0 || processed == filecount)
this.label2.Text = "Processed: " + processed.ToString();
}
}

private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
convert((string)e.Argument);
}

private void backgroundWorker1_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
SetText();
}

private void backgroundWorker3_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
convert((string)e.Argument);
}

private void backgroundWorker3_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
SetText();
}

private void backgroundWorker4_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
convert((string)e.Argument);
}

private void backgroundWorker4_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
SetText();
}

private void backgroundWorker5_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
convert((string)e.Argument);
}

private void backgroundWorker5_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
SetText();
}

private void backgroundWorker6_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
convert((string)e.Argument);
}

private void backgroundWorker6_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
SetText();
}
}
}
[/src]

Kleiner Vorher-Nachher vergleich mit 3005 Dateien:
Vorher:
vorher.png
Nachher:
Unbenannt.png

Sind ~2,5 Sekunden bei 3000 Dateien.
Wenn sich das linear verhält, wären dass alle 70.000-75.000 Dateien 1 Minute die ich spare :coffee:
Ich glaube das reicht nicht, um die Zeit wieder reinzuholen, die ich gebraucht habe um die BW zu raffen :confused: :D

---------------------------------

Hmmm... die Debug-Version ist super performant, wenn ich das Ganze als Release-Build raushaue, wird es extrem langsam :D
Ich bin verwirrt :D
 
Zuletzt bearbeitet:

Roin

Freier Denker

Registriert
22 Juli 2013
Beiträge
581
Einfach mal in den Raum geworfen:
In C++ gilt die Regel "Bei jedem new muss auch ein delete vorkommen". Macht das C# von alleine? Ist der da so schlau?
 

KaPiTN

♪♪♫ wild at heart ♪♫♫♪

Registriert
14 Juli 2013
Beiträge
29.138
Du kannst natürlich Objekte mit Dispose zerstören oder , wie Ta Lun anmerkte, in einem Using-Block verwenden, wo nach dem verlassen des Blocks das Objekt verworfen wird.
Ansonsten erledigt die Garbage Collection die Entsorgung und Speicherfreigabe.


Grundsätzlich haben die Anwendungen auch immer zuviele Verweise. Ich kann nicht sagen , welchen Einfluß dies auf die Performance hat, aber allein optisch macht das im Code und im Projektordner etwas her, wenn Unnötiges entfernt wird.
 

Ta Lun

Neu angemeldet

Registriert
15 Mai 2018
Beiträge
12
@KcDaRookie: Da bist Du ein wenig zu empfindlich gewesen, da es einfach in der schnelle passiert ist und bei einem längeren Wort wäre es aufgefallen. Ich kann das aber gut verstehen, werde ja auch nicht gerne angebrüllt.
Hast aber mit dem BW was gelernt, was doch gut ist.
Das mit dem Release und Debug macht eigentlich keinen Sinn außer DU (grins) startest das im Debug Modus und meinst nicht nur den Complie.
Was meine ich damit. Wenn Du im Debug Modus das alle laufen läßt, läufst Du in einem anderen User Kontext (dem vom VS). Wenn Du aber das Program mit doppelklick öffnest, läufst Du im User Kontext von Dir.
Das muss nicht der gleiche sein. Ich bin mir nicht sicher, wie Deine Formulierung zu interpretieren ist, daher kurz aufklären, was bei Dir die Debug Version ist....
 

KaPiTN

♪♪♫ wild at heart ♪♫♫♪

Registriert
14 Juli 2013
Beiträge
29.138
Das mit dem BW bringt aber für die Performance nichts. Wenn ich mich recht erinnere, verwendet der, ebenso wie die Klasse Threading, nur softwareseitiges Multithreading. Die Threads arbeiten also nicht wirklich parallel, sondern wechseln sich in der Verarbeitung ab. Wenn man dann den Aufwand dazu nimmt, um die Threads zu verwalten, schadet es eher.
Sinn ergibt dieses Multithreading deshalb, weil andere Teile, wie die Oberfläche, nicht auf das Ende von Berechnungen warten müssen.

Performance kann man erlangen, wenn man auch Multitasking erzeugt, die Threads als wirklich gleichzeitig auf verschiedenen Kernen laufen.

ich habe damit noch nicht gearbeitet, aber da müßte es eine Klasse Task geben.
 

alter_Bekannter

N.A.C.J.A.C.

Registriert
14 Juli 2013
Beiträge
4.823
Ort
Midgard
https://msdn.microsoft.com/de-de/library/system.threading.threadpool(v=vs.110).aspx

Dann managed dein System die Threadzahl sogar selber. Und du hantierst nicht mit so ekligen kopierten Blöcken.

Dazu muss man noch anmerken das ein überschreiten der physischen Threadzahll die Performance deutlich verschlechtern kann weil die Kommunikation mit dem RAM im Vergleich zu CPU internen Operationen extrem hohe Latenzen hat. Weil man da drum herum getrickst hat haben wir jetzt so Lücken wie Spectre und Meltdown. Und deswegen fressen die "patches" je nach Implementierung ud Anwendungsfall so viel Leistung.

Ein Ramzugriff kann dich locker mal 60-200 CPU Taktzyklen an reiner Wartezeit kosten. Jetzt teil deinen CPU Leistung mal durch 60-200, das ist der Worst case durch zuviele Threads ohne irgendwelche anderen Bottlenecks.

Und Gleichzeitig auch warum Hyperthreading im Schnitt so viel bringt obwohl es keinen zusätzlichen Leistung bringt im CPU Hardware Sinne. Weil ein Hyperthread in fast allen Aspekten Speicherzugriffslatenzen spart, nichts anderes.
 
Oben