<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" version="2.0">
  <channel>
    <title>topic Re: Possible non-thread-safe behavior in dfeast_scsrgv with parallel interval search in Intel® oneAPI Math Kernel Library</title>
    <link>https://community.intel.com/t5/Intel-oneAPI-Math-Kernel-Library/Possible-non-thread-safe-behavior-in-dfeast-scsrgv-with-parallel/m-p/1724232#M37417</link>
    <description>&lt;P&gt;Hello Fengrui,&lt;/P&gt;&lt;P&gt;Thank you for your reply.&lt;/P&gt;&lt;P&gt;To clarify, I’m &lt;STRONG&gt;not using OpenMP&lt;/STRONG&gt;.&lt;BR /&gt;The FEAST solvers (dfeast_scsrgv) are executed in &lt;STRONG&gt;parallel threads&lt;/STRONG&gt; created manually using boost::thread_group (each thread computes the eigenvalues in a different interval).&lt;BR /&gt;Each thread constructs its own instance of EigenValuesAndVectorsFeast, which in turn calls dfeast_scsrgv.&lt;/P&gt;&lt;P&gt;Attached you'll finde the relevant code section.&lt;/P&gt;&lt;P&gt;The results become perfectly reproducible (same as the sequential version).&lt;BR /&gt;Without the mutex, the computed eigenvalues vary slightly between runs — even when mkl_set_num_threads(1) is used.&lt;/P&gt;&lt;P&gt;So it seems that dfeast_scsrgv is not fully thread-safe when called concurrently from different threads, even if the data and fpm arrays are distinct per thread.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
    <pubDate>Thu, 30 Oct 2025 17:47:33 GMT</pubDate>
    <dc:creator>GiPe1978</dc:creator>
    <dc:date>2025-10-30T17:47:33Z</dc:date>
    <item>
      <title>Possible non-thread-safe behavior in dfeast_scsrgv with parallel interval search</title>
      <link>https://community.intel.com/t5/Intel-oneAPI-Math-Kernel-Library/Possible-non-thread-safe-behavior-in-dfeast-scsrgv-with-parallel/m-p/1723878#M37403</link>
      <description>&lt;P&gt;We are running multiple FEAST eigenvalue searches (dfeast_scsrgv) in parallel on different intervals.&lt;BR /&gt;When run sequentially, the eigenvalues are perfectly reproducible.&lt;BR /&gt;When run concurrently, results vary between executions unless we protect dfeast_scsrgv with a mutex.&lt;BR /&gt;&lt;BR /&gt;Environment:&lt;BR /&gt;- Intel MKL version: Intel oneAPI mkl 2023.1.0&lt;BR /&gt;- OS: Windows 2011&lt;BR /&gt;- Compiler:&amp;nbsp; MSVC&lt;BR /&gt;&lt;BR /&gt;&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Tue, 28 Oct 2025 11:52:17 GMT</pubDate>
      <guid>https://community.intel.com/t5/Intel-oneAPI-Math-Kernel-Library/Possible-non-thread-safe-behavior-in-dfeast-scsrgv-with-parallel/m-p/1723878#M37403</guid>
      <dc:creator>GiPe1978</dc:creator>
      <dc:date>2025-10-28T11:52:17Z</dc:date>
    </item>
    <item>
      <title>Re: Possible non-thread-safe behavior in dfeast_scsrgv with parallel interval search</title>
      <link>https://community.intel.com/t5/Intel-oneAPI-Math-Kernel-Library/Possible-non-thread-safe-behavior-in-dfeast-scsrgv-with-parallel/m-p/1723928#M37406</link>
      <description>&lt;P&gt;Thank you for posting in the forum!&lt;/P&gt;
&lt;P&gt;Could you share more details how the FEAST solvers are called? Are they called in an OpenMP region with one thread for each of them? It will be great if you could share a code to reproduce this issue.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Tue, 28 Oct 2025 18:33:51 GMT</pubDate>
      <guid>https://community.intel.com/t5/Intel-oneAPI-Math-Kernel-Library/Possible-non-thread-safe-behavior-in-dfeast-scsrgv-with-parallel/m-p/1723928#M37406</guid>
      <dc:creator>Fengrui</dc:creator>
      <dc:date>2025-10-28T18:33:51Z</dc:date>
    </item>
    <item>
      <title>Re: Possible non-thread-safe behavior in dfeast_scsrgv with parallel interval search</title>
      <link>https://community.intel.com/t5/Intel-oneAPI-Math-Kernel-Library/Possible-non-thread-safe-behavior-in-dfeast-scsrgv-with-parallel/m-p/1724039#M37408</link>
      <description>&lt;P&gt;Hello Fengrui,&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;To clarify, I’m not using OpenMP.&lt;BR /&gt;The FEAST solvers (dfeast_scsrgv) are executed in parallel threads created manually using boost::thread_group (each thread computes the eigenvalues in a different interval).&lt;BR /&gt;Each thread constructs its own instance of EigenValuesAndVectorsFeast, which in turn calls dfeast_scsrgv.&lt;/P&gt;&lt;P&gt;Here is the relevant code section:&lt;/P&gt;&lt;P&gt;&lt;BR /&gt;&lt;BR /&gt;&lt;/P&gt;&lt;LI-CODE lang="markup"&gt;bool 
