Method for managing access to critical sections in multicore processor

TWI938594BActive Publication Date: 2026-09-11NATIONAL CHUNG CHENG UNIV
View PDF 4 Cites 0 Cited by

Patent Information

Application Number
TW113119491
Authority / Receiving Office
TW · TW
Patent Type
Patents
Current Assignee / Owner
Filing Date
2024-05-27
Publication Date
2026-09-11
Estimated Expiration
2044-05-26

AI Technical Summary

Technical Problem

Traditional synchronization locks in multi-core processors lead to performance bottlenecks, resource waste, and unfairness due to threads competing for critical sections, with threads often idle and consuming resources inefficiently.

Method used

A method for managing access to critical sections in multi-core processors by ensuring only one core accesses at a time, using atomic operations to manage synchronization locks and thread scheduling, and implementing a fair locking mechanism based on ticket numbers to ensure orderliness and fairness.

Benefits of technology

Improves efficiency and fairness in managing synchronization locks, reducing performance degradation and resource waste by ensuring only one thread accesses the critical section at a time, and implementing a fair locking mechanism.

✦ Generated by Eureka AI based on patent content.

Smart Images

  • Figure TWG2TB001910154_001
    Figure TWG2TB001910154_001
  • Figure TWG2TB001910154_002
    Figure TWG2TB001910154_002
  • Figure TWG2TB001910154_003
    Figure TWG2TB001910154_003
Patent Text Reader

Abstract

This invention proposes a method for managing access to critical sections in a multi-core processor. The multi-core processor has a plurality of cores. The steps of the multi-core processor managing access to a critical section by the plurality of cores include: at any given time, only one of the plurality of cores is allowed to access the critical section, and the next thread to access the critical section is specified in the core that is allowed to access the critical section.
Need to check novelty before this filing date? Find Prior Art

Description

[Technical Field]

[0001] The present invention relates to a method for managing access critical sections in a multi-core processor, and more particularly to a method for managing multi-threaded access critical sections in a multi-core processor. [Previous Technology]

[0002] According to Wikipedia, a critical section is a segment of code that accesses shared resources (such as shared devices or shared memory), which cannot be accessed by multiple threads simultaneously. Therefore, when multiple threads compete for the same critical section (CS), traditional synchronization locks can lead to performance bottlenecks, especially on multi-core processors. This is because when one thread holds a synchronization lock for a critical section, all other threads that need the lock must wait, resulting in threads being idle while waiting for the lock to be released. Furthermore, during this waiting period, threads consume multi-core processor cycles in ineffective spin-waiting instead of performing useful work. This spin-waiting not only wastes processor resources but can also lead to reduced energy efficiency.

[0003] In the case of Non-uniform Memory Access (NUMA) mechanisms, memory access time depends on the location of data in physical memory, leading to reduced performance. Furthermore, in traditional spinlock mechanisms, some threads may acquire the lock earlier than others, causing some threads to wait for extended periods, resulting in unfairness. Also, with a large number of threads, traditional spinlocks require a large number of spin loops to acquire the lock, leading to a significant performance degradation.

[0004] In summary, the problems of traditional multi-threaded lock usage include performance bottlenecks, waste of resources, reduced efficiency, and insufficient fairness. Therefore, how to solve the problem of fair competition for synchronization locks in access critical sections by too many threads and the solution for thread scheduling, and how to provide a balance between fairness and performance, are urgent problems to be solved. [Summary of the Invention]

[0005] In view of the problems of the prior art, the object of the present invention is to improve the efficiency and fairness of managing synchronization locks in a multi-core processor by managing the number of waiting threads and the state of synchronization locks on each core, and by using atomic operations to ensure the atomicity of lock acquisition and release.

[0006] According to the purpose of the present invention, a method for managing access to critical sections of a multi-core processor is provided. The multi-core processor has a plurality of cores. The steps of managing access to critical sections of a plurality of cores include: at any given time, only one of the plurality of cores is allowed to access the critical section, and the next thread to access the critical section is specified in the core that is allowed to access the critical section.

[0007] Wherein, when the currently operating thread of the core that is allowed to access the critical section wants to access the critical section, but the currently operating thread is not the thread designated as the next thread to access the critical section, the currently operating thread relinquishes its right to access the critical section from the core that is allowed to access the critical section.

[0008] In summary, in a multi-core processor, only one core is allowed to access the critical section at any given time, and when any core is allowed to access the critical section, only one thread is allowed to access the critical section.

Implementation Method

[0010] Embodiments of the present invention will be further explained below with reference to the accompanying drawings. Wherever possible, the same reference numerals in the drawings and description represent the same or similar components. In the drawings, shapes and thicknesses may be exaggerated for simplification and convenience. It is understood that elements not specifically shown in the drawings or described in the description are forms known to those skilled in the art. Those skilled in the art can make various changes and modifications based on the content of this invention.

[0011] The present invention is a method for managing access to critical sections in a multi-core processor. Please refer to Figure 1. The multi-core processor has a plurality of cores. The steps of managing access to critical sections in a multi-core processor include: (S101) at any given time, only one of the plurality of cores is allowed to access the critical section; and (S102) in the core that is allowed to access the critical section, the next thread to access the critical section is specified.

[0012] In some embodiments of the present invention, a multi-core processor executes an algorithm to determine that only one of the plurality of cores is allowed to access the critical section, and designates the next thread to access the critical section in the core that is allowed to access the critical section. The multi-core processor executes an algorithm including a pre-operation, an access operation, and a leave operation. After the pre-operation is completed, an access operation is performed each time a thread requests to access the critical section, and a leave operation is performed each time a thread leaves the critical section.

[0013] Pre-processing: Pre-processing includes setting the data structure, synchronization lock state, and number of waiting threads for multiple cores, and using atomic operations to update the data structure, number of waiting threads, and synchronization lock state for each core. The synchronization lock state of each core is used to indicate the locked state that allows access to the critical section or the unlocked state that waits to access the critical section. The number of waiting threads for each core indicates the number of threads waiting to access the critical section. Each data structure is used to indicate the order in which each thread is allowed to enter the critical section among the number of waiting threads.

[0014] Access operation: When a thread requests access to the critical section, an atomic operation is used to increase the number of waiting threads in the core where the thread is located, and an atomic operation is used to update the synchronization lock state to locked or unlocked. At the same time, only one of the multiple cores has a locked synchronization lock state. When any core is in the locked state, it uses the data structure and the number of waiting threads to designate one of all threads as the thread to access the next critical section.

