Intel® Advisor Help

Lock Annotations

Lock annotations mark where you expect you will be adding explicit synchronization.

Syntax

C/C++:

ANNOTATE_LOCK_ACQUIRE(pointer-expression); and ANNOTATE_LOCK_RELEASE(pointer-expression);

Fortran:

call annotate_lock_acquire(address) and call annotate_lock_release(address)

C#:

Annotate.LockAcquire([int expr]); and Annotate.LockRelease([int expr]); (for each annotation, its argument is optional)

With C/C++ and Fortran programs, all of the lock annotations use an address value to represent distinct locks in your final program. You can use the address value 0 to represent a global "lock" that is the same across the entire program. With C# programs, the argument is an int with a default value of 0 (zero).

Intel recommends that you start by using a default lock, unless you need additional locks for performance scaling.

The modeling step is aware of the standard locking routines in the Windows* OS API, as well as Intel® TBB, OpenMP*, and Intel® Cilk™ Plus, so there is no need to annotate existing locking. Lock annotations are only required for cases where you are not already using synchronization.

The lock-acquire and lock-release annotations denote points in your program where you intend to acquire and release locks. These annotations take a single parameter, which is an address that you choose.

For example, if you decided you would have a lock used only for glob_variable, you specify the same memory address for all cases where you are protecting access to glob_variable, to represent that specific lock. The sample below uses the variable's address to represent the lock that will be associated with glob_variable.

You typically can use one of the following four values, using a finer granularity of synchronization when necessary:

This C/C++ example shows the intent for the parallel program to acquire and release a lock around the access to the global variable glob_variable in each task:

    ...
    extern int glob_variable = 0;
    ...
    ANNOTATE_SITE_BEGIN(sitename);
    for (I=0; i<N; I++) {
        ANNOTATE_TASK_BEGIN(taskfunc1);
        func1(I);
        ANNOTATE_LOCK_ACQUIRE(&glob_variable);
        glob_variable++;
        ANNOTATE_LOCK_RELEASE(&glob_variable);
        func2(I);
        ANNOTATE_TASK_END();
    }
    ANNOTATE_SITE_END();
    ...

This Fortran example also shows the intent to acquire and release a lock around the access to the global variable glob_variable in each task:

 ...
 integer :: glob_variable = 0 

 call annotate_site_begin("sitename")
   do i=1,size
      call annotate_task_begin("taskfunc1")
      call func1(i)
      call annotate_lock_acquire(0)
      glob_variable = glob_variable + 1
      call annotate_lock_release(0)
      call func2(i)
      call annotate_task_end
   end do
 call annotate_site_end
 ...

The following C/C++ example is a typical use of a data item's address. It shows the use of an Entity address, where there is a vector of integers that are each going to have an associated lock, because the program is counting random elements of the array that will be accessed by different tasks, some of which may occasionally have the same random value. The text from adding annotations appears in bold below.

   struct Entity {
       int val;
   };
   ...
   std::vector<Entity> v;
   ...

   for (int I=0; i<v.size()*10000; I++) {
       int random_int = random_n();
       ANNOTATE_LOCK_ACQUIRE(&v[random_int]);
          v[random_int].val++;
       ANNOTATE_LOCK_RELEASE(&v[random_int]);
   }
   ...

Using Lock Annotations

Lock addresses are the basis of lock annotations, and each lock address corresponds to the intent to create a unique lock, or other synchronization mechanism, in the final program. Tasks sharing a parallel site are modeled as executing in parallel unless you describe synchronization using lock addresses, or known locking mechanisms.

See Also