MklSymMatrixSolver::EigenSystem(int neig, int m, double *eig, double *eiv, int &amp;amp;neig_found)
{
if (!matrix_a_  &amp;amp;&amp;amp; !matrix_b_)
return false;

std::vector&amp;lt;std::pair&amp;lt;double,double&amp;gt;&amp;gt; intervals;

intervals.push_back(std::make_pair(0.1,1.5));
intervals.push_back(std::make_pair(1.5,3));
intervals.push_back(std::make_pair(3,6));
intervals.push_back(std::make_pair(6,15));
intervals.push_back(std::make_pair(15,30));
intervals.push_back(std::make_pair(30,60));
intervals.push_back(std::make_pair(60,120));
intervals.push_back(std::make_pair(120,250));
intervals.push_back(std::make_pair(250,500));

int last = 10000;
for (int i =500, shift =250; i&amp;lt;last; i+=shift)
intervals.push_back(std::make_pair(i,i+shift));

intervals.push_back(std::make_pair(last,1e10));

std::vector&amp;lt;EigenValuesAndVectors*&amp;gt; eigvs;

int saved_neig = neig;
int tot_eigen_found= 0;
int i_eig_start = 0;
int i_eiv_start = 0;

size_t grp_shift = intervals.size()&amp;gt;1? thread::hardware_concurrency() / 4 :1;
size_t remaning_intervals = intervals.size();



for (auto grp_it = intervals.begin(); grp_it!= intervals.end() &amp;amp;&amp;amp; neig&amp;gt;0 &amp;amp;&amp;amp; grp_shift&amp;gt;0; grp_it+=grp_shift){
thread_group grp_thread;

for (auto it= grp_it; it!= grp_it+grp_shift; it++){

EigenValuesAndVectors *eigv = new EigenValuesAndVectorsFeast(matrix_a_,matrix_b_,*it,neig,m);
eigvs.push_back(eigv);
grp_thread.create_thread(bind(&amp;amp;EigenValuesAndVectors::Compute, eigv));

}

grp_thread.join_all();



for (auto it= eigvs.begin(); it!= eigvs.end() &amp;amp;&amp;amp; neig&amp;gt;0; it++){

EigenValuesAndVectors *eigv = *it;
neig_found = eigv-&amp;gt;GetNeigFound();
bool cond = neig_found&amp;lt;neig;

if ((*it)-&amp;gt;Found()){



#ifdef _DEBUG
int i_eig_start_bck = i_eig_start;
#endif
eigv-&amp;gt;FillExternalVectors(eig,eiv,i_eig_start,i_eiv_start, neig);
#ifdef _DEBUG
std::cout&amp;lt;&amp;lt;"found "&amp;lt;&amp;lt; (cond?neig_found:neig)&amp;lt;&amp;lt;" eigenvalue\\s within  interval:"&amp;lt;&amp;lt;eigv-&amp;gt;GetInterval().first&amp;lt;&amp;lt;"-"&amp;lt;&amp;lt;eigv-&amp;gt;GetInterval().second&amp;lt;&amp;lt;std::endl;
for (int i=i_eig_start_bck; i&amp;lt;i_eig_start ; i++)
std::cout&amp;lt;&amp;lt;eig[i]&amp;lt;&amp;lt;std::endl;
#endif

tot_eigen_found+=cond?neig_found:neig;
neig-=cond?neig_found:neig;
}

delete eigv;

}

eigvs.clear();

remaning_intervals-=grp_shift;
grp_shift = remaning_intervals&amp;gt;grp_shift? grp_shift:remaning_intervals;
}

neig_found= tot_eigen_found;



return true;
}
&lt;/LI-CODE&gt;&lt;P&gt;The classes used to compute the eigenvalues in each interval are the following:&lt;BR /&gt;&lt;BR /&gt;&lt;/P&gt;&lt;LI-CODE lang="markup"&gt;class EigenValuesAndVectors
{
public:
	EigenValuesAndVectors(std::shared_ptr&amp;lt;MklSymMatrix&amp;gt; A, std::shared_ptr&amp;lt;MklSymMatrix&amp;gt; B, const std::pair&amp;lt;double,double&amp;gt; &amp;amp;interval, int neig, int m):
	  A_(A),B_(B), interval_(interval)
	{
		mat_type_ = 'U';
		feastinit (fpm_);
		fpm_[1] = 3;	//number of contour points Ne = 8
		fpm_[3] = 2;	//refinement loops
		
		//fpm_[27] = 1;	//check positive definte

		epsout_ = 0.0;	//On output, contains the relative error on the trace: |tracei - tracei-1| /max(|emin|, |emax|)
		loop_ = 0;		//On output, contains the number of refinement loop executed. Ignored on input.
		
		m0_ = static_cast&amp;lt;int&amp;gt;(ceil(neig*1.5));  //Total number of eigenvalues to be computed
		

		leiv_ = new double [m0_ *m];
		leig_ = new double [m0_];
		res_ = new double[m0_];

		info_  = 0;
	}

	virtual void Compute()=0;

	virtual void FillExternalVectors(double *eig, double *eiv, int &amp;amp;i_eig_start, int &amp;amp;i_eiv_start, int &amp;amp;neig ) = 0;

	bool Found() const
	{
		return info_ == 0 || info_ == 3;  
	}

	int GetNeigFound() const
	{
		return neig_found_;
	}

	const double* GetEiv() const
	{
		return leiv_;
	}

	const double* GetEig() const
	{
		return leig_;
	}

	

	const std::pair&amp;lt;double, double&amp;gt; &amp;amp; GetInterval() const
	{
		return interval_;
	}
	

	~EigenValuesAndVectors()
	{
		delete [] leiv_;
		delete [] leig_;
		delete [] res_;
	}

protected:
	char mat_type_;
	MKL_INT fpm_[128]; //see https://software.intel.com/en-us/node/470386
	double epsout_;	//On output, contains the relative error on the trace: |tracei - tracei-1| /max(|emin|, |emax|)
	MKL_INT loop_;	//On output, contains the number of refinement loop executed. Ignored on input.
	MKL_INT m0_;	//Total number of eigenvalues to be computed

    const std::pair&amp;lt;double,double&amp;gt; &amp;amp;interval_;
	std::shared_ptr&amp;lt;MklSymMatrix&amp;gt; A_;
	std::shared_ptr&amp;lt;MklSymMatrix&amp;gt; B_;
	
	
	int neig_found_;
	
	double *leiv_;
	double *leig_;
	double *res_;

	MKL_INT info_;
	
	
};


class EigenValuesAndVectorsFeast: public EigenValuesAndVectors
{
public:
	EigenValuesAndVectorsFeast(std::shared_ptr&amp;lt;MklSymMatrix&amp;gt; A, std::shared_ptr&amp;lt;MklSymMatrix&amp;gt; B, const std::pair&amp;lt;double,double&amp;gt; &amp;amp;interval, int neig, int m):
	  EigenValuesAndVectors(A,B, interval,neig,m)
	 {}

	virtual void Compute()
	{
		double emin = 1/interval_.second;	//The lower bounds of the interval to be searched for eigenvalues; emin &amp;lt; emax.
		double emax = 1/interval_.first;	//The upper bounds of the interval to be searched for eigenvalues; emin &amp;lt; emax.
	
		int num_of_mat_row = A_-&amp;gt;N();

		std::lock_guard&amp;lt;std::mutex&amp;gt; lock(feast_mutex_);
		dfeast_scsrgv(&amp;amp;mat_type_, &amp;amp;num_of_mat_row, B_-&amp;gt;A(),B_-&amp;gt;Ia(),B_-&amp;gt;Ja(), A_-&amp;gt;A(),A_-&amp;gt;Ia(),A_-&amp;gt;Ja(),
			fpm_,&amp;amp;epsout_,&amp;amp;loop_,&amp;amp;emin,&amp;amp;emax,&amp;amp;m0_,leig_,leiv_,&amp;amp;neig_found_,res_,&amp;amp;info_);

	}