[0015] Leave Operation: When a thread leaves the critical section, an atomic operation is used to reduce the number of waiting threads on the core where the thread resides, and an atomic operation is used to update the data structure, the number of waiting threads, and the synchronization lock state of the core where the thread resides. This determines whether to maintain the core where the thread resides in a locked state or to set another core among multiple cores to allow access to the critical section. After the preparatory operations are completed, an access operation is performed each time a thread requests access to the critical section, and a leave operation is performed each time a thread leaves the critical section.

[0016] In order to solve the problem of oversubscription caused by multiple threads in a multi-core system, a fairness (bounded waiting) solution is proposed in the first embodiment of the present invention. In the following, the first embodiment is referred to as "RON-ticket". The data structure is a ticket data structure. The ticket data structure defines an authorization variable and a ticket variable for each core. When an access operation is performed, the value of the authorization variable, the value of the ticket variable and the number of waiting threads in the core where the thread is located are updated with atomic operations. Based on the difference between the value of the authorization variable and the value of the ticket variable, and the case where the number of waiting threads is zero or greater than zero, the synchronization lock state of the core where the thread is located is updated to a locked state or an unlocked state.

[0017] In the first embodiment, the initial value of the authorization variable is zero, the initial value of the ticket number variable is one, and the initial value of the number of threads waiting to be executed is zero as set by an atomic operation. In actual implementation of the present invention, the initial values ​​of the authorization variable and the ticket number variable can be other values. In other words, as long as the value of the ticket number variable corresponding to any core can be used to represent the order of the threads waiting to access the critical section, the authorization variable corresponding to any core can be compared with the ticket number variable of the same core to confirm that the synchronization lock state of the core can be set to the locked state or the unlocked state. That is, the authorization variable and the ticket number variable referred to in the present invention. For example, the initial value of the authorization variable is set to one, and the initial value of the ticket number variable is set to two, or the initial value of the authorization variable is set to three, and the initial value of the ticket number variable is set to zero, as long as the relevant operations of the subsequent operation process are modified accordingly.

[0018] In the RON-ticket implementation, the algorithm also sets up a wait array, the number of which is the same as the number of cores, and each element of the wait array is the ticket number variable and the authorization variable for each core. The atomic operation updates the value of the ticket number variable and the authorization variable in the wait array corresponding to the core where the thread is located. For example, assuming a three-core processor, the initial value of the authorization variable is zero and the initial value of the ticket number variable is one. The ticket number data structure of each core in the wait array is as follows: TicketLock WaitArray[3] = { {0, 1}, / / Initial state of TicketLock of core 0 {0, 1}, / / Initial state of TicketLock of core 1 {0, 1} / / Initial state of TicketLock of core 2};

[0019] Furthermore, the pre-processing also includes setting a separate identification code for each core and setting a sequence array (TSP_ID_ARRAY). Each element of the sequence array corresponds to the access order of the identification code of each core. The access order refers to the priority variable value of each core, so that when a thread requests access to a critical section, it obtains the corresponding priority variable value. For example, if the sequence array is {2, 0, 1}, this means that the priority variable values ​​of cores 0, 1, and 2 are defined as 2, 0, and 1, respectively. In other words, the access priority order of cores 0, 1, and 2 is mapped to core 2, core 0, and core 1, respectively.

[0020] Furthermore, the access operation also includes setting a locking function, which includes the following steps: Each time a thread requests access to the critical section, the number of waiting threads is incremented using atomic operations; After the number of waiting threads has been incremented, if it is determined that the number of waiting threads was zero before the increment, the synchronization lock state corresponding to the core where the thread is located is directly set to the locked state; After the number of waiting threads has been incremented, if the number of waiting threads is not zero after the increment, the identification code corresponding to the core where the thread is located is obtained using atomic operations, and the ticket number variable of the waiting array corresponding to the identification code is updated.

[0021] . In the RON-ticket embodiment, after updating the ticket number variable in the waiting array corresponding to the identification code, the locking function further includes setting an access check loop. The access check loop is used to check whether the currently executing thread can enter the critical section, and includes the following steps: comparing whether the difference between the value of the authorized variable and the value of the ticket number variable in the waiting array of the core where the executing thread is located is a first preset value; when the difference between the value of the authorized variable and the value of the ticket number variable is a first preset value, the core where the executing thread is located relinquishes the right to access the critical section, so that the synchronization lock state of the core where the executing thread is located is updated to the unlocked state; when the difference between the value of the authorized variable and the value of the ticket number variable is not a first preset value, comparing whether the difference between the value of the authorized variable and the value of the ticket number variable in the waiting array of the core where the executing thread is located is a second preset value. When the difference between the value of the authorized variable and the value of the ticket number variable is equal to the second preset value, the synchronization lock state of the core where the thread is located is updated to the locked state, allowing the thread whose difference between the value of the ticket number variable and the value of the authorized variable is equal to the second preset value to be designated as the thread for the next access critical section in the core where access critical sections are allowed.

[0022] Thus, the present invention implements a fair locking mechanism based on ticket numbers through a locking function, ensuring fairness and orderliness when multiple threads access shared resources (critical sections). Through continuous polling (traversal) and comparison mechanisms of ticket number variables and authorization variables, each core and each thread will access the critical section in the order of the ticket numbers they have obtained, thereby avoiding the situation where any thread fails to access the critical section.

[0023] In the RON-ticket embodiment, the exit operation also includes an unlock function. The unlock function includes the following processing steps: Decrementing the number of waiting threads using atomic operations; After the number of waiting threads has been decremented, determining whether the number of waiting threads is a third preset value; When the number of waiting threads is a third preset value, it means that there are no waiting threads, and the exit can be made through the unlock function.

[0024] In the RON-ticket embodiment, when the number of waiting threads is a third preset value, indicating that there are no waiting threads, the following steps are performed: A departure check variable is set, which is the identifier of the core currently allowed to access the critical section, to obtain the next core to be checked in the access order; A departure check loop is set, which is used to visit all cores of the multi-core processor to determine the next thread allowed to enter the critical section, including the following steps: Determining whether the difference between the authorization variable and the ticket number variable in the waiting array corresponding to the currently checked core is a fourth preset value; If the difference between the authorization variable and the ticket number variable in the waiting array corresponding to the currently checked core is a fourth preset value, the authorization variable in the waiting array of the currently checked core is incrementally updated using atomic operations, and then the departure check loop ends, so that the synchronization lock state of the currently checked core is set to the locked state; When the currently checked core is in the locked state, the data structure and the number of waiting threads are used to designate one of all threads as the next thread to access the critical section.

