Prelude/Context:我刚刚开始学习c ++,并决定编写一些代码,将单个量子比特门应用于量子寄存器,其中寄存器保存在称为振幅的数组中,单个量子比特门的四个元素是a,b,c,d。我试图编写一个版本,避免在我第一次通过时出现的 if 语句,令我最初高兴的是,它似乎有轻微的性能增强 (~10%(。如果我更改寄存器中的量子比特数量或我用门瞄准的量子比特,我会得到类似的结果。然后,我尝试制作一个循环,对各种目标量子比特进行时序比较,发生了一些非常奇怪的事情(至少对我来说(。我编写的避免 if 语句的替代函数将其执行时间加倍(从 ~0.23 秒到 0.46 秒(,而带有 if 语句的函数的执行时间不受影响(~0.25 秒(。这让我想到了我的问题:
在这两种情况下,当给定相同的输入时,代码如何在迭代这些输入的循环中执行更长的时间?
例如,如果我运行一个给出 25 个量子位和目标量子比特 1 的测试,则"no if"函数获胜。然后,如果我编写一个 while 循环来对从 1 开始的每个目标值在 25 个量子位处进行比较,那么"no if"函数即使在第一次迭代中收到与前一种情况相同的输入时,也需要两倍的时间来执行。有趣的是,如果我只包含 while 循环并通过在 while 语句中输入"True"或注释掉增量语句 target+=1 来使其成为无限 while 循环,则该函数不再需要双倍时间。这种现象需要循环和增量,据我所知。
下面的代码,以防这是我不太熟悉的新语言的简单编码错误。我使用的是具有所有默认设置的Visual Studio 2017社区版,除了我使用"发布"版本以加快代码执行速度。注释掉 while 语句和相应的右大括号会使"no if"计时加倍。
#include "stdafx.h"
#include <iostream>
#include <time.h>
#include <complex>
void matmulpnoif(std::complex<float> arr[], std::complex<float> out[], int numqbits, std::complex<float> a,
std::complex<float> b, std::complex<float> c, std::complex<float> d, int target)
{
long length = 1 << (numqbits);
long offset = 1 << (target - 1);
long state = 0;
while (state < length)
{
out[state] = arr[state] * a + arr[state + offset] * b;
out[state + offset] = arr[state] * c + arr[state + offset] * d;
state += 1 + offset * (((state%offset) + 1) / offset);
}
}
void matmulpsingle(std::complex<float> arr[], std::complex<float> out[], int numqbits, std::complex<float> a,
std::complex<float> b, std::complex<float> c, std::complex<float> d, int target)
{
long length = 1 << (numqbits);
int shift = target - 1;
long offset = 1 << shift;
for (long state = 0; state < length; ++state)
{
if ((state >> shift) & 1)
{
out[state] = arr[state - offset] * c + arr[state] * d;
}
else
{
out[state] = arr[state] * a + arr[state + offset] * b;
}
}
}
int main()
{
using namespace std;
int numqbits = 25;
long arraylength = 1 << numqbits;
complex<float>* amplitudes = new complex<float>[arraylength];
for (long i = 0; i < arraylength; ++i)
{
amplitudes[i] = complex<float>(0., 0.);
}
amplitudes[0] = complex<float>(1., 0.);
complex<float> a(0., 0.);
complex<float> b(1., 0.);
complex<float> c(0., 0.);
complex<float> d(1., 0.);
int target = 1;
int repititions = 10;
clock_t startTime;
//while (target <= numqbits) {
startTime = clock();
for (int j = 0; j < repititions; ++j) {
complex<float>* outputs = new complex<float>[arraylength];
matmulpsingle(amplitudes, outputs, numqbits, a, b, c, d, target);
delete[] outputs;
}
cout << float(clock() - startTime) / (float)(CLOCKS_PER_SEC*repititions) << " seconds." << endl;
startTime = clock();
for (int k = 0; k < repititions; ++k) {
complex<float>* outputs = new complex<float>[arraylength];
matmulpnoif(amplitudes, outputs, numqbits, a, b, c, d, target);
delete[] outputs;
}
cout << float(clock() - startTime) / (float)(CLOCKS_PER_SEC*repititions) << " seconds." << endl;
target+=1;
//}
delete[] amplitudes;
return 0;
}
不幸的是,我还不能发表评论,所以我会在这里发布这个,即使它可能不是一个完整的答案。
一般来说,你提出的问题很困难。编译器执行优化,这两种情况是不同的代码,因此它们的优化方式不同。
例如,在我的机器上(Linux,GCC 7.3.1(,仅启用-O3,matmulpnoif
总是更快(4.8s vs 2.4s 或 4.8s vs 4.2s - 这些时间不是用clock()
测量的,这取决于循环是否存在(。如果我不得不猜测在这种情况下会发生什么,编译器可能会意识到offset
总是 1,并优化其余操作(除法是迄今为止您在那里最昂贵的操作(。但是,它也可能是其他事情的组合。
另一件需要注意的事情是,clock()
不应该用来测量时间。它计算时钟周期的数量,例如,如果您在 2 个线程中并行化代码,则数量将是时间的两倍(假设您的代码没有在任何地方等待 - 在我的机器上似乎并非如此(。如果你想测量时间,我建议你看看<chrono>
,high_resolution_clock
应该可以解决问题。
另一个旁注,没有必要继续分配和解除分配输出数组,您可以简单地使用一个,这样您将浪费更少的时间。但最重要的是,如果你正在使用C++我建议你把所有这些放在一个类中,因为它是你向每个函数传递许多参数,如果你传递大量数据(因为它被复制(,它会使事情变得难以阅读和变慢。
第二点需要注意的是,由于您使用的是位移,因此使用无符号变量可能更安全,因为右移位>>
没有严格定义它与有符号变量填充的内容。至少要记住的是,它可能是在那一侧填充 1。