	virtual void FillExternalVectors(double *eig, double *eiv, int &amp;amp;i_eig_start, int &amp;amp;i_eiv_start, int &amp;amp;neig )
	{
		bool cond = neig_found_&amp;lt;neig;
		int exit_loop = cond?0:neig_found_-neig;

		for (int ie= neig_found_-1; ie&amp;gt;= exit_loop; ie--, i_eig_start++)
			eig[i_eig_start]= 1/leig_[ie];

		int m = A_-&amp;gt;N();
		for (int ie= (neig_found_-1)*m; ie&amp;gt;=exit_loop*m; ie-=m)
			for (int j = ie; j&amp;lt;ie+m; j++)
				eiv[i_eiv_start++]= leiv_[j];
	}

	
	protected:
		static std::mutex feast_mutex_;
};&lt;/LI-CODE&gt;&lt;P&gt;As mentioned, if I protect the call to dfeast_scsrgv with a global mutex,&amp;nbsp;the results become perfectly reproducible (same as the sequential version).&amp;nbsp;Without the mutex, the computed eigenvalues vary slightly between runs,even when mkl_set_num_threads(1) is used.&amp;nbsp;So it seems that dfeast_scsrgv is not fully thread-safe when called concurrently from different threads, even if the data and fpm arrays are distinct per thread.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Wed, 29 Oct 2025 10:36:17 GMT</pubDate>
      <guid>https://community.intel.com/t5/Intel-oneAPI-Math-Kernel-Library/Possible-non-thread-safe-behavior-in-dfeast-scsrgv-with-parallel/m-p/1724039#M37408</guid>
      <dc:creator>GiPe1978</dc:creator>
      <dc:date>2025-10-29T10:36:17Z</dc:date>
    </item>
    <item>
      <title>Re: Possible non-thread-safe behavior in dfeast_scsrgv with parallel interval search</title>
      <link>https://community.intel.com/t5/Intel-oneAPI-Math-Kernel-Library/Possible-non-thread-safe-behavior-in-dfeast-scsrgv-with-parallel/m-p/1724040#M37409</link>
      <description>&lt;P&gt;Hello Fengrui,&lt;/P&gt;&lt;P&gt;Thank you for your reply.&lt;/P&gt;&lt;P&gt;To clarify, I’m not using OpenMP.&lt;BR /&gt;The FEAST solvers (dfeast_scsrgv) are executed in parallel threads created manually using boost::thread_group (each thread computes the eigenvalues in a different interval).&lt;BR /&gt;Each thread constructs its own instance of EigenValuesAndVectorsFeast, which in turn calls dfeast_scsrgv.&lt;/P&gt;&lt;P&gt;Here is the relevant code section:&lt;BR /&gt;&lt;BR /&gt;&lt;/P&gt;&lt;LI-CODE lang="cpp"&gt;bool 
	MklSymMatrixSolver::EigenSystem(int neig, int m, double *eig, double *eiv, int &amp;amp;neig_found)
{
	if (!matrix_a_  &amp;amp;&amp;amp; !matrix_b_)
		return false;

	std::vector&amp;lt;std::pair&amp;lt;double,double&amp;gt;&amp;gt; intervals;

	
	
	
	intervals.push_back(std::make_pair(0.1,1.5));
	intervals.push_back(std::make_pair(1.5,3));
	intervals.push_back(std::make_pair(3,6));
	intervals.push_back(std::make_pair(6,15));
	intervals.push_back(std::make_pair(15,30));
	intervals.push_back(std::make_pair(30,60));
	intervals.push_back(std::make_pair(60,120));
	intervals.push_back(std::make_pair(120,250));
	intervals.push_back(std::make_pair(250,500));

	int last = 10000;
	for (int i =500, shift =250; i&amp;lt;last; i+=shift)
		intervals.push_back(std::make_pair(i,i+shift));

	intervals.push_back(std::make_pair(last,1e10));

	std::vector&amp;lt;EigenValuesAndVectors*&amp;gt; eigvs;
	
	int saved_neig = neig;
	int tot_eigen_found= 0;
	int i_eig_start = 0;
	int i_eiv_start = 0;
	
	size_t grp_shift = intervals.size()&amp;gt;1? thread::hardware_concurrency() / 4 :1;
	size_t remaning_intervals = intervals.size();

	

	for (auto grp_it = intervals.begin(); grp_it!= intervals.end() &amp;amp;&amp;amp; neig&amp;gt;0 &amp;amp;&amp;amp; grp_shift&amp;gt;0; grp_it+=grp_shift){
		thread_group grp_thread;

		for (auto it= grp_it; it!= grp_it+grp_shift; it++){

			EigenValuesAndVectors *eigv = new EigenValuesAndVectorsFeast(matrix_a_,matrix_b_,*it,neig,m);
			eigvs.push_back(eigv);
			grp_thread.create_thread(bind(&amp;amp;EigenValuesAndVectors::Compute, eigv));

		}

		grp_thread.join_all();

		

		for (auto it= eigvs.begin(); it!= eigvs.end() &amp;amp;&amp;amp; neig&amp;gt;0; it++){
	
			EigenValuesAndVectors *eigv = *it;
			neig_found = eigv-&amp;gt;GetNeigFound();
			bool cond = neig_found&amp;lt;neig;

			if ((*it)-&amp;gt;Found()){

				

				
#ifdef _DEBUG
				int i_eig_start_bck = i_eig_start;
#endif
				eigv-&amp;gt;FillExternalVectors(eig,eiv,i_eig_start,i_eiv_start, neig);
#ifdef _DEBUG
				std::cout&amp;lt;&amp;lt;"found "&amp;lt;&amp;lt; (cond?neig_found:neig)&amp;lt;&amp;lt;" eigenvalue\\s within  interval:"&amp;lt;&amp;lt;eigv-&amp;gt;GetInterval().first&amp;lt;&amp;lt;"-"&amp;lt;&amp;lt;eigv-&amp;gt;GetInterval().second&amp;lt;&amp;lt;std::endl;
				for (int i=i_eig_start_bck; i&amp;lt;i_eig_start ; i++)
					std::cout&amp;lt;&amp;lt;eig[i]&amp;lt;&amp;lt;std::endl;
#endif
			
				tot_eigen_found+=cond?neig_found:neig;
				neig-=cond?neig_found:neig;
			}

			delete eigv;

		}

		eigvs.clear();

		remaning_intervals-=grp_shift;
		grp_shift = remaning_intervals&amp;gt;grp_shift? grp_shift:remaning_intervals;
	}

	neig_found= tot_eigen_found;

	

	return true;
}&lt;/LI-CODE&gt;&lt;P&gt;&amp;nbsp;These are the classes used in the above code, to compute the eigenvalues in each interval:&lt;BR /&gt;&lt;BR /&gt;&lt;/P&gt;&lt;LI-CODE lang="cpp"&gt;class EigenValuesAndVectors
{
public:
	EigenValuesAndVectors(std::shared_ptr&amp;lt;MklSymMatrix&amp;gt; A, std::shared_ptr&amp;lt;MklSymMatrix&amp;gt; B, const std::pair&amp;lt;double,double&amp;gt; &amp;amp;interval, int neig, int m):
	  A_(A),B_(B), interval_(interval)
	{
		mat_type_ = 'U';
		feastinit (fpm_);
		fpm_[1] = 3;	//number of contour points Ne = 8
		fpm_[3] = 2;	//refinement loops
		
		//fpm_[27] = 1;	//check positive definte

		epsout_ = 0.0;	//On output, contains the relative error on the trace: |tracei - tracei-1| /max(|emin|, |emax|)
		loop_ = 0;		//On output, contains the number of refinement loop executed. Ignored on input.
		
		m0_ = static_cast&amp;lt;int&amp;gt;(ceil(neig*1.5));  //Total number of eigenvalues to be computed
		

		leiv_ = new double [m0_ *m];
		leig_ = new double [m0_];
		res_ = new double[m0_];

		info_  = 0;
	}

	virtual void Compute()=0;

	virtual void FillExternalVectors(double *eig, double *eiv, int &amp;amp;i_eig_start, int &amp;amp;i_eiv_start, int &amp;amp;neig ) = 0;

	bool Found() const
	{
		return info_ == 0 || info_ == 3;  
	}

	int GetNeigFound() const
	{
		return neig_found_;
	}

	const double* GetEiv() const
	{
		return leiv_;
	}

	const double* GetEig() const
	{
		return leig_;
	}

	

	const std::pair&amp;lt;double, double&amp;gt; &amp;amp; GetInterval() const
	{
		return interval_;
	}
	

	~EigenValuesAndVectors()
	{
		delete [] leiv_;
		delete [] leig_;
		delete [] res_;
	}

protected:
	char mat_type_;
	MKL_INT fpm_[128]; //see https://software.intel.com/en-us/node/470386
	double epsout_;	//On output, contains the relative error on the trace: |tracei - tracei-1| /max(|emin|, |emax|)
	MKL_INT loop_;	//On output, contains the number of refinement loop executed. Ignored on input.
	MKL_INT m0_;	//Total number of eigenvalues to be computed

    const std::pair&amp;lt;double,double&amp;gt; &amp;amp;interval_;
	std::shared_ptr&amp;lt;MklSymMatrix&amp;gt; A_;
	std::shared_ptr&amp;lt;MklSymMatrix&amp;gt; B_;
	
	
	int neig_found_;
	
	double *leiv_;
	double *leig_;
	double *res_;

	MKL_INT info_;
	
	
};