[0025] In the RON-ticket embodiment, after the exit check loop ends, the exit check variable is updated. The step of updating the exit check variable includes: updating the exit check variable to the identification code of the next core to be checked according to the currently checked core's identification code, in the visit order. When the exit check variable is the identification code of the last core in the visit order, it is updated to the identification code of the first core in the visit order.

[0026] As described above, the unlock function checks whether there are waiting threads in each core. If there are waiting threads in the currently checked core, the current value of the authorization variable is updated atomically. Each time a thread leaves the critical section, the unlock function checks if there are still waiting threads, and if the difference between the authorization variable and the ticket number variable is a second preset value, the value of the authorization variable is updated atomically to determine whether the next thread is allowed to access the critical section. In other words, the unlock function uses the number of waiting threads, the ticket number variable, and the authorization variable to determine the next core and thread allowed to access the critical section.

[0027] The implementation of RON-ticket can be further implemented using C language code, as shown below: 1 struct TicketLock {grant=0 , ticket=1 ;} 2 atomic_int nWait=0 ; / / per-lock variable 3 TicketLock WaitArray [ NUM_CORE] ; / / per-lock variable 4 TSP_ID = TSP_ID_ARRAY[ getcpu() ] 5 void spin_lock() 6 if( atomic_fetch_add(&nWait, 1 ) == 0 ) return ; 7 l_ticket = atomic_fetch_add(&WaitArray[ TSP_ID] . ticket, 1 ) ; 8 while( 1 ) 9 if( WaitArray[ TSP_ID] . grant-l_ticket ≠1 ) 10 sched_yield() ; 11 if( l_ticket==WaitArray[ TSP_ID] . grant) return ; 12 void spin_unlock( ) 13 if( atomic_fetch_sub(&nWait, 1 ) == 1 ) return ; 14 next = ( TSP_ID+1 )%NUM_core; 15 while( 1 ) 16 if( WaitArray[ next] . grant - WaitArray[ next] .ticket ≤ -2 ) 17 atomic_inc(&WaitArray[ next] . grant, 1 ) ; 18 return; 19 next = ( next+1 )%NUM_core ;

[0028] The variables in the above encoding are explained as follows: (1) NUM_core: This variable represents the total number of cores on the system. This is a system-wide variable. (2) TSP_ID_ARRAY[]: This array stores each core ID to its corresponding "TSP ORDER ID" (i.e., TSP_ID), where TSP_ID is the core access order, which can check whether the threads on each core can access the critical section. This is a per-process variable. Each process can have its own routing path (TSP ORDER) because each process has a different number of cores and can have different TSP ORDERs. (3) TSP_ID: This is the core identifier. Each thread operates in one of the cores, so each thread has its corresponding identifier. Therefore, when a thread accesses TSP_ID, the thread on each different core will obtain the corresponding identifier. This is a per-thread variable. (4) WaitArray[]: This array is used to indicate which core threads are waiting to access the critical section. When a thread wants to access a critical section, its corresponding WaitArray[TSP_ID] is set to 1. Other threads can access the critical section when their corresponding flags are set to 0 in WaitArray[]. This is a per-synchronization lock array.

[0029] In the above code, each core has a wait array, consisting of elements corresponding to each core. These elements consist of two variables: grant (the grant variable) and ticket (the ticket number variable). Each core has its own variable for the number of waiting threads (denoted by nWait) (the above refers to lines 1-3 of the above code). When a thread waits to access a critical section, it uses an atomic operation (line 6 of the above code) to check if any thread is already in the critical section. This atomic operation updates the number of waiting threads in the core where the thread is running. If no thread is in the critical section, the currently waiting thread can access the critical section. To access the critical section, the thread uses an atomic operation to update the ticket number variable (line 7 of the above code). Then, the thread waits in a loop within the locking function (lines 7-10 of the above code) until it is its turn to access the critical section. If the current core's execution thread is not the next thread that should access the critical section, release the current core's synchronization lock and try again later (lines 8-11). In other words, if the difference between the grant variable and the ticket number variable is not equal to 1 (i.e., when the difference between the grant variable and the ticket number variable is the first preset value, which is 1), it means that it is not yet time for the current execution thread to enter the critical section. At this time, the execution thread should yield the critical section to other execution threads (via sched_yield). If the current execution thread's ticket number variable is equal to the corresponding core's grant variable (i.e., when the difference between the grant variable and the ticket number variable is the second preset value), it means that it is now the execution thread's turn to enter the critical section, allowing the execution thread to continue executing the operations within the critical section. When a thread leaves the critical section, the unlock function `void spin_unlock()` is executed, reducing the variable for the number of waiting threads (line 13 in the code above). If the value of the number of waiting threads (nWait) before the reduction is 1 (the third preset value), it means that the current thread is the only one waiting to access the critical section, and the function ends after returning (line 13). If there are waiting threads, it searches for a kernel with waiting threads in a loop (lines 14-19). Once a kernel with waiting threads is found, that is, one that satisfies the condition `WaitArray[next].grant - WaitArray[next].ticket ≤ -2` (the fourth preset value), it increments the grant variable of that kernel by 1, allowing the waiting thread to access the critical section (lines 17-18). If the condition is not met, it updates and continues checking (line 19).

[0030] In the second embodiment of the present invention, the second embodiment is referred to as "RON-Plock" in the following content. The data structure is a spin data structure. The spin data structure includes the number of waiting threads (numWait) and the synchronization lock variable (lock). The number of waiting threads for each core represents the number of threads waiting for each core to acquire the synchronization lock permission. The initial value of the number of waiting threads is zero (numWait=0). The synchronization lock variable for each core is used to indicate whether the synchronization lock state of each core is locked or unlocked. The initial value of the synchronization lock variable indicates that the synchronization lock state of each core is waiting to access the critical section (lock=MUST_WAIT), which is the unlocked state.

