- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
下面提到的程序使用顺序、CPU 并行(使用 OpenMP)和 GPU 并行(Cuda)方法计算 vector - vector 点积。以下代码段显示了如何调用这些函数中的每一个以及如何计算耗时。
#define SEQUENTIAL "-s"
#define PARALLEL "-p"
#define CUDA "-c"
#define VERIFY "-v"
#define TEST_AND_COMPARE "-t"
#define GET_TIME(x); if (clock_gettime(CLOCK_MONOTONIC, &(x)) < 0) { perror("clock_gettime( ):");exit(EXIT_FAILURE);}
int main(int argc, char **argv) {
struct timespec t1, t2, t3, t4;
unsigned long sec, nsec;
float comp_time;
//invoking the sequential version
if (!strcmp(argv[1], SEQUENTIAL)) {
GET_TIME(t1);
sequentialVersion();
GET_TIME(t2);
comp_time = elapsed_time_msec(&t1, &t2, &sec, &nsec);
printf("N=%d: Time(ms)=%.5f \n", N, comp_time);
}
//invoking the parallel version
else if (!strcmp(argv[1], PARALLEL)) {
noOfThreads = atoi(argv[2]);
GET_TIME(t1);
parallelVersion();
GET_TIME(t2);
comp_time = elapsed_time_msec(&t1, &t2, &sec, &nsec);
printf("N=%d: Threads=%d: Time(ms)=%.5f \n", N, noOfThreads,
comp_time);
}
//the cuda invoke goes here...
//comparing the answers received by each method of calculation
else if (!strcmp(argv[1], TEST_AND_COMPARE)) {
precision answer1, answer2, answer3;
GET_TIME(t1);
answer1 = sequentialVersion();
GET_TIME(t2);
comp_time = elapsed_time_msec(&t1, &t2, &sec, &nsec);
printf("%-10s\tN=%d: Ans=%f: Time(ms)=%.5f \n", "Serial", N, answer1, comp_time);
noOfThreads = atoi(argv[2]);
GET_TIME(t3);
answer2 = parallelVersion();
GET_TIME(t4);
comp_time = elapsed_time_msec(&t3, &t4, &sec, &nsec);
printf("%-10s\tN=%d: Ans=%f: Time(ms)=%.5f Threads=%d \n", "Parallel", N, answer2, comp_time, noOfThreads);
}
}
float elapsed_time_msec(struct timespec *begin, struct timespec *end,
unsigned long *sec, unsigned long *nsec) {
if (end->tv_nsec < begin->tv_nsec) {
*nsec = 1000000000 - (begin->tv_nsec - end->tv_nsec);
*sec = end->tv_sec - begin->tv_sec - 1;
} else {
*nsec = end->tv_nsec - begin->tv_nsec;
*sec = end->tv_sec - begin->tv_sec;
}
return (float) (*sec) * 1000 + ((float) (*nsec)) / 1000000;
}
上述程序的Makefile
如下。
#specifying single or double precision
ifeq ($(double),)
precision=
else
precision=-D USE_DOUBLES
endif
#specifying the problem size
ifeq ($(N),)
problem-size=-D PROBLEM_SIZE=1000000
else
problem-size=-D PROBLEM_SIZE=${N}
endif
dot:
nvcc dot-product.cu -arch compute_11 -Xcompiler -fopenmp -O3 $(problem-size) $(precision) -o prog
代码编译为make dot
,默认N,运行./prog -s
输出显示为
`N=1000000: Time(ms)=0.00010`
但使用相同的 N,当程序使用 ./prog -t 6
运行时,串行时间消耗显示预期行为,如下所示
Serial N=1000000: Ans=2249052.500000: Time(ms)=2.19174
Parallel N=1000000: Ans=2248955.500000: Time(ms)=0.53915 Threads=6
Cuda N=1000000: Ans=2248959.750000: Time(ms)=0.09935
为什么会这样?
最佳答案
虽然最好提供完整的代码,但我相信对运行 SEQUENTIAL
测试 (-s
) 与相同时的时间差异的解释TEST_AND_COMPARE
案例 (-t
) 中的函数是由于您在每种情况下调用 sequentialVersion()
函数的方式,以及以下事实您指定了积极的编译器优化 (-O3
)。
这是一个有效的测试用例,它展示了大致相同的行为差异:
$ cat t1017.cu
#include <time.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#define N 1000000
#define SEQUENTIAL "-s"
#define PARALLEL "-p"
#define CUDA "-c"
#define VERIFY "-v"
#define TEST_AND_COMPARE "-t"
#define GET_TIME(x); if (clock_gettime(CLOCK_MONOTONIC, &(x)) < 0) { perror("clock_gettime( ):");exit(EXIT_FAILURE);}
typedef float precision;
precision sequentialVersion() {precision retval = 0.0f; for (int i=0; i<N; i++) retval += (precision)i; return retval; }
precision parallelVersion() {sleep(1); return 0.0f;};
float elapsed_time_msec(struct timespec *begin, struct timespec *end,
unsigned long *sec, unsigned long *nsec) {
if (end->tv_nsec < begin->tv_nsec) {
*nsec = 1000000000 - (begin->tv_nsec - end->tv_nsec);
*sec = end->tv_sec - begin->tv_sec - 1;
} else {
*nsec = end->tv_nsec - begin->tv_nsec;
*sec = end->tv_sec - begin->tv_sec;
}
return (float) (*sec) * 1000 + ((float) (*nsec)) / 1000000;
}
int main(int argc, char **argv) {
struct timespec t1, t2, t3, t4;
unsigned long sec, nsec;
float comp_time;
int noOfThreads;
//invoking the sequential version
if (!strcmp(argv[1], SEQUENTIAL)) {
GET_TIME(t1);
sequentialVersion();
GET_TIME(t2);
comp_time = elapsed_time_msec(&t1, &t2, &sec, &nsec);
printf("N=%d: Time(ms)=%.5f \n", N, comp_time);
}
//invoking the parallel version
else if (!strcmp(argv[1], PARALLEL)) {
noOfThreads = atoi(argv[2]);
GET_TIME(t1);
parallelVersion();
GET_TIME(t2);
comp_time = elapsed_time_msec(&t1, &t2, &sec, &nsec);
printf("N=%d: Threads=%d: Time(ms)=%.5f \n", N, noOfThreads,
comp_time);
}
//the cuda invoke goes here...
//comparing the answers received by each method of calculation
else if (!strcmp(argv[1], TEST_AND_COMPARE)) {
precision answer1, answer2, answer3;
GET_TIME(t1);
answer1 = sequentialVersion();
GET_TIME(t2);
comp_time = elapsed_time_msec(&t1, &t2, &sec, &nsec);
printf("%-10s\tN=%d: Ans=%f: Time(ms)=%.5f \n", "Serial", N, answer1, comp_time);
noOfThreads = atoi(argv[2]);
GET_TIME(t3);
answer2 = parallelVersion();
GET_TIME(t4);
comp_time = elapsed_time_msec(&t3, &t4, &sec, &nsec);
printf("%-10s\tN=%d: Ans=%f: Time(ms)=%.5f Threads=%d \n", "Parallel", N, answer2, comp_time, noOfThreads);
}
}
$ nvcc -o t1017 t1017.cu
$ ./t1017 -s
N=1000000: Time(ms)=3.61435
$ nvcc -O3 -o t1017 t1017.cu
$ ./t1017 -s
N=1000000: Time(ms)=0.00068
$ ./t1017 -t 6
Serial N=1000000: Ans=499940360192.000000: Time(ms)=1.40843
Parallel N=1000000: Ans=0.000000: Time(ms)=1000.16150 Threads=6
$
请注意,在未指定优化的情况下编译代码时,-s
情况下的时间为几毫秒。当我们在-O3
情况下编译时,在-s
测试中时间大约为零,但在-t
中仍然是几毫秒> 测试。
要解决此问题,只需将 sequentialVersion()
函数的返回值分配(不要忽略)到您使用的变量即可。而不是这个:
sequentialVersion();
这样做:
precision temp = sequentialVersion();
您可能还想稍后打印或以其他方式“使用”temp
值。通过这样做,编译器无法优化顺序代码。
如前所述,此问题与 CUDA 无关。您可以采用我展示的代码,将其放在 .cpp 文件而不是 .cu 文件中,并使用 g++ 而不是 nvcc 进行编译,并见证相同的特性。由于代码中没有设备代码,nvcc 会简单地将它交给主机编译器。
宏免责声明:
虽然使用您展示的特定计时宏可能存在风格问题和/或危险,但我相信:
由于我不相信特定的宏会影响这个特定的问题,所以我选择保持原样,以证明问题可以在不修改计时宏的情况下得到解决(在这个测试用例中)。当我选择进行基于主机的计时时,我通常使用普通函数,例如我演示过的here。 .如果您对与该特定宏相关的问题有疑问,您可能希望将其作为一个单独的问题提出。我认为不需要用 cuda
标记。
关于c - 串行代码花费的时间不同,因为它是单独运行或与并行代码一起运行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34559916/
我一直在读一本分配给类(class)的书,它提到数组访问需要 O(1) 时间。我意识到这非常快(也许尽可能快),但是如果您有一个循环必须多次引用它,那么分配一个临时变量以在数组中查找值有什么好处吗?或
我一直试图找出为什么这个查询花了这么长时间。以前,它的执行时间约为 150 毫秒到 200 毫秒,但现在需要 25 秒或更长时间。这是从昨晚到今天之间的事。唯一改变的就是将数据添加到表中。 根据下面的
我有一个 ng repeat 重复数据。 - data.image(src)部分为null,src=null的不再重复。 我用一个简单的 ng-if 解决了它。
我有一个包含大量测试的 Laravel 项目。我正在使用 pcov 来计算代码覆盖率,大约需要 4 分钟。但是 pcov 不支持分支覆盖,所以我决定使用 xdebug。 使用 xdebug 测试执行,
我已经被这个问题困扰了一段时间了,我被难住了。 Automapper 需要 4 秒来映射 19 个对象。在我的机器(24GB 内存,3.6Ghz i7)上,该操作应该花费毫秒或纳秒。 这是映射调用。
我有一个包含大量测试的 Laravel 项目。我正在使用 pcov 来计算代码覆盖率,大约需要 4 分钟。但是 pcov 不支持分支覆盖,所以我决定使用 xdebug。 使用 xdebug 测试执行,
我在机器 A 上有一个 java 进程通过 TCP 与机器 B 上的 Tomcat 通信。 TCP 连接(只是 syn-syn/ack 交换)始终需要 100 毫秒的数量级,而 ping 请求需要 1
我做了一项任务,从 sqlserver 获取超过 200 万条记录并将它们填充到 Asp.net GridView 中。 问题是,查询需要超过 2 分钟才能获得记录,而我的查询现在已经完全优化。 当我
我希望将 165 秒变成 2:40 而不是 0:2:45 函数需要能够适应秒值的大小。 我知道有无数种方法可以做到这一点,但我正在寻找一种干净的方法来做到这一点,除了 jQuery 之外没有任何外部库
我是一名优秀的程序员,十分优秀!