class EigenValuesAndVectorsFeast: public EigenValuesAndVectors
{
public:
	EigenValuesAndVectorsFeast(std::shared_ptr&amp;lt;MklSymMatrix&amp;gt; A, std::shared_ptr&amp;lt;MklSymMatrix&amp;gt; B, const std::pair&amp;lt;double,double&amp;gt; &amp;amp;interval, int neig, int m):
	  EigenValuesAndVectors(A,B, interval,neig,m)
	 {}

	virtual void Compute()
	{
		double emin = 1/interval_.second;	//The lower bounds of the interval to be searched for eigenvalues; emin &amp;lt; emax.
		double emax = 1/interval_.first;	//The upper bounds of the interval to be searched for eigenvalues; emin &amp;lt; emax.
	
		int num_of_mat_row = A_-&amp;gt;N();

		std::lock_guard&amp;lt;std::mutex&amp;gt; lock(feast_mutex_);
		dfeast_scsrgv(&amp;amp;mat_type_, &amp;amp;num_of_mat_row, B_-&amp;gt;A(),B_-&amp;gt;Ia(),B_-&amp;gt;Ja(), A_-&amp;gt;A(),A_-&amp;gt;Ia(),A_-&amp;gt;Ja(),
			fpm_,&amp;amp;epsout_,&amp;amp;loop_,&amp;amp;emin,&amp;amp;emax,&amp;amp;m0_,leig_,leiv_,&amp;amp;neig_found_,res_,&amp;amp;info_);

	}

	virtual void FillExternalVectors(double *eig, double *eiv, int &amp;amp;i_eig_start, int &amp;amp;i_eiv_start, int &amp;amp;neig )
	{
		bool cond = neig_found_&amp;lt;neig;
		int exit_loop = cond?0:neig_found_-neig;

		for (int ie= neig_found_-1; ie&amp;gt;= exit_loop; ie--, i_eig_start++)
			eig[i_eig_start]= 1/leig_[ie];

		int m = A_-&amp;gt;N();
		for (int ie= (neig_found_-1)*m; ie&amp;gt;=exit_loop*m; ie-=m)
			for (int j = ie; j&amp;lt;ie+m; j++)
				eiv[i_eiv_start++]= leiv_[j];
	}

	
	protected:
		static std::mutex feast_mutex_;
};&lt;/LI-CODE&gt;&lt;P&gt;&amp;nbsp;The results become perfectly reproducible (same as the sequential version).&lt;BR /&gt;Without the mutex, the computed eigenvalues vary slightly between runs.&lt;/P&gt;&lt;P&gt;So it seems that dfeast_scsrgv is not fully thread-safe when called concurrently from different threads, even if the data and fpm arrays are distinct per thread.&lt;BR /&gt;&lt;BR /&gt;Thanks in advance.&lt;/P&gt;</description>
      <pubDate>Wed, 29 Oct 2025 10:44:10 GMT</pubDate>
      <guid>https://community.intel.com/t5/Intel-oneAPI-Math-Kernel-Library/Possible-non-thread-safe-behavior-in-dfeast-scsrgv-with-parallel/m-p/1724040#M37409</guid>
      <dc:creator>GiPe1978</dc:creator>
      <dc:date>2025-10-29T10:44:10Z</dc:date>
    </item>
    <item>
      <title>Re: Possible non-thread-safe behavior in dfeast_scsrgv with parallel interval search</title>
      <link>https://community.intel.com/t5/Intel-oneAPI-Math-Kernel-Library/Possible-non-thread-safe-behavior-in-dfeast-scsrgv-with-parallel/m-p/1724048#M37411</link>
      <description>&lt;P&gt;Hello Fengrui,&lt;/P&gt;&lt;P&gt;Thank you for your reply.&lt;/P&gt;&lt;P&gt;To clarify, I’m not using OpenMP.&lt;BR /&gt;The FEAST solvers (dfeast_scsrgv) are executed in parallel threads created manually using boost::thread_group (each thread computes the eigenvalues in a different interval).&lt;BR /&gt;Each thread constructs its own instance of EigenValuesAndVectorsFeast, which in turn calls dfeast_scsrgv.&lt;/P&gt;&lt;P&gt;Here is the relevant code section:&lt;BR /&gt;&lt;BR /&gt;&lt;/P&gt;&lt;LI-CODE lang="cpp"&gt;bool 
	MklSymMatrixSolver::EigenSystem(int neig, int m, double *eig, double *eiv, int &amp;amp;neig_found)
{
	if (!matrix_a_  &amp;amp;&amp;amp; !matrix_b_)
		return false;

	std::vector&amp;lt;std::pair&amp;lt;double,double&amp;gt;&amp;gt; intervals;

	
		
	intervals.push_back(std::make_pair(0.1,1.5));
	intervals.push_back(std::make_pair(1.5,3));
	intervals.push_back(std::make_pair(3,6));
	intervals.push_back(std::make_pair(6,15));
	intervals.push_back(std::make_pair(15,30));
	intervals.push_back(std::make_pair(30,60));
	intervals.push_back(std::make_pair(60,120));
	intervals.push_back(std::make_pair(120,250));
	intervals.push_back(std::make_pair(250,500));

	int last = 10000;
	for (int i =500, shift =250; i&amp;lt;last; i+=shift)
		intervals.push_back(std::make_pair(i,i+shift));

	intervals.push_back(std::make_pair(last,1e10));

	std::vector&amp;lt;EigenValuesAndVectors*&amp;gt; eigvs;
	
	int saved_neig = neig;
	int tot_eigen_found= 0;
	int i_eig_start = 0;
	int i_eiv_start = 0;
	
	size_t grp_shift = intervals.size()&amp;gt;1? thread::hardware_concurrency() / 4 :1;
	size_t remaning_intervals = intervals.size();

	

	for (auto grp_it = intervals.begin(); grp_it!= intervals.end() &amp;amp;&amp;amp; neig&amp;gt;0 &amp;amp;&amp;amp; grp_shift&amp;gt;0; grp_it+=grp_shift){
		thread_group grp_thread;

		for (auto it= grp_it; it!= grp_it+grp_shift; it++){

			EigenValuesAndVectors *eigv = new EigenValuesAndVectorsFeast(matrix_a_,matrix_b_,*it,neig,m);
			eigvs.push_back(eigv);
			grp_thread.create_thread(bind(&amp;amp;EigenValuesAndVectors::Compute, eigv));

		}

		grp_thread.join_all();

		

		for (auto it= eigvs.begin(); it!= eigvs.end() &amp;amp;&amp;amp; neig&amp;gt;0; it++){
	
			EigenValuesAndVectors *eigv = *it;
			neig_found = eigv-&amp;gt;GetNeigFound();
			bool cond = neig_found&amp;lt;neig;

			if ((*it)-&amp;gt;Found()){
	


#ifdef _DEBUG
				int i_eig_start_bck = i_eig_start;
#endif
				eigv-&amp;gt;FillExternalVectors(eig,eiv,i_eig_start,i_eiv_start, neig);
#ifdef _DEBUG
				std::cout&amp;lt;&amp;lt;"found "&amp;lt;&amp;lt; (cond?neig_found:neig)&amp;lt;&amp;lt;" eigenvalue\\s within  interval:"&amp;lt;&amp;lt;eigv-&amp;gt;GetInterval().first&amp;lt;&amp;lt;"-"&amp;lt;&amp;lt;eigv-&amp;gt;GetInterval().second&amp;lt;&amp;lt;std::endl;
				for (int i=i_eig_start_bck; i&amp;lt;i_eig_start ; i++)
					std::cout&amp;lt;&amp;lt;eig[i]&amp;lt;&amp;lt;std::endl;
#endif
			
				tot_eigen_found+=cond?neig_found:neig;
				neig-=cond?neig_found:neig;
			}

			delete eigv;

		}

		eigvs.clear();

		remaning_intervals-=grp_shift;
		grp_shift = remaning_intervals&amp;gt;grp_shift? grp_shift:remaning_intervals;
	}

	neig_found= tot_eigen_found;

	

	return true;
}