[0031] In the RON-Plock embodiment, the Bollinger value state of the synchronization lock state of each core is set by atomic operations, and the initial value of the Bollinger value state is set to a false value to indicate that the synchronization lock state of each core is an unlocked state.

[0032] In the RON-Plock embodiment, the pre-operation further includes setting a wait array (WaitArray[NUM_core]), the number of wait arrays is the same as the number of multiple cores, and each element of the wait array is the number of waiting threads and the synchronization lock variable for each core.

[0033] Furthermore, the pre-processing also includes setting an identification code and a search order for each core. The number of waiting threads and the order of setting the synchronization lock variables for each core of the waiting array are set according to these identification codes and search orders.

[0034] In the RON-Plock embodiment, the access operation includes setting a locking function (void lock()). The locking function includes the following steps: each time an execution thread requests access to the critical section, the number of waiting execution threads in the waiting array corresponding to the core where the execution thread is located is updated with atomic operations.

[0035] Furthermore, after the locking function updates the number of waiting threads in the waiting array corresponding to the core where the thread is located with atomic operations, it sets a change loop, which includes the following steps: Trying to see if the synchronization lock variable of the core where the current thread is located can be updated from the unlocked state to the locked state; When the synchronization lock variable of the core where the current thread is located is updated from the unlocked state to the locked state, it means that the current thread is allowed to access the critical section and can leave the locking function.

[0036] Furthermore, the locking function performs atomic operations in the change loop, using comparison and swap to attempt to set the core corresponding to the thread from the unlocked state to the locked state. When the core corresponding to the thread is successfully set from the unlocked state to the locked state through comparison and swap, the thread is allowed to access the critical section and then leaves the locking function.

[0037] Furthermore, in the change loop, if an atomic operation fails to set the kernel corresponding to the current thread from an unlocked state to a locked state using comparison and swap, it indicates that the current thread is not allowed to access the critical section. That is, the current thread relinquishes its right to access the critical section from the kernel that allows access to the critical section. Then, the following steps are performed: An atomic operation (e.g., an atomic operation of comparison and swap) is used to attempt to set the Bollinger value from a false value to a allowed true value; When the Bollinger value is updated from a false value to a true value, it indicates that the synchronization lock state of the kernel corresponding to the current thread is allowed to be set to a locked state, making the current thread the next thread allowed to access the critical section, and then the change loop is exited; If the Bollinger value cannot be updated from a false value to a true value, then the process is carried out according to whether the change loop can remove the synchronization lock variable of the kernel where the current thread is located from the waiting to access the critical section unlocked state.

[0038] In RON-Plock, the exit operation also includes setting an unlock function. The unlock function includes the following processing steps: using atomic operations to reduce the number of waiting threads in the wait array of the core where the currently executing thread resides, and setting a check loop. The check loop includes the following steps: sequentially checking whether the number of waiting threads in the wait array of each core is the fifth preset value; when the number of waiting threads in the wait array of the currently checked core is the fifth preset value, using atomic operations to update the synchronization lock state of the wait array of the currently checked core to the locked state; when the synchronization lock state of the wait array of the currently checked core is updated to the locked state, then specify the next thread to access the critical section in the currently checked core's threads, and then exit the locking function; when the number of waiting threads in the wait array of the currently checked core is not the fifth preset value, indicating that there are currently no threads, then set the Boolean value state to false, indicating that the synchronization lock state of each core is unlocked.

[0039] RON-Plock can be further coded in C language, and its code is as follows: 1 struct PLock {numWait=0 , lock=MUST_WAIT;} 2 atomic_bool InUse=false; / / per-lock variable 3 PLock WaitArray[ NUM_core] ; / / per-lock variable 4 void lock() 5 atomic_inc(&WaitArray[ TSP_ID] . numWait) ; 6 while( 1 ) 7 if ( cmpxchg(&WaitArray[ TSP_ID] . lock, HAS_LOCK,MUST_WAIT) ) 8 return; 9 if ( cmpxchg(&InUse, false, true) ) 10 return; 11 void unlock() 12 atomic_dec(&WaitArray[ TSP_ID] . numWait) ; 13 for( int i = 1 ; i < NUM_core+1 ; i++) 14 if( WaitArray[ ( TSP_ID+i)%NUM_core] . numWait>0 ) 15 WaitArray[ ( TSP_ID+i)%NUM_core]=HAS_LOCK; 16 return; 17 InUse=false;

[0040] The variables in the above encoding are similar to those in the RON-ticket implementation. The following describes the variables not used in RON-ticket: InUse: Represents the Boolean value state of the synchronization lock state of each core. If the variable is "false", there are no threads in the critical section; otherwise, if the variable is "true", it means there are threads in the critical section. This is a per-synchronization-lock variable.

[0041] In the above code, the elements of the wait array correspond to each core component. These elements consist of two variables: the number of waiting threads (numWait) and the synchronization lock variable (lock). When each thread wants to access the critical section, it must use an atomic operation (atomic_inc()) to set the number of waiting threads (numWait) of the core to which the current thread belongs. When the synchronization lock variable of a core can be changed from the unlocked state to the locked state (HAS_LOCK) using an atomic operation, the thread currently executing on that core can access the critical section (i.e., lines 7-8 of the RON-Plock code in C language), and again use an atomic operation to attempt to set the Boolean value of the synchronization lock state. If the Bollinger Bands value of the synchronization lock state was originally false, indicating that the lock was not occupied, it is set to true, indicating that the critical section is occupied by the currently executing thread, and the locking function is exited (i.e., lines 9-10 of the RON-Plock C code). Furthermore, lines 9-10 of the algorithm perform an additional check on the Bollinger Bands value of the synchronization lock state. If the Bollinger Bands value can be successfully set from false to true, it means that no other thread is accessing or attempting to access the critical section in any of the cores, and therefore the currently executing thread can safely access the critical section. This is a strategy to ensure that even when the synchronization lock state of a specific core cannot be directly obtained as locked, it is still possible to determine whether access to the critical section is possible in another way.

