C 提高置换检验p值矩阵构造的性能
我使用了一个R代码,它实现了一个置换测试,用于两个总体函数之间的分布比较。我们有p个单变量p值 瓶颈是构建一个包含所有可能的连续p值的矩阵。C 提高置换检验p值矩阵构造的性能,c,r,performance,C,R,Performance,我使用了一个R代码,它实现了一个置换测试,用于两个总体函数之间的分布比较。我们有p个单变量p值 瓶颈是构建一个包含所有可能的连续p值的矩阵。 p值矩阵的最后一行包含所有单变量p值。 倒数第二行按以下顺序包含所有二元p值: p_val_c(1,2),p_val_c(2,3),…,p_val_c(p,1) … 第一行的元素是重合的,关联的值是全局测试p_val_c(1,…,p)=p_val_c(2,…,p,1)=…=pval(p,1,…,p-1)的p值 由于计算方面的原因,我决定用c实现这个组件,并
p值矩阵的最后一行包含所有单变量p值。
倒数第二行按以下顺序包含所有二元p值:
p_val_c(1,2),p_val_c(2,3),…,p_val_c(p,1) …
第一行的元素是重合的,关联的值是全局测试p_val_c(1,…,p)=p_val_c(2,…,p,1)=…=pval(p,1,…,p-1)的p值 由于计算方面的原因,我决定用c实现这个组件,并在R和.c中使用它 这是代码。唯一重要的部分是函数Build_pval_asymm_矩阵的定义
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <time.h>
void Build_pval_asymm_matrix(int * p, int * B, double * pval,
double * L,
double * pval_asymm_matrix);
// Function used for the sorting of vector T_temp with qsort
int cmp(const void *x, const void *y);
int main() {
int B = 1000; // number Conditional Monte Carlo (CMC) runs
int p = 100; // number univariate tests
// Generate fictitiously data univariate p-values pval and matrix L.
// The j-th column of L is the empirical survival
// function of the statistics test associated to the j-th coefficient
// of the basis expansion. The dimension of L is B * p.
// Generate pval
double pval[p];
memset(pval, 0, sizeof(pval)); // initialize all elements to 0
for (int i = 0; i < p; i++) {
pval[i] = (double)rand() / (double)RAND_MAX;
}
// Construct L
double L[B * p];
// Inizialize to 0 the elements of L
memset(L, 0, sizeof(L));
// Array used to construct the columns of L
double temp_array[B];
memset(temp_array, 0, sizeof(temp_array));
for(int i = 0; i < B; i++) {
temp_array[i] = (double) (i + 1) / (double) B;
}
for (int iter_coeff=0; iter_coeff < p; iter_coeff++) {
// Shuffle temp_array
if (B > 1) {
for (int k = 0; k < B - 1; k++)
{
int j = rand() % B;
double t = temp_array[j];
temp_array[j] = temp_array[k];
temp_array[k] = t;
}
}
for (int i=0; i<B; i++) {
L[iter_coeff + p * i] = temp_array[i];
}
}
double pval_asymm_matrix[p * p];
memset(pval_asymm_matrix, 0, sizeof(pval_asymm_matrix));
// Construct the asymmetric matrix of p-values
clock_t start, end;
double cpu_time_used;
start = clock();
Build_pval_asymm_matrix(&p, &B, pval, L, pval_asymm_matrix);
end = clock();
cpu_time_used = ((double) (end - start)) / CLOCKS_PER_SEC;
printf("TOTAL CPU time used: %f\n", cpu_time_used);
return 0;
}
void Build_pval_asymm_matrix(int * p, int * B, double * pval,
double * L,
double * pval_asymm_matrix) {
int nbasis = *p, iter_CMC = *B;
// Scalar output fisher combining function applied on univariate
// p-values
double T0_temp = 0;
// Vector output fisher combining function applied on a set of
//columns of L
double T_temp[iter_CMC];
memset(T_temp, 0, sizeof(T_temp));
// Counter for elements of T_temp greater than or equal to T0_temp
int count = 0;
// Indexes for columns of L
int inf = 0, sup = 0;
// The last row of matrice_pval_asymm contains the univariate p-values
for(int i = 0; i < nbasis; i++) {
pval_asymm_matrix[i + nbasis * (nbasis - 1)] = pval[i];
}
// Construct the rows from bottom to up
for (int row = nbasis - 2; row >= 0; row--) {
for (int col = 0; col <= row; col++) {
T0_temp = 0;
memset(T_temp, 0, sizeof(T_temp));
inf = col;
sup = (nbasis - row) + col - 1;
// Combining function Fisher applied on
// p-values pval[inf:sup]
for (int k = inf; k <= sup; k++) {
T0_temp += log(pval[k]);
}
T0_temp *= -2;
// Combining function Fisher applied
// on columns inf:sup of matrix L
for (int k = 0; k < iter_CMC; k++) {
for (int l = inf; l <= sup; l++) {
T_temp[k] += log(L[l + nbasis * k]);
}
T_temp[k] *= -2;
}
// Sort the vector T_temp
qsort(T_temp, iter_CMC, sizeof(double), cmp);
// Count the number of elements of T_temp less than T0_temp
int h = 0;
while (h < iter_CMC && T_temp[h] < T0_temp) {
h++;
}
// Number of elements of T_temp greater than or equal to T0_temp
count = iter_CMC - h;
pval_asymm_matrix[col + nbasis * row] = (double) count / (double)iter_CMC;
}
// auxiliary variable for columns of L inf:nbasis-1 and 1:sup
int aux_first = 0, aux_second = 0;
int num_col_needed = 0;
for (int col = row + 1; col < nbasis; col++) {
T0_temp = 0;
memset(T_temp, 0, sizeof(T_temp));
inf = col;
sup = ((nbasis - row) + col) % nbasis - 1;
// Useful indexes
num_col_needed = nbasis - inf + sup + 1;
int index_needed[num_col_needed];
memset(index_needed, -1, num_col_needed * sizeof(int));
aux_first = inf;
for (int i = 0; i < nbasis - inf; i++) {
index_needed[i] = aux_first;
aux_first++;
}
aux_second = 0;
for (int j = 0; j < sup + 1; j++) {
index_needed[j + nbasis - inf] = aux_second;
aux_second++;
}
// Combining function Fisher applied on p-values
// pval[inf:p-1] and pval[0:sup-1]1]
for (int k = 0; k < num_col_needed; k++) {
T0_temp += log(pval[index_needed[k]]);
}
T0_temp *= -2;
// Combining function Fisher applied on columns inf:p-1 and 0:sup-1
// of matrix L
for (int k = 0; k < iter_CMC; k++) {
for (int l = 0; l < num_col_needed; l++) {
T_temp[k] += log(L[index_needed[l] + nbasis * k]);
}
T_temp[k] *= -2;
}
// Sort the vector T_temp
qsort(T_temp, iter_CMC, sizeof(double), cmp);
// Count the number of elements of T_temp less than T0_temp
int h = 0;
while (h < iter_CMC && T_temp[h] < T0_temp) {
h++;
}
// Number of elements of T_temp greater than or equal to T0_temp
count = iter_CMC - h;
pval_asymm_matrix[col + nbasis * row] = (double) count / (double)iter_CMC;
} // end for over col from row + 1 to nbasis - 1
} // end for over rows of asymm p-values matrix except the last row
}
int cmp(const void *x, const void *y)
{
double xx = *(double*)x, yy = *(double*)y;
if (xx < yy) return -1;
if (xx > yy) return 1;
return 0;
}
#包括
#包括
#包括
#包括
#包括
无效构建矩阵(int*p,int*B,double*pval,
双*L,
双*pval_asymm_矩阵);
//用于使用qsort对向量T_temp进行排序的函数
int cmp(常数无效*x,常数无效*y);
int main(){
int B=1000;//条件蒙特卡罗(CMC)运行次数
int p=100;//单变量测试数
//生成一元p值pval和矩阵L的虚拟数据。
//L的第j列是经验存活率
//与第j系数相关的统计检验的功能
//L的维数是B*p。
//生成pval
双pval[p];
memset(pval,0,sizeof(pval));//将所有元素初始化为0
对于(int i=0;i1){
对于(int k=0;k 对于(int col=0;colI将输入大小减少到
p
到50以避免等待(不要有这么快的机器)--保持p
不变并将B
减少到100具有类似的效果,但分析表明,用于计算的~8秒中,约7.5秒用于log
函数
qsort
甚至不是一个真正的热点。这项测试在微观效率方面似乎比其他任何测试都更具挑战性
因此,除非您的编译器的log
实现速度比我快得多,否则我的第一个建议是,如果您能够承受一定的精度损失,那么就要找到一个快速的日志实现(有一些编译器可以在大约3%的精度损失范围内,以一个数量级的速度计算日志)
如果您无法获得精度损失,并且精度非常关键,那么我建议您尽可能记住您用于log
的值,并将其存储到查找表中
更新
我尝试了后一种方法
// Create a memoized table of log values.
double log_cache[B * p];
for (int j=0, num=B*p; j < num; ++j)
log_cache[j] = log(L[j]);
有了这些:
T_temp[k] += log_cache[l + nbasis * k];
...
T_temp[k] += log_cache[index_needed[l] + nbasis * k];
这将我的时间从~8秒提高到~5.3秒,但我们将log
的计算开销换成了内存开销,而内存开销并没有那么好(事实上,这种情况很少发生,但调用log
进行双精度浮点显然非常昂贵,足以让这种交换变得值得).下一次迭代,如果你想要更高的速度,而且很有可能,需要研究缓存效率
对于这种巨大的矩阵,专注于内存布局和访问模式会产生奇迹。我已经预先计算了L元素和pval元素的日志值。对我来说,增益非常大!!p=100和B=1000大约从R函数的90秒到C函数的3-9秒。你为我节省了很多时间!!非常感谢!干杯!很高兴to帮个忙。如果你能找到一个真正的缓存友好型内存访问模式,你也可能会很快疯掉。这有点难,但我们可以做得更多。我觉得有可能在一秒钟内完成。不幸的是,这些问题的算法细节和数学对我来说有点陌生我不知道该如何重新构造数据或与之相关的访问模式。但你想要的是让机器以连续的方式访问内存,并避免不必要地将相同部分的内存重新加载到缓存线中。如果你能管理它,它将给你带来更大的潜力比日志优化带来的好处要大得多…可能有点过头了。我喜欢用分析器对这些东西进行调优和调优。但您可能会感兴趣的一个主题是矩阵的循环平铺。这可能与此相关。现在概念已经清楚了。在这个网站上,我发现了关于缓存友好型实现的高质量讨论为了改进我的解决方案,这可能很有用。谢谢
T_temp[k] += log_cache[l + nbasis * k];
...
T_temp[k] += log_cache[index_needed[l] + nbasis * k];