class EigenValuesAndVectors
{
public:
	EigenValuesAndVectors(std::shared_ptr&amp;lt;MklSymMatrix&amp;gt; A, std::shared_ptr&amp;lt;MklSymMatrix&amp;gt; B, const std::pair&amp;lt;double,double&amp;gt; &amp;amp;interval, int neig, int m):
	  A_(A),B_(B), interval_(interval)
	{
		mat_type_ = 'U';
		feastinit (fpm_);
		fpm_[1] = 3;	//number of contour points Ne = 8
		fpm_[3] = 2;	//refinement loops
		
		//fpm_[27] = 1;	//check positive definte

		epsout_ = 0.0;	//On output, contains the relative error on the trace: |tracei - tracei-1| /max(|emin|, |emax|)
		loop_ = 0;		//On output, contains the number of refinement loop executed. Ignored on input.
		
		m0_ = static_cast&amp;lt;int&amp;gt;(ceil(neig*1.5));  //Total number of eigenvalues to be computed
		

		leiv_ = new double [m0_ *m];
		leig_ = new double [m0_];
		res_ = new double[m0_];

		info_  = 0;
	}

	virtual void Compute()=0;

	virtual void FillExternalVectors(double *eig, double *eiv, int &amp;amp;i_eig_start, int &amp;amp;i_eiv_start, int &amp;amp;neig ) = 0;

	bool Found() const
	{
		return info_ == 0 || info_ == 3;  
	}

	int GetNeigFound() const
	{
		return neig_found_;
	}

	const double* GetEiv() const
	{
		return leiv_;
	}

	const double* GetEig() const
	{
		return leig_;
	}

	

	const std::pair&amp;lt;double, double&amp;gt; &amp;amp; GetInterval() const
	{
		return interval_;
	}
	

	~EigenValuesAndVectors()
	{
		delete [] leiv_;
		delete [] leig_;
		delete [] res_;
	}

protected:
	char mat_type_;
	MKL_INT fpm_[128]; //see https://software.intel.com/en-us/node/470386
	double epsout_;	//On output, contains the relative error on the trace: |tracei - tracei-1| /max(|emin|, |emax|)
	MKL_INT loop_;	//On output, contains the number of refinement loop executed. Ignored on input.
	MKL_INT m0_;	//Total number of eigenvalues to be computed

    const std::pair&amp;lt;double,double&amp;gt; &amp;amp;interval_;
	std::shared_ptr&amp;lt;MklSymMatrix&amp;gt; A_;
	std::shared_ptr&amp;lt;MklSymMatrix&amp;gt; B_;
	
	
	int neig_found_;
	
	double *leiv_;
	double *leig_;
	double *res_;

	MKL_INT info_;
	
	
};


class EigenValuesAndVectorsFeast: public EigenValuesAndVectors
{
public:
	EigenValuesAndVectorsFeast(std::shared_ptr&amp;lt;MklSymMatrix&amp;gt; A, std::shared_ptr&amp;lt;MklSymMatrix&amp;gt; B, const std::pair&amp;lt;double,double&amp;gt; &amp;amp;interval, int neig, int m):
	  EigenValuesAndVectors(A,B, interval,neig,m)
	 {}

	virtual void Compute()
	{
		double emin = 1/interval_.second;	//The lower bounds of the interval to be searched for eigenvalues; emin &amp;lt; emax.
		double emax = 1/interval_.first;	//The upper bounds of the interval to be searched for eigenvalues; emin &amp;lt; emax.
	
		int num_of_mat_row = A_-&amp;gt;N();

		std::lock_guard&amp;lt;std::mutex&amp;gt; lock(feast_mutex_);
		dfeast_scsrgv(&amp;amp;mat_type_, &amp;amp;num_of_mat_row, B_-&amp;gt;A(),B_-&amp;gt;Ia(),B_-&amp;gt;Ja(), A_-&amp;gt;A(),A_-&amp;gt;Ia(),A_-&amp;gt;Ja(),
			fpm_,&amp;amp;epsout_,&amp;amp;loop_,&amp;amp;emin,&amp;amp;emax,&amp;amp;m0_,leig_,leiv_,&amp;amp;neig_found_,res_,&amp;amp;info_);

	}