[0042] Furthermore, `void unlock()` defines an unlock function (i.e., line 11 of the RON-Plock C code). The purpose of the unlock function is to release the synchronization lock currently held by the thread into an unlocked state and attempt to pass the opportunity to other threads waiting to access the critical section. This reduces the number of waiting threads in the current core (specified by `TSP_ID`) through atomic operations. This means that the current thread no longer needs to be in a locked state, thus reducing the number of waiting threads (i.e., line 12 of the RON-Plock C code). Then, it iterates through all cores, attempting to find the next thread that needs to acquire a locked state (i.e., line 13 of the RON-Plock C code). It checks if each core has waiting threads; if so, if a thread is waiting (i.e., `numWait` is greater than 0), then the next operation is performed (i.e., line 14 of the RON-Plock C code). The first core with waiting threads is locked, meaning its synchronization lock variable can be locked, allowing waiting threads to access the critical section (line 15 of RON-Plock's C code). The function exits (line 16 of RON-Plock's C code). InUse is set to false, indicating that no threads are currently using the critical section, thus allowing other threads to attempt to access it.

[0043] To compare the performance of the present invention with other methods, the above-described RON-Ticket and RON-Plock embodiments are further compared with existing methods:

[0044] In the performance evaluation experiments, we used an AMD Threadripper 2990WX equipped with 64 cores (32 physical cores) and a GNU / Linux operating system. The kernel version was 5.4. The compiler used was gcc-9.3, with the optimization parameter "-march=znver1 -O3" set, which enabled gcc-9.3 to optimize for the Threadripper microarchitecture. All experiments were run 100 times, and the results were averaged. AMD Threadripper is a chip-level NUMA. There are four modules in the chip, each module has two core complexes (CPU ComplexX, CCX), and each CCX has four cores. In addition, the Linux numastat command showed that the 2990WX has 4 NUMA nodes. The cache coherence protocol operates at the cache line granularity, which means low latency also means high bandwidth. Therefore, the transmission latency obtained from the experiments not only provides information for the design of inter-core locking algorithms but also provides insights into the performance characteristics of the underlying hardware. By analyzing inter-core latency, the operating system can optimize the lock-unlock algorithm accordingly. Furthermore, better performance may be achieved if CPU manufacturers can provide detailed microarchitecture information about NoC (Network on Chip, NoC). The source code in this article can be found at https: / / github.com / shiwulo / ron-osdi2023. For a more comprehensive comparison with other methods, we used the LiTL (Library for Transparent Lock interposition) framework (see DOI: https: / / doi.org / 10.1145 / 3301501). In this invention, RON-ticket or RON-Plock can be compared with other methods in different benchmarks by using LD_PRELOAD. The following describes the performance characteristics of each traditional algorithm, rather than the implementation details. (1) Plock (see https: / / doi.org / 10.1109 / 71.80120): A spinlock for GNU P threads. A thread intending to access a critical section tests the synchronization lock until its value is equal to 0. When a thread leaves the critical section, it sets the synchronization lock to 0. The first kernel to observe that the synchronization lock is 0 can access the critical section. The closer the core to which the synchronization lock is released, the greater the likelihood that the core will access the critical section. (2) Ticke (see https: / / doi.org / 10.1145 / 359060.359076): This method allows each thread waiting to access the critical section to have a “ticket” number. Threads wait until the “authorization” equals their ticket number.All waiting threads continuously query the "authorization" value using atomic instructions, which consumes limited chip network (NoC) bandwidth. (3) MCS (see https: / / doi.org / 10.1145 / 103727.103729): Because all threads waiting to access the critical section are queued in a linked list, when a thread leaves the critical section, it only needs to set the "wait flag" of the next task to false. For CPUs that support the directory cache coherence algorithm, setting the wait flag of the next thread is more efficient than multicast. MCS does not optimize interconnect latency in multi-core architectures. (4) C-BO-MCS (see https: / / doi.org / 10.1145 / 2686884): A thread must first acquire the MCS lock of its NUMA node. Then, it must compete with threads on other NUMA nodes for the backoff lock. If a core is adjacent to a core that has acquired the C-BO-MCS lock, it has a higher priority to access the critical section. In this way, threads belonging to the same node can be grouped together to reduce handover costs. (5) ShflLock (see https: / / doi.org / 10.1145 / 3341301.3359629): This also uses grouping to improve performance. Shuffle can assign a thread in the queue to be responsible for shuffling. However, when a task that is allowed to access CS is shuffling the queue, the thread cannot immediately access the critical section, and system performance may degrade.

[0045] In this comparison experiment, each method was analyzed quantitatively through controlled micro-benchmarking. In each set of experiments, each thread was bound (i.e., sched_setaffinity()) to a hardware thread, and each method was tested. The test code is shown below: 1 void thread() : 2 while( 1 ) : 3 spin_lock() ; / / LS 4 for ( each element in SharedData) : / / CS 5 element = element + 1 ; / / CS 6 spin_unlock() ; / / US 7 t = clock_gettime() ; / / nCS 8 / / syscall overhead, rdtscp implement in userspace 9 while( clock_gettime() -t > nCS_size*rand( 0 . 85~1 . 15 ) ) ;

[0046] In this experiment, Simultaneous multi-threading (SMT) was enabled, with 2 hardware threads per core. The total number of software threads was 64. In the while loop of the test algorithm (lines 2-9 of the test code), a thread in the Lock Section (LS) (line 3 of the test code) requests access to the critical section (lines 4-5 of the test code). After the thread accesses the critical section, it reads and writes each entry in SharedData and releases the lock to the unlock section (US) when the thread leaves the critical section (line 6 of the test code). When the elapsed time of the non-critical section (nCS) exceeds ±15% of the non-critical section's value, clock_gettime(), as defined in the POSIX.1-2001 standard, is called (lines 7-9 of the test code).

[0047] In each set of experiments, each thread is bound (i.e., sched_setaffinity()) to a core and the test algorithm is executed. Each core has at most [num_thread ÷ num_core] threads. A key feature of the RON-ticket implementation is bounded waiting. In this experiment, micro-benchmarks were used to evaluate the performance of RON-ticket and RON-Plock. In the oversubscription case, two factors affect performance. The first is whether the thread holding the synchronization lock is scheduled out. The second is whether each method specifies the thread that will access the next critical section, and whether it is scheduled out.

[0048] In Figure 2, RON-ticket and RON-Plock perform better under oversubscription conditions, where the y-axis represents the number of synchronized locks per second (1.0 is one million per second). Although C-BO-MCS(-B) and ShflLock(-B) also support oversubscription, the number of lock-unlock operations per second drops rapidly.

