

hukmac
Members-
Content Count
7 -
Joined
-
Last visited
-
Days Won
1
hukmac last won the day on June 13 2020
hukmac had the most liked content!
Community Reputation
5 NeutralTechnical Information
-
Delphi-Version
Delphi 2 - 7
-
Strange problem with mutexes
hukmac replied to hukmac's topic in Algorithms, Data Structures and Class Design
OK, so... I have checked that the above has nothing to do with hyperthreading. I have also revisited my observations. Somehow earlier I assumed that the time of single PerformCalculation is at least twice the time of executing SaveDataToDisk - it was the opposite. Thus, the observed behaviour is expected. SaveDataToDisk is a real bottleneck. Processes are entering Mutex2 critical section as fast as possible and nothing strange is with Mutex2. When I artificially increased the time of PerformCalculation to be 10 times the SaveDataToDisk, it nicely performs SaveDataToDisk during PerformCalculation on other cores - and PerformCalculation in all processes uses almost 100% time of the CPU. I will check if I can speed up the SaveDataToDisk. In the example I'm working with each 3s calculation creates 800MB of data to save. Saving this takes ~7s. I'm working on Samsung EVO 970 Plus with 3300MB/s write speed so there is room for improvement. Thank you Remy for making me thinking. -
Strange problem with mutexes
hukmac replied to hukmac's topic in Algorithms, Data Structures and Class Design
Dear Remy, thank you for your question and proposed code. So here I go with the answer and clarification. > Why are you creating and destroying the mutexes over and over? First reason: the situation presented above is a simplification (I thought it will be more clear to present the problem in this form). In my real code, for important reasons, each process is performing calculations and writing results only specified number of times (so the above Main loop would not be infinite). When the work is done then the process creates its child process assigned to the same core and self-terminates. Thus at the end of the process the mutex handle would be closed by the OS anyway. To make things clear I'm doing it manually. Second reason: The named mutex in the OS will exist till at least one handle to it will exist in any of the processes. So while the Mutex2 queue is not empty the mutex is not destroyed by the OS. It is created just once and opened many times by processes if/as the queue is formed. If given queue is emptied the mutex will be destroyed. But creation/destruction of the mutex by the OS is not costly, especially in comparison with the time cores need to spend in crtical sections (in Mutex2 - many seconds). So in my case the problematic Mutex2 - as its queue is almost always full of waiting processes - is in fact created once and not destroyed till the last process is destroyed. And only local handles to the mutex are created/destroyed as processes are hitting/passing the barrier. The problem is that the Mutex2 queue is not working as expected: none of processes waiting in Mutex2 is entering Mutex2 critical section till the last process which was in this critical section - or its child process - will hit this barrier again. Expected behaviour is: when process A leaves the Mutex2 critical section and starts doing other things, some other process waiting in Mutex2 enters its critical section (even if process A is calculating), etc. Is this mutex improperly created or some Win10 mechanism needs additional configuration to make it working as expected? Second thought: my CPU is i9-9900K in a hyperthreading mode - can the problem be related with the hyperthreading? If process A is assigned to logical core 0 and next process in a Mutex2 queue (name it B) is assigned to the logical core 1 (both logical cores 0 and 1 are simulated by the same SMT unit - physical core 0), then: if process A is leaving Mutex2 critical section and starts calculating faster than process B will get to Mutex2 critical section, then maybe OS will not like to switch the context of the physical core 0 (simulating hyperthreading cores 0 and 1) and will not release process B to critical section - and none of other waiting processes which are lower in the queue. I will switch off hyperthreading and check it. -
Hello 🙂I have the following code in Delphi12, with slow functions PerformCalculations and SaveDataToDisk: function MutexBarrier(resource_mutex_id :String) :THandle; var Mutex :THandle; begin Mutex:=CreateMutex(nil,false,PChar(resource_mutex_id)); WaitForSingleObject(Mutex,INFINITE); result:=Mutex; end; procedure Main; //process main code (N processes are being run): var mutex :THandle; begin repeat mutex:=MutexBarrier('Mutex1_constant_unique_name'); try Prepare; finally ReleaseMutex(mutex); CloseHandle(mutex); end; PerformCalculations; mutex:=MutexBarrier('Mutex2_constant_unique_name'); try SaveDataToDisk; finally ReleaseMutex(mutex); CloseHandle(mutex); end; until False; end; I'm running N processes with this code, each on separate core (set with affinity, the number of cores > N). Processes do not create additional threads. Processes are run by a separate script like "proc.exe -core=x", where x is the expected affinity. Affinity is set correctly. But the effect of usage of mutexes is strange: 1. Initially all N processes quickly create queue on Mutex1, go through it one after another and then in parallel they are performing calculations on separate cores. 2. Then all N processes start to wait on Mutex2, and one of them (lets name it Process A) gets to the critical section of Mutex2. This is ok. 3. Then the process A which passed critical section of Mutex2 is going through Mutex1 and is performing calculations. At the same time all other processes still wait on Mutex1. - this is strange. 4. Only when process A finishes calculations and enters Mutex2 queue then some other process (name it process B) is entering the critical section of Mutex2. - This is very strange to me. 5. Then process B goes through Mutex1 and performs calculations., etc. In the effect, starting from point 3 only one process is performing calculations at a time. This is very strange to me. Why Mutex2 behaves like that. It is Win10. Remark 1: When SaveDataToDisk is fast (or not called at all - empty critical section of Mutex2), the problem is not happening - and all processes execute PerformCalculations in parallel. Remark 2: I have also verified that Prepare, PerformCalculations and SaveDataToDisk are not failing. I'm searching how to make Mutex2 to behave like expected, but no success till now. Maybe someone has some idea?
-
Dear Peter Below. Thank you for pointing out the usage of global cmtx variable in cmtx22/cmtx33 functions. I need to check, but most probably this is the cause of at least part of the observed problem. I was blinded by the spaghetti 🙂 Btw. The way of data representation and the fact that the code is almost fully procedural (no OOP) is here by purpose - to achieve very high speed of computation - and the result is more than good. This includes also usage of global variables in some cases. The price: much higher cost of maintenance and the related pain.
-
Hello 🙂 At the beginning: thanks to all of you for many very informative discussions on this forum. When working in D7 in most cases Ctrl+F1 was the right answer to most problems. But now I'm more and more frequently saved by the info provided by members of Delphi-PRAXiS 🙂 And now my actual pain: I'm using Delphi 11 CE ver 28.0. While working with quite simple code I have encountered two strange, fully reproducible problems. The description and related code are below. Additionally all the details and both problems are presented within a short video: https://drive.google.com/file/d/147uSLerfnu-3DZME1jHQ8nsnlAdqtNRi (the code is compiled without optimization). Maybe someone wise can shed some light on the sources of those problems and how to get rid of them (excluding "Embarcadero" problem) ? All the best! --------- >>> The example task: The square matrix TConfusionMatrix = array[0..x,0..x] of Extended is created in two ways (please see the code below): - with primitive functions such as Cmtx33() or Cmtx22() which place the right values in the right cells of the matrix, or - by splitting string representation of the matrix (with function MtxStr2ConfusionMatrix). Example string representation of the 3x3 matrix is: '[0,1,2, 3,4,5, 6,7,8]' (rows concatenation, spaces and brackets do not matter). Created matrices should represent structure "array of rows" and oboth methods should fill the matrix in the same way. For example, if cmtx and cmtx2 are of TConfusionMatrix type then after calls: MtxStr2ConfusionMatrix('[43,0,1,0,45,0,2,0,44]', cmtx, class_cnt); cmtx2:=Cmtx33(43,0,1,0,45,0,2,0,44); variables cmtx and cmtx2 should include the same values in the same elements of matrices (among those which were modified by both functions). Additionally class_cnt will include the size of the row/column of the matrix (in this example class_cnt will be equal 3). It is assumed that strings representing the matrix will always have correct structure and that will include the right number of elements to create a square matrix (so we do not need to think about such things here). >>> Encountered problems: 1.) The first problem ("the inspection problem"): Depending on the order of calls to MtxStr2ConfusionMatrix() and Cmtx33() the results of the former function differ. Sometimes after the call of MtxStr2ConfusionMatrix() the created matrix is displayed in inspection window as it would be (improperly) transposed. But after next call of Cmtx33() the matrix set by MtxStr2ConfusionMatrix() starts to look properly. Further comparison of matrices created with both functions indicate that they are equal and related code execution reacts accordingly. 2.) The second problem ("the wtf problem"): But sometimes - for exactly the same data - the matrix being the result of MtxStr2ConfusionMatrix() permanently stays improperly transposed and the matrices created with both methods are recognised as different. Then this breaks the logic of the code. I'm blind or something but I can't see any obvious reason for such behaviour. Results from primitive functions Cmtx33(), Cmtx22() are always correct and presented properly. ---------- >>> The code: const MAX_CLASSES = 50; type TConfusionMatrix = array[0..MAX_CLASSES,0..MAX_CLASSES] of Extended; //transforms square matrix in the form of string to a table of rows; //cmtx_str = '[0,1,2,3,4,5,6,7,8]' => [[0,1,2][3,4,5][6,7,8]] //(row and column of index=0 are left for other purposes) function MtxStr2ConfusionMatrix(cmtx_str :String; var cmtx :TConfusionMatrix; var class_cnt :Word) :Boolean; var i,j :Word; splittedString : TStringDynArray; res :Boolean; begin res:=True; try RemoveChar(cmtx_str,' '); RemoveChar(cmtx_str,'['); RemoveChar(cmtx_str,']'); class_cnt:=0; splittedString:=SplitString(cmtx_str, ','); if Length(splittedString)<4 then res:=False else begin if Frac(Sqrt(Length(splittedString)))<0.01 then class_cnt:=Round(Sqrt(Length(splittedString))) else res:=False; end; if class_cnt>=2 then begin EraseConfusionMatrix(cmtx,class_cnt,MAX_CLASSES,0); for i:=1 to class_cnt do for j:=1 to class_cnt do cmtx[i,j]:= StrToFloat(splittedString[i+(j-1)*class_cnt - 1]); end; except res:=False; end; result:=res; end; function CompareConfusionMatrices(c1,c2 :TConfusionMatrix; class_cnt :Word) :Boolean; var i,j :Integer; ok :Boolean; begin ok:=True; for i:=1 to class_cnt do for j:=1 to class_cnt do if c1[i,j]<>c2[i,j] then begin ok:=False; break; end; result:=ok; end; function Cmtx33(c11,c12,c13,c21,c22,c23,c31,c32,c33 :LongInt) :TConfusionMatrix; begin cmtx[1,1]:=c11; cmtx[1,2]:=c12; cmtx[1,3]:=c13; cmtx[2,1]:=c21; cmtx[2,2]:=c22; cmtx[2,3]:=c23; cmtx[3,1]:=c31; cmtx[3,2]:=c32; cmtx[3,3]:=c33; result:=cmtx; end; function Cmtx22(c11,c12,c21,c22 :LongInt) :TConfusionMatrix; begin cmtx[1,1]:=c11; cmtx[1,2]:=c12; cmtx[2,1]:=c21; cmtx[2,2]:=c22; result:=cmtx; end; procedure RemoveChar(var s :String; ch :Char); var d :String; i,ls :LongInt; begin d:=''; ls:=Length(s); i:=1; for i:=1 to ls do if s[i]<>ch then d:=d+s[i]; s:=d; end; procedure EraseConfusionMatrix(var cmtx :TConfusionMatrix; num_classes, max_classes :Word; value :Integer); var i,j :Word; begin if num_classes<=max_classes then begin for i:=0 to num_classes do for j:=0 to num_classes do cmtx[i,j]:=value; end else ShowMessage('Confusion Matrix can not be greater than '+IntToStr(max_classes)+'x'+IntToStr(max_classes)); end; ===EOT===
-
Dear Anders Melander. Thank you for your time developing map2pdb project. In my case it works great with Delphi 11 Community Edition and VTune 2024.0.1. It is a very useful tool 🙂
-
Dear Uwe Raabe, I just found your great software MMX - and downloaded version 13.03 (I'm still working with Delphi7 and WinXP). The software looks great. Thank you for your work and long support for Delphi 7. I think I will discover the usefulness of MMX with pleasure :) Best regards, Maciej