	virtual void FillExternalVectors(double *eig, double *eiv, int &amp;amp;i_eig_start, int &amp;amp;i_eiv_start, int &amp;amp;neig )
	{
		bool cond = neig_found_&amp;lt;neig;
		int exit_loop = cond?0:neig_found_-neig;

		for (int ie= neig_found_-1; ie&amp;gt;= exit_loop; ie--, i_eig_start++)
			eig[i_eig_start]= 1/leig_[ie];

		int m = A_-&amp;gt;N();
		for (int ie= (neig_found_-1)*m; ie&amp;gt;=exit_loop*m; ie-=m)
			for (int j = ie; j&amp;lt;ie+m; j++)
				eiv[i_eiv_start++]= leiv_[j];
	}

	
	protected:
		static std::mutex feast_mutex_;
};&lt;/LI-CODE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;the results become perfectly reproducible (same as the sequential version).&lt;BR /&gt;Without the mutex, the computed eigenvalues vary slightly between runs.&lt;/P&gt;&lt;P&gt;So it seems that dfeast_scsrgv is not fully thread-safe when called concurrently from different threads, even if the data and fpm arrays are distinct per thread.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Wed, 29 Oct 2025 11:24:14 GMT</pubDate>
      <guid>https://community.intel.com/t5/Intel-oneAPI-Math-Kernel-Library/Possible-non-thread-safe-behavior-in-dfeast-scsrgv-with-parallel/m-p/1724048#M37411</guid>
      <dc:creator>GiPe1978</dc:creator>
      <dc:date>2025-10-29T11:24:14Z</dc:date>
    </item>
    <item>
      <title>Re: Possible non-thread-safe behavior in dfeast_scsrgv with parallel interval search</title>
      <link>https://community.intel.com/t5/Intel-oneAPI-Math-Kernel-Library/Possible-non-thread-safe-behavior-in-dfeast-scsrgv-with-parallel/m-p/1724065#M37412</link>
      <description>&lt;P&gt;Hello Fengrui,&lt;/P&gt;&lt;P&gt;Thank you for your reply.&lt;/P&gt;&lt;P&gt;To clarify, I’m not using OpenMP.&lt;BR /&gt;The FEAST solvers (dfeast_scsrgv) are executed in parallel threads created manually using boost::thread_group (each thread computes the eigenvalues in a different interval).&lt;BR /&gt;Each thread constructs its own instance of EigenValuesAndVectorsFeast, which in turn calls dfeast_scsrgv.&lt;/P&gt;&lt;P&gt;Here is the relevant code section:&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;LI-CODE lang="cpp"&gt;bool 
	MklSymMatrixSolver::EigenSystem(int neig, int m, double *eig, double *eiv, int &amp;amp;neig_found)
{
	if (!matrix_a_  &amp;amp;&amp;amp; !matrix_b_)
		return false;

	std::vector&amp;lt;std::pair&amp;lt;double,double&amp;gt;&amp;gt; intervals;
	
	intervals.push_back(std::make_pair(0.1,1.5));
	intervals.push_back(std::make_pair(1.5,3));
	intervals.push_back(std::make_pair(3,6));
	intervals.push_back(std::make_pair(6,15));
	intervals.push_back(std::make_pair(15,30));
	intervals.push_back(std::make_pair(30,60));
	intervals.push_back(std::make_pair(60,120));
	intervals.push_back(std::make_pair(120,250));
	intervals.push_back(std::make_pair(250,500));

	int last = 10000;
	for (int i =500, shift =250; i&amp;lt;last; i+=shift)
		intervals.push_back(std::make_pair(i,i+shift));

	intervals.push_back(std::make_pair(last,1e10));

	std::vector&amp;lt;EigenValuesAndVectors*&amp;gt; eigvs;
	
	int saved_neig = neig;
	int tot_eigen_found= 0;
	int i_eig_start = 0;
	int i_eiv_start = 0;
	
	size_t grp_shift = intervals.size()&amp;gt;1? thread::hardware_concurrency() / 4 :1;
	size_t remaning_intervals = intervals.size();

	

	for (auto grp_it = intervals.begin(); grp_it!= intervals.end() &amp;amp;&amp;amp; neig&amp;gt;0 &amp;amp;&amp;amp; grp_shift&amp;gt;0; grp_it+=grp_shift){
		thread_group grp_thread;

		for (auto it= grp_it; it!= grp_it+grp_shift; it++){

			EigenValuesAndVectors *eigv = new EigenValuesAndVectorsFeast(matrix_a_,matrix_b_,*it,neig,m);
			eigvs.push_back(eigv);
			grp_thread.create_thread(bind(&amp;amp;EigenValuesAndVectors::Compute, eigv));

		}

		grp_thread.join_all();

		

		for (auto it= eigvs.begin(); it!= eigvs.end() &amp;amp;&amp;amp; neig&amp;gt;0; it++){
	
			EigenValuesAndVectors *eigv = *it;
			neig_found = eigv-&amp;gt;GetNeigFound();
			bool cond = neig_found&amp;lt;neig;

			if ((*it)-&amp;gt;Found()){

				

				
#ifdef _DEBUG
				int i_eig_start_bck = i_eig_start;
#endif
				eigv-&amp;gt;FillExternalVectors(eig,eiv,i_eig_start,i_eiv_start, neig);
#ifdef _DEBUG
				std::cout&amp;lt;&amp;lt;"found "&amp;lt;&amp;lt; (cond?neig_found:neig)&amp;lt;&amp;lt;" eigenvalue\\s within  interval:"&amp;lt;&amp;lt;eigv-&amp;gt;GetInterval().first&amp;lt;&amp;lt;"-"&amp;lt;&amp;lt;eigv-&amp;gt;GetInterval().second&amp;lt;&amp;lt;std::endl;
				for (int i=i_eig_start_bck; i&amp;lt;i_eig_start ; i++)
					std::cout&amp;lt;&amp;lt;eig[i]&amp;lt;&amp;lt;std::endl;
#endif
			
				tot_eigen_found+=cond?neig_found:neig;
				neig-=cond?neig_found:neig;
			}

			delete eigv;

		}

		eigvs.clear();

		remaning_intervals-=grp_shift;
		grp_shift = remaning_intervals&amp;gt;grp_shift? grp_shift:remaning_intervals;
	}

	neig_found= tot_eigen_found;

	

	return true;
}

class EigenValuesAndVectors
{
public:
	EigenValuesAndVectors(std::shared_ptr&amp;lt;MklSymMatrix&amp;gt; A, std::shared_ptr&amp;lt;MklSymMatrix&amp;gt; B, const std::pair&amp;lt;double,double&amp;gt; &amp;amp;interval, int neig, int m):
	  A_(A),B_(B), interval_(interval)
	{
		mat_type_ = 'U';
		feastinit (fpm_);
		fpm_[1] = 3;	//number of contour points Ne = 8
		fpm_[3] = 2;	//refinement loops
		
		//fpm_[27] = 1;	//check positive definte

		epsout_ = 0.0;	//On output, contains the relative error on the trace: |tracei - tracei-1| /max(|emin|, |emax|)
		loop_ = 0;		//On output, contains the number of refinement loop executed. Ignored on input.
		
		m0_ = static_cast&amp;lt;int&amp;gt;(ceil(neig*1.5));  //Total number of eigenvalues to be computed
		

		leiv_ = new double [m0_ *m];
		leig_ = new double [m0_];
		res_ = new double[m0_];

		info_  = 0;
	}

	virtual void Compute()=0;

	virtual void FillExternalVectors(double *eig, double *eiv, int &amp;amp;i_eig_start, int &amp;amp;i_eiv_start, int &amp;amp;neig ) = 0;

	bool Found() const
	{
		return info_ == 0 || info_ == 3;  
	}

	int GetNeigFound() const
	{
		return neig_found_;
	}

	const double* GetEiv() const
	{
		return leiv_;
	}

	const double* GetEig() const
	{
		return leig_;
	}

	

	const std::pair&amp;lt;double, double&amp;gt; &amp;amp; GetInterval() const
	{
		return interval_;
	}
	

	~EigenValuesAndVectors()
	{
		delete [] leiv_;
		delete [] leig_;
		delete [] res_;
	}

protected:
	char mat_type_;
	MKL_INT fpm_[128]; //see https://software.intel.com/en-us/node/470386
	double epsout_;	//On output, contains the relative error on the trace: |tracei - tracei-1| /max(|emin|, |emax|)
	MKL_INT loop_;	//On output, contains the number of refinement loop executed. Ignored on input.
	MKL_INT m0_;	//Total number of eigenvalues to be computed

    const std::pair&amp;lt;double,double&amp;gt; &amp;amp;interval_;
	std::shared_ptr&amp;lt;MklSymMatrix&amp;gt; A_;
	std::shared_ptr&amp;lt;MklSymMatrix&amp;gt; B_;
	
	
	int neig_found_;
	
	double *leiv_;
	double *leig_;
	double *res_;

	MKL_INT info_;
	
	
};


class EigenValuesAndVectorsFeast: public EigenValuesAndVectors
{
public:
	EigenValuesAndVectorsFeast(std::shared_ptr&amp;lt;MklSymMatrix&amp;gt; A, std::shared_ptr&amp;lt;MklSymMatrix&amp;gt; B, const std::pair&amp;lt;double,double&amp;gt; &amp;amp;interval, int neig, int m):
	  EigenValuesAndVectors(A,B, interval,neig,m)
	 {}

	virtual void Compute()
	{
		double emin = 1/interval_.second;	//The lower bounds of the interval to be searched for eigenvalues; emin &amp;lt; emax.
		double emax = 1/interval_.first;	//The upper bounds of the interval to be searched for eigenvalues; emin &amp;lt; emax.
	
		int num_of_mat_row = A_-&amp;gt;N();

		std::lock_guard&amp;lt;std::mutex&amp;gt; lock(feast_mutex_);
		dfeast_scsrgv(&amp;amp;mat_type_, &amp;amp;num_of_mat_row, B_-&amp;gt;A(),B_-&amp;gt;Ia(),B_-&amp;gt;Ja(), A_-&amp;gt;A(),A_-&amp;gt;Ia(),A_-&amp;gt;Ja(),
			fpm_,&amp;amp;epsout_,&amp;amp;loop_,&amp;amp;emin,&amp;amp;emax,&amp;amp;m0_,leig_,leiv_,&amp;amp;neig_found_,res_,&amp;amp;info_);

	}