[0049] RON-Plock and Plock use intuitive methods (e.g., test-test-and-set) to solve the oversubscription problem. Plock allows one thread to access the critical section as long as the lock holder is not scheduled out (because all threads are waiting for the same variable). RON-plock is similar to Plock, except that all threads on the next core are scheduled out. Because RON-Plock is based on RON, it outperforms Plock. RON-ticket, ShflLock-B, and C-BO-MCS-B use system calls (i.e., futex() and yield()) to prevent threads from spinning unnecessarily. Unlocking ShflLock-B directly wakes up the next thread. However, unlocking C-BO-MCS-B may wake up all threads that can access the critical section. RON-ticket makes the next thread that can access the critical section busy-wait, while other threads on that core are in a sleep state. For the same reasons as RON-Plock, RON-ticket has better performance than traditional methods.

[0050] Furthermore, this addresses the issue of algorithm performance varying with the number of threads used. This experiment was conducted on a 2990WX with SMT technology. In the experiment, each thread accessed 100 integers in the critical section, while non-critical sections took 10000 ± 15% nanoseconds (ns).

[0051] As shown in Figure 3, when the number of threads is less than 64, the performance of RON-Plock improves with the increase of the number of threads. In this experimental result, both "executing more threads simultaneously" and "having shorter non-critical sections" lead to increased contention for accessing critical sections. However, when the number of threads exceeds 64, the performance of all algorithms is determined by their ability to handle oversubscription problems.

[0052] We selected five different application-level benchmarks to represent different performance bottlenecks. For consistency of the experiments, we used RON-ticket for comparison with other traditional methods.

[0053] In the experiment, we used Google's LevelDB to test the performance of the spinlock. The horizontal axis of Figure 4 represents the algorithm being tested, and the vertical axis represents the time cost of each operation reported by LevelDB. Due to differences in data proportions, "fillsync" was normalized to MCS, while the others were normalized to Ticket.

[0054] We used LevelDB's db_bench to evaluate performance, using 1 million entries and 64 threads. For each spinlock, fillseq, fillsync, fillrandom, overwrite, and readrandom were tested. The last item is the geometric mean of the five LevelDB tests. RON-ticket, ShflLock, and C-BO-MCS are spinlocks optimized for ccNUMA or NUMA. Note that RON-ticket supports oversubscription and also satisfies bounded wait. As can be seen from Figure 4, RON-ticket improves performance by 22.1% and 24.2% compared to ShflLock and C-BO-MCS, respectively.

[0055] Benchmarking for different contention levels

[0056] We applied four additional application benchmarks to evaluate the performance of different algorithms. These algorithms were selected from LiTL (DOI: https: / / doi.org / 10.1145 / 3301501) and cover high and low contention scenarios. Volrend and Raytrace are from the SPLASH2x benchmark set, representing extreme and high contention levels, respectively. For the extreme level, more than 40 threads are waiting to acquire the same lock instance. For the high level, approximately 10 to 40 threads are waiting to acquire the lock. Dedup and Ferret are from the PARSEC3.0 benchmark set, representing memory pressure and relatively low contention levels, respectively. In Figure 5, the vertical axis represents the elapsed time of the benchmark tasks (including the geometric mean of LevelDB Figure 4). Due to the different data scales, the figures are expressed as the percentage of the algorithm that performed the worst in each task.

[0057] Raytrace's bottleneck is lock contention for protecting a single counter, with approximately one million acquisitions per second. RON-ticket, MCS, and Plock complete the task in about 70% of the elapsed time. MCS is optimized for multi-core systems and has a dedicated cache to reduce the overhead of lock contention, making it well-suited for high contention levels. Plock's code is not optimized for multi-core systems. However, cores adjacent to the core releasing the lock are more likely to successfully execute the atomic operation compare_exchange() to acquire the lock. Therefore, Plock is implicitly optimized for multi-core platforms.

[0058] Under extreme contention levels, the performance of Plock and MCS begins to decline, while ShflLock and RON-ticket can handle the pressure. At the extreme contention level (Volrend), RON-ticket achieves the best performance, using only 24.3% of the time required to complete the task. The bottleneck in Volrend is lock contention protecting different task queues, with approximately 40 threads waiting. This benchmark verifies that the RON algorithm generally performs best under higher contention levels. As more cores are owned by threads waiting for lock instances, routing paths can significantly reduce handover costs.

[0059] However, under low contention levels, RON-ticket performs only the second best among all six algorithms. Ferret is a parallelization software, running approximately 2000 times per second. While RON-ticket uses about 20% of the C-BO-MCS elapsed time, Plock uses only about 15.6% of the C-BO-MCS elapsed time. The ticket variable guarantees fairness because threads continuously query global variables to know if they can access the critical section. The ticket variable is suitable for low contention levels. However, under higher pressure, the bandwidth consumed by synchronization lock contention limits the bandwidth available for handover.

[0060] Furthermore, according to Dedup's results, RON-ticket and Plock exhibit low memory pressure. Dedup allocated a large number of locks (266k), which would put pressure on memory if the synchronization locks were not reusable. The reusability of WaitArray and Get_TSP_ID, among others, gives RON-ticket the ability to handle a large number of lock allocations.

[0061] Finally, this invention also cites the entire contents of the journal article "RON: One-Way Circular Shortest Routing to Achieve Efficient and Bounded-waiting Spinlocks" (publication URL: https: / / www.usenix.org / conference / osdi23 / presentation / lo). Furthermore, it should be specifically stated that the first author of this journal article is the inventor of this invention. All algorithms and concepts were invented by the first author, who has made full contributions to the invention of this application. Other authors only conducted experiments, guided experiments, or modified the article content, and have not made any contributions to this invention.

[0062] As described above, both embodiments of the present invention use atomic operations (such as atomic_fetch_add, atomic_inc, atomic_dec, cmpxchg) to ensure safe variable reading and modification in a multi-threaded environment. These atomic operations ensure data consistency and system stability, preventing data races and condition races. Both implement core-specific lock control for multi-core processors. Through the WaitArray array, each core has its corresponding lock state management (TicketLock or PLock structure), allowing for more granular management of the synchronization lock acquisition behavior of threads on each core.

[0063] Furthermore, in this invention, a waiting mechanism is implemented when a thread cannot immediately acquire the lock to access the critical section. RON-ticket uses the sched_yield() function to yield CPU time, while RON-Plock implements waiting through a loop (while(1)) and condition checks (cmpxchg operation). Both algorithms include the management of lock states (such as MUST_WAIT, HAS_LOCK, InUse) to indicate whether the synchronization lock is available and whether any thread is in the critical section. The management of these states is crucial to ensuring the correct acquisition and release of the synchronization lock. When a thread releases the synchronization lock, both embodiments of this invention implement a mechanism for dynamically adjusting the lock state so that other waiting threads can access the critical section in sequence. RON-ticket does this by updating the grant value, while RON-Plock does it by changing the lock state and the InUse flag.

[0064] In summary, the common technical features of RON-ticket and RON-Plock include the application of atomic operations, core-oriented synchronization lock control, implementation of waiting mechanisms, management of synchronization lock states, and dynamic adjustment mechanisms for synchronization locks. These features together form the basis for their handling of multi-threaded synchronization and critical section access control on multi-core processors.

[0065] The above description is only for illustrating the preferred embodiments of the present invention and is not intended to limit the scope of implementation. Any simple substitutions and equivalent changes made in accordance with the scope of the patent application and the contents of the patent specification of the present invention shall fall within the scope of the patent application of the present invention. [Simplified Explanation of the Diagram]

[0009] Figure 1 is a flowchart of the present invention; Figure 2 is a performance comparison diagram of the present invention and the traditional method under oversubscription; Figure 3 is a comparison diagram of the number of times the present invention and the traditional method access the locked state per second under different thread counts; Figure 4 is a performance comparison diagram of the present invention and the traditional method using Google's LevelDB to test spin locks; Figure 5 is a performance comparison diagram of applications with different contention levels of the present invention.

Claims

1. A method for managing access to a critical section in a multi-core processor, the multi-core processor having a plurality of cores, the steps of the multi-core processor managing access to a critical section by the plurality of cores comprising: At any given time, only one of the multiple cores is allowed to access the critical section; The multi-core processor executes an algorithm to determine that only one of the plurality of cores is allowed to access the critical section, and to designate the next thread to access the critical section within the core that is allowed to access it; wherein, if the currently running thread of the core that is allowed to access the critical section wants to access the critical section, but the currently running thread is not the designated next thread to access the critical section, then the currently running thread relinquishes its right to access the critical section from the core that is allowed to access it; wherein, the multi-core processor executes an algorithm to determine that only one of the plurality of cores is allowed to access the critical section, and to designate the next thread to access the critical section within the core that is allowed to access it, the algorithm comprising the following operations: Pre-processing: Set a data structure, a synchronization lock state, and a number of waiting threads for each of the plurality of cores, and use atomic operations to update the data structure, the number of waiting threads, and the synchronization lock state for each core. The synchronization lock state for each core is used to indicate a locked state that allows access to the critical section or an unlocked state that waits to access the critical section. The number of waiting threads for each core indicates the number of threads waiting to access the critical section. Each data structure is used to indicate the order in which each thread in the number of waiting threads is allowed to enter the critical section. Access operation: When a thread requests access to the critical section, an atomic operation is used to increase the number of waiting threads in the core containing the thread, and an atomic operation is used to update the synchronization lock state to the locked state or the unlocked state. At any given time, only one of the multiple cores has the synchronization lock state in the locked state. In the locked state, any core uses the data structure and the number of waiting threads to designate one of all threads as the next thread to access the critical section. Exit operation: When a thread leaves the critical section, an atomic operation is used to decrease the number of waiting threads in the core containing the thread, and an atomic operation is used to update the data structure, the number of waiting threads, and the synchronization lock state of the core containing the thread to determine whether to maintain the locked state of the core containing the thread or to set another core in the multiple cores to allow access to the critical section. After completing the pre-operations, the access operation is performed every time a thread requests access to the critical section, and the exit operation is performed every time a thread leaves the critical section.

2. The method for managing access critical sections in a multi-core processor as described in claim 1, wherein the data structure is a ticket data structure, the ticket data structure defines an authorization variable and a ticket variable for each core, and when an access operation is performed, the value of the authorization variable, the value of the ticket variable, and the number of waiting threads in the core where the thread is located are updated with atomic operations, and based on the difference between the value of the authorization variable and the value of the ticket variable, and the case where the number of waiting threads is zero or greater than zero, the synchronization lock state of the core where the thread is located is updated to the locked state or the unlocked state.

3. The method for managing access critical sections in a multi-core processor as described in claim 2, wherein the pre-processing further includes: Set up a wait array, the number of which is the same as the number of cores, and each element of the wait array represents the ticket number variable and the authorization variable for each core. The atomic operation updates the values ​​of the ticket number variable and the authorization variable in the wait array corresponding to the core where the thread is located.

4. The method for managing access critical sections in a multi-core processor as described in claim 3, wherein the pre-processing further includes: Set a unique identifier for each core; And set a sequential array, where each element of the sequential array is a lookup order corresponding to each of the identification codes.

5. The method for managing access to critical sections in a multi-core processor as described in claim 4, further comprising: Set a locking function, which includes the following steps: Each time a thread requests access to the critical section, increment the number of waiting threads using the atomic operation; After the number of waiting threads has been incremented, if it is determined that the number of waiting threads was zero before the increment, the state of the synchronization lock corresponding to the core where the thread is located is directly set to the locked state. And when the number of waiting threads is updated, if the number of waiting threads is not zero, use the atomic operation to obtain the identification code corresponding to the core where the thread is located, and update the ticket number variable of the waiting array corresponding to the identification code.

6. The method for managing access critical sections in a multi-core processor as described in claim 5, wherein after the locking function updates the ticket number variable for the waiting array corresponding to the identification code, it further includes: An access check loop is set up to check whether the currently executing thread can enter the critical section, and includes the following steps: Compare the difference between the value of the authorized variable and the value of the ticket number variable in the wait array of the core where the thread resides to whether it is a first preset value; When the difference between the value of the authorized variable and the value of the ticket number variable is the first preset value, the core where the thread resides relinquishes the right to access the critical section, causing the synchronization lock state of the core where the thread resides to be updated to the unlocked state; When the difference between the value of the authorized variable and the value of the ticket number variable is not the first preset value, compare the difference between the value of the authorized variable and the value of the ticket number variable in the wait array of the core where the thread resides to whether it is a second preset value; And when the difference between the value of the authorized variable and the value of the ticket number variable is the second preset value, the synchronization lock state of the core where the execution thread is located is updated to the locked state, allowing the execution thread corresponding to the difference between the value of the ticket number variable and the value of the authorized variable being the second preset value, as the next execution thread to access the critical section in the core that is allowed to access the critical section.