	virtual void FillExternalVectors(double *eig, double *eiv, int &amp;amp;i_eig_start, int &amp;amp;i_eiv_start, int &amp;amp;neig )
	{
		bool cond = neig_found_&amp;lt;neig;
		int exit_loop = cond?0:neig_found_-neig;

		for (int ie= neig_found_-1; ie&amp;gt;= exit_loop; ie--, i_eig_start++)
			eig[i_eig_start]= 1/leig_[ie];

		int m = A_-&amp;gt;N();
		for (int ie= (neig_found_-1)*m; ie&amp;gt;=exit_loop*m; ie-=m)
			for (int j = ie; j&amp;lt;ie+m; j++)
				eiv[i_eiv_start++]= leiv_[j];
	}

	
	protected:
		static std::mutex feast_mutex_;
};
&lt;/LI-CODE&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;the results become perfectly reproducible (same as the sequential version).&lt;BR /&gt;Without the mutex, the computed eigenvalues vary slightly between runs.&lt;/P&gt;&lt;P&gt;So it seems that dfeast_scsrgv is not fully thread-safe when called concurrently from different threads, even if the data and fpm arrays are distinct per thread.&lt;/P&gt;</description>
      <pubDate>Wed, 29 Oct 2025 15:08:00 GMT</pubDate>
      <guid>https://community.intel.com/t5/Intel-oneAPI-Math-Kernel-Library/Possible-non-thread-safe-behavior-in-dfeast-scsrgv-with-parallel/m-p/1724065#M37412</guid>
      <dc:creator>GiPe1978</dc:creator>
      <dc:date>2025-10-29T15:08:00Z</dc:date>
    </item>
    <item>
      <title>Re: Possible non-thread-safe behavior in dfeast_scsrgv with parallel interval search</title>
      <link>https://community.intel.com/t5/Intel-oneAPI-Math-Kernel-Library/Possible-non-thread-safe-behavior-in-dfeast-scsrgv-with-parallel/m-p/1724231#M37416</link>
      <description>&lt;DIV class=""&gt;&lt;DIV class=""&gt;&lt;P class=""&gt;Hello Fengrui,&lt;/P&gt;&lt;P class=""&gt;Thank you for your reply.&lt;/P&gt;&lt;P class=""&gt;To clarify, I’m not using OpenMP.&lt;BR /&gt;The FEAST solvers (dfeast_scsrgv) are executed in parallel threads created manually using boost::thread_group (each thread computes the eigenvalues in a different interval).&lt;BR /&gt;Each thread constructs its own instance of EigenValuesAndVectorsFeast, which in turn calls dfeast_scsrgv.&lt;/P&gt;&lt;P class=""&gt;Here is the relevant code section:&lt;/P&gt;&lt;P class=""&gt;&amp;nbsp;&lt;/P&gt;&lt;PRE&gt;bool 
	MklSymMatrixSolver::EigenSystem(int neig, int m, double *eig, double *eiv, int &amp;amp;neig_found)
{
	if (!matrix_a_  &amp;amp;&amp;amp; !matrix_b_)
		return false;

	std::vector&amp;lt;std::pair&amp;lt;double,double&amp;gt;&amp;gt; intervals;
	
	intervals.push_back(std::make_pair(0.1,1.5));
	intervals.push_back(std::make_pair(1.5,3));
	intervals.push_back(std::make_pair(3,6));
	intervals.push_back(std::make_pair(6,15));
	intervals.push_back(std::make_pair(15,30));
	intervals.push_back(std::make_pair(30,60));
	intervals.push_back(std::make_pair(60,120));
	intervals.push_back(std::make_pair(120,250));
	intervals.push_back(std::make_pair(250,500));

	int last = 10000;
	for (int i =500, shift =250; i&amp;lt;last; i+=shift)
		intervals.push_back(std::make_pair(i,i+shift));

	intervals.push_back(std::make_pair(last,1e10));

	std::vector&amp;lt;EigenValuesAndVectors*&amp;gt; eigvs;
	
	int saved_neig = neig;
	int tot_eigen_found= 0;
	int i_eig_start = 0;
	int i_eiv_start = 0;
	
	size_t grp_shift = intervals.size()&amp;gt;1? thread::hardware_concurrency() / 4 :1;
	size_t remaning_intervals = intervals.size();

	

	for (auto grp_it = intervals.begin(); grp_it!= intervals.end() &amp;amp;&amp;amp; neig&amp;gt;0 &amp;amp;&amp;amp; grp_shift&amp;gt;0; grp_it+=grp_shift){
		thread_group grp_thread;

		for (auto it= grp_it; it!= grp_it+grp_shift; it++){

			EigenValuesAndVectors *eigv = new EigenValuesAndVectorsFeast(matrix_a_,matrix_b_,*it,neig,m);
			eigvs.push_back(eigv);
			grp_thread.create_thread(bind(&amp;amp;EigenValuesAndVectors::Compute, eigv));

		}

		grp_thread.join_all();

		

		for (auto it= eigvs.begin(); it!= eigvs.end() &amp;amp;&amp;amp; neig&amp;gt;0; it++){
	
			EigenValuesAndVectors *eigv = *it;
			neig_found = eigv-&amp;gt;GetNeigFound();
			bool cond = neig_found&amp;lt;neig;

			if ((*it)-&amp;gt;Found()){

				

				
#ifdef _DEBUG
				int i_eig_start_bck = i_eig_start;
#endif
				eigv-&amp;gt;FillExternalVectors(eig,eiv,i_eig_start,i_eiv_start, neig);
#ifdef _DEBUG
				std::cout&amp;lt;&amp;lt;"found "&amp;lt;&amp;lt; (cond?neig_found:neig)&amp;lt;&amp;lt;" eigenvalue\\s within  interval:"&amp;lt;&amp;lt;eigv-&amp;gt;GetInterval().first&amp;lt;&amp;lt;"-"&amp;lt;&amp;lt;eigv-&amp;gt;GetInterval().second&amp;lt;&amp;lt;std::endl;
				for (int i=i_eig_start_bck; i&amp;lt;i_eig_start ; i++)
					std::cout&amp;lt;&amp;lt;eig[i]&amp;lt;&amp;lt;std::endl;
#endif
			
				tot_eigen_found+=cond?neig_found:neig;
				neig-=cond?neig_found:neig;
			}

			delete eigv;

		}

		eigvs.clear();

		remaning_intervals-=grp_shift;
		grp_shift = remaning_intervals&amp;gt;grp_shift? grp_shift:remaning_intervals;
	}

	neig_found= tot_eigen_found;

	

	return true;
}

class EigenValuesAndVectors
{
public:
	EigenValuesAndVectors(std::shared_ptr&amp;lt;MklSymMatrix&amp;gt; A, std::shared_ptr&amp;lt;MklSymMatrix&amp;gt; B, const std::pair&amp;lt;double,double&amp;gt; &amp;amp;interval, int neig, int m):
	  A_(A),B_(B), interval_(interval)
	{
		mat_type_ = 'U';
		feastinit (fpm_);
		fpm_[1] = 3;	//number of contour points Ne = 8
		fpm_[3] = 2;	//refinement loops
		
		//fpm_[27] = 1;	//check positive definte

		epsout_ = 0.0;	//On output, contains the relative error on the trace: |tracei - tracei-1| /max(|emin|, |emax|)
		loop_ = 0;		//On output, contains the number of refinement loop executed. Ignored on input.
		
		m0_ = static_cast&amp;lt;int&amp;gt;(ceil(neig*1.5));  //Total number of eigenvalues to be computed
		

		leiv_ = new double [m0_ *m];
		leig_ = new double [m0_];
		res_ = new double[m0_];

		info_  = 0;
	}