7. The method for managing access to critical sections in a multi-core processor as described in claim 4, wherein the exit operation further includes: Set up an unlock function, which includes the following processing steps: decrement the number of waiting threads in the core where the thread is located using the atomic operation; after the number of waiting threads is decremented, determine whether the number of waiting threads in the core where the thread is located is a third preset value; and if the number of waiting threads is the third preset value, it means that there are no waiting threads in the core where the thread is located, and the thread can leave the unlock function.

8. The method for managing access to a critical section in a multi-core processor as described in claim 7, wherein when the number of waiting threads is the third preset value, indicating that there are no waiting threads, the following steps are performed: A departure check variable is set, which is the identification code of the core currently allowed to access the critical section, and the next core to be checked is obtained in the access order; A departure check loop is set to access all cores of the multi-core processor to determine the next thread allowed to enter the critical section, including the following steps: Determining whether the difference between the authorization variable and the ticket number variable in the waiting array corresponding to the currently checked core is a fourth preset value; When the difference between the authorized variable and the ticket number variable in the waiting array corresponding to the currently checked core is the fourth preset value, the authorized variable in the waiting array of the currently checked core is incremented by the atomic operation, and then the exit check loop ends, so that the synchronization lock state of the currently checked core is set to the locked state; and when the currently checked core is in the locked state, the data structure and the number of waiting threads are used to designate one of all threads as the next thread to access the critical section.

9. The method for managing access critical sections in a multi-core processor as described in claim 8, wherein after the exit check loop ends, the exit check variable is updated, and the step of updating the exit check variable includes: Based on the identification code of the currently inspected core, the departure inspection variable is updated to the identification code of the next inspected core according to the search order; wherein, when the departure inspection variable is the identification code of the last core in the search order, it is updated to the identification code of the first core in the search order.

10. The method for managing access to critical sections in a multi-core processor as described in claim 1, wherein the data structure is a spin data structure, the spin data structure includes a number of waiting threads and a synchronization lock variable, the number of waiting threads for each core represents the number of threads waiting to access the critical section, and the synchronization lock variable for each core is used to indicate whether the synchronization lock state of each core is the locked state or the unlocked state, wherein the initial value of the number of waiting threads is zero, and the initial value of the synchronization lock variable for each core is the unlocked state.

11. The method for managing access critical sections in a multi-core processor as described in claim 10, wherein the pre-processing further includes: The atomic operation sets a Bollinger value state for the synchronization lock state of each core, and sets the initial value of the Bollinger value state to a false value to indicate that the synchronization lock state of each core is in the unlocked state.

12. The method for managing access critical sections in a multi-core processor as described in claim 11, wherein the pre-processing further includes: Set up a wait array, the number of which is the same as the number of cores, and each element of the wait array is the number of waiting threads and the synchronization lock variable for each core.

13. The method for managing access critical sections in a multi-core processor as described in claim 12, wherein the preceding pre-operation further includes: Each core is assigned an identification code and a search order; The number of waiting threads for each core of the waiting array and the order in which the synchronization lock variables are set are based on the identification codes and the access order.

14. The method for managing access to critical sections in a multi-core processor as described in claim 13, wherein the access operation includes: Define a locking function that includes the following steps: Each time a thread requests access to the critical section, update the number of waiting threads in the waiting array corresponding to the core where the thread resides using the atomic operation.

15. The method for managing access to a critical section in a multi-core processor as described in claim 14, wherein after the locking function updates the number of waiting threads in the waiting array corresponding to the core where the thread resides with the atomic operation, a change loop is set, the change loop including the following steps: attempting to update the synchronization lock variable of the core where the current thread resides from the unlocked state to the locked state; when the synchronization lock variable of the core where the current thread resides is updated from the unlocked state to the locked state, indicating that the current thread is the next thread to access the critical section, the locking function can be exited.

16. The method for managing access to a critical section in a multi-core processor as described in claim 15, wherein if the synchronization lock variable of the core in which the currently executing thread resides cannot be updated from the unlocked state to the locked state, indicating that the currently executing thread is not allowed to access the critical section, the currently executing thread relinquishes its right to access the critical section from the core in which access to the critical section is permitted, and then performs the following steps: whether the atomic operation updates the Bollinger value state from the false value to a true value; and when the Bollinger value state is updated from the false value to the true value, indicating that the synchronization lock state of the core in which the currently executing thread resides is allowed to be set to the locked state, making the currently executing thread the next thread allowed to access the critical section, and then exiting the change loop.

17. The method for managing access to critical sections in a multi-core processor as described in claim 16, wherein if the Boolean value state cannot be updated from the false value to the true value, then the process is further performed according to the step of whether the change loop can remove the synchronization lock variable of the core where the currently executing thread is located from the unlocked state of waiting to access the critical section.

18. The method for managing access to a critical section in a multi-core processor as described in claim 13, wherein the exit operation further includes setting an unlock function, the unlock function comprising the following processing steps: reducing the number of waiting threads in the wait array of the core where the currently executing thread resides using an atomic operation; setting a check loop comprising the following steps: sequentially checking whether the number of waiting threads in the wait array of each core is a fifth preset value; when the number of waiting threads in the wait array of the currently checked core is the fifth preset value, updating the synchronization lock state of the wait array of the currently checked core to the locked state using an atomic operation; and when the synchronization lock state of the wait array of the currently checked core is updated to the locked state, specifying the next thread to access the critical section in the currently checked core, and then exiting the locking function.

19. The method for managing access critical sections of a multi-core processor as described in claim 18, wherein when the number of waiting threads in the waiting array of the currently inspected core is not the fifth preset value, indicating that there are currently no threads, the Boolean value state is set to the false value, indicating that the synchronization lock state of each core is the unlocked state.

Citation Information

Patent Citations

  • Distributed operation system and operation method for distributed system

    CN105205031A

  • System including a fine-grained memory and a less-fine-grained memory

    TW200912643A

  • System of system and method for virtualization and cloud computing system

    TW201600970A

  • Shared buffered memory routing

    TW201702882A