	virtual void Compute()=0;

	virtual void FillExternalVectors(double *eig, double *eiv, int &amp;amp;i_eig_start, int &amp;amp;i_eiv_start, int &amp;amp;neig ) = 0;

	bool Found() const
	{
		return info_ == 0 || info_ == 3;  
	}

	int GetNeigFound() const
	{
		return neig_found_;
	}

	const double* GetEiv() const
	{
		return leiv_;
	}

	const double* GetEig() const
	{
		return leig_;
	}

	

	const std::pair&amp;lt;double, double&amp;gt; &amp;amp; GetInterval() const
	{
		return interval_;
	}
	

	~EigenValuesAndVectors()
	{
		delete [] leiv_;
		delete [] leig_;
		delete [] res_;
	}

protected:
	char mat_type_;
	MKL_INT fpm_[128]; //see https://software.intel.com/en-us/node/470386
	double epsout_;	//On output, contains the relative error on the trace: |tracei - tracei-1| /max(|emin|, |emax|)
	MKL_INT loop_;	//On output, contains the number of refinement loop executed. Ignored on input.
	MKL_INT m0_;	//Total number of eigenvalues to be computed

    const std::pair&amp;lt;double,double&amp;gt; &amp;amp;interval_;
	std::shared_ptr&amp;lt;MklSymMatrix&amp;gt; A_;
	std::shared_ptr&amp;lt;MklSymMatrix&amp;gt; B_;
	
	
	int neig_found_;
	
	double *leiv_;
	double *leig_;
	double *res_;

	MKL_INT info_;
	
	
};


class EigenValuesAndVectorsFeast: public EigenValuesAndVectors
{
public:
	EigenValuesAndVectorsFeast(std::shared_ptr&amp;lt;MklSymMatrix&amp;gt; A, std::shared_ptr&amp;lt;MklSymMatrix&amp;gt; B, const std::pair&amp;lt;double,double&amp;gt; &amp;amp;interval, int neig, int m):
	  EigenValuesAndVectors(A,B, interval,neig,m)
	 {}

	virtual void Compute()
	{
		double emin = 1/interval_.second;	//The lower bounds of the interval to be searched for eigenvalues; emin &amp;lt; emax.
		double emax = 1/interval_.first;	//The upper bounds of the interval to be searched for eigenvalues; emin &amp;lt; emax.
	
		int num_of_mat_row = A_-&amp;gt;N();

		std::lock_guard&amp;lt;std::mutex&amp;gt; lock(feast_mutex_);
		dfeast_scsrgv(&amp;amp;mat_type_, &amp;amp;num_of_mat_row, B_-&amp;gt;A(),B_-&amp;gt;Ia(),B_-&amp;gt;Ja(), A_-&amp;gt;A(),A_-&amp;gt;Ia(),A_-&amp;gt;Ja(),
			fpm_,&amp;amp;epsout_,&amp;amp;loop_,&amp;amp;emin,&amp;amp;emax,&amp;amp;m0_,leig_,leiv_,&amp;amp;neig_found_,res_,&amp;amp;info_);

	}

	virtual void FillExternalVectors(double *eig, double *eiv, int &amp;amp;i_eig_start, int &amp;amp;i_eiv_start, int &amp;amp;neig )
	{
		bool cond = neig_found_&amp;lt;neig;
		int exit_loop = cond?0:neig_found_-neig;

		for (int ie= neig_found_-1; ie&amp;gt;= exit_loop; ie--, i_eig_start++)
			eig[i_eig_start]= 1/leig_[ie];

		int m = A_-&amp;gt;N();
		for (int ie= (neig_found_-1)*m; ie&amp;gt;=exit_loop*m; ie-=m)
			for (int j = ie; j&amp;lt;ie+m; j++)
				eiv[i_eiv_start++]= leiv_[j];
	}

	
	protected:
		static std::mutex feast_mutex_;
};&lt;/PRE&gt;&lt;P class=""&gt;&amp;nbsp;&lt;/P&gt;&lt;P class=""&gt;the results become perfectly reproducible (same as the sequential version).&lt;BR /&gt;Without the mutex, the computed eigenvalues vary slightly between runs.&lt;/P&gt;&lt;P class=""&gt;So it seems that dfeast_scsrgv is not fully thread-safe when called concurrently from different threads, even if the data and fpm arrays are distinct per thread.&lt;/P&gt;&lt;/DIV&gt;&lt;/DIV&gt;&lt;DIV class=""&gt;&amp;nbsp;&lt;/DIV&gt;</description>
      <pubDate>Thu, 30 Oct 2025 17:34:43 GMT</pubDate>
      <guid>https://community.intel.com/t5/Intel-oneAPI-Math-Kernel-Library/Possible-non-thread-safe-behavior-in-dfeast-scsrgv-with-parallel/m-p/1724231#M37416</guid>
      <dc:creator>GiPe1978</dc:creator>
      <dc:date>2025-10-30T17:34:43Z</dc:date>
    </item>
    <item>
      <title>Re: Possible non-thread-safe behavior in dfeast_scsrgv with parallel interval search</title>
      <link>https://community.intel.com/t5/Intel-oneAPI-Math-Kernel-Library/Possible-non-thread-safe-behavior-in-dfeast-scsrgv-with-parallel/m-p/1724232#M37417</link>
      <description>&lt;P&gt;Hello Fengrui,&lt;/P&gt;&lt;P&gt;Thank you for your reply.&lt;/P&gt;&lt;P&gt;To clarify, I’m &lt;STRONG&gt;not using OpenMP&lt;/STRONG&gt;.&lt;BR /&gt;The FEAST solvers (dfeast_scsrgv) are executed in &lt;STRONG&gt;parallel threads&lt;/STRONG&gt; created manually using boost::thread_group (each thread computes the eigenvalues in a different interval).&lt;BR /&gt;Each thread constructs its own instance of EigenValuesAndVectorsFeast, which in turn calls dfeast_scsrgv.&lt;/P&gt;&lt;P&gt;Attached you'll finde the relevant code section.&lt;/P&gt;&lt;P&gt;The results become perfectly reproducible (same as the sequential version).&lt;BR /&gt;Without the mutex, the computed eigenvalues vary slightly between runs — even when mkl_set_num_threads(1) is used.&lt;/P&gt;&lt;P&gt;So it seems that dfeast_scsrgv is not fully thread-safe when called concurrently from different threads, even if the data and fpm arrays are distinct per thread.&lt;/P&gt;&lt;P&gt;&amp;nbsp;&lt;/P&gt;</description>
      <pubDate>Thu, 30 Oct 2025 17:47:33 GMT</pubDate>
      <guid>https://community.intel.com/t5/Intel-oneAPI-Math-Kernel-Library/Possible-non-thread-safe-behavior-in-dfeast-scsrgv-with-parallel/m-p/1724232#M37417</guid>
      <dc:creator>GiPe1978</dc:creator>
      <dc:date>2025-10-30T17:47:33Z</dc:date>
    </item>
  </channel>
</rss>

