这一章我觉得问题还是比较多的,也不是特别理解CPE的计算。
如果对题目有什么其他见解,欢迎大家一起讨论。
5.15
A.
关键路径是%xmm1更新路径上的加法。
B. CPE下界是浮点加法的延迟。
C. 两个load操作的吞吐量界限。(我觉得是2.00)
D. 因为乘法不在关键路径上,乘法也是流水线执行的,其限制因素为吞吐量界限。整个程序的限制因素为最后的浮点数加法的延迟,这个延迟对float和double都是3.00。
书上之前说关键路径,现在其实可以再仔细分析一下(以下属于个人分析):
把执行指令写出了就明了了。
以整数为例:
相同底色表示这些指令在一个循环内执行,以及同一个循环内的初始值:
所以我觉得整数的时候CPE是2.00,可是为什么是3.00呢?
浮点数的话,延迟是3.00没问题。
时间线 | xmm1_add单元 | xmm1 | mul单元发射 | load单元发射 | rdx_add单元 | rdx的值 |
1 | | 0 | | load1 | add | +0 |
2 | | 0 | | load2 | | +1 |
3 | | | | load1 | add | +1 |
4 | | | | load2 | | +2 |
5 | | | | load1 | add | +2 |
6 | | | mul (load延迟4) | load2 | ... | |
7 | | | | ... | | |
8 | | 0 | mul | | | |
9 | add 整数mul 延迟为3 | 0 | | | | |
10 | | added 整数加法 延迟为1 | mul | | | |
11 | add | | | | | |
12 | | added | | | | |
5.16
void inner4(vec_ptr u, vec_ptr v, data_t *dest)
{
long int i;
int length = vec_length(u);
data_t *udata = get_vec_start(u);
data_t *vdata = get_vec_start(v);
data_t sum = (data_t) 0;
int limit = length - 2;
for (i = 0; i < limit; i++) {
sum = sum + udata[i] * vdata[i];
sum = sum + udata[i+1] * vdata[i+1];
sum = sum + udata[i+2] * vdata[i+2];
}
for(; i<length; ++i)
sum = sum + udata[i] * vdata[i];
*dest = sum;
}
{
long int i;
int length = vec_length(u);
data_t *udata = get_vec_start(u);
data_t *vdata = get_vec_start(v);
data_t sum = (data_t) 0;
int limit = length - 2;
for (i = 0; i < limit; i++) {
sum = sum + udata[i] * vdata[i];
sum = sum + udata[i+1] * vdata[i+1];
sum = sum + udata[i+2] * vdata[i+2];
}
for(; i<length; ++i)
sum = sum + udata[i] * vdata[i];
*dest = sum;
}
A. 因为load吞吐量为1.00,每计算一个值需要两次load,所以CPE不可能低于2.00。
B. 关键路径上仍然有N个浮点加法,所以循环展开并没有改变
5.17
A. load执行单元的吞吐量
B. IA32可用寄存器实际只有6个,而三路展开需要i, limit, udata, vdata,以及存储udata[i], vdata[i]的寄存器,所以肯定有些循环变量会溢出到寄存器,这会影响效率。(至于为什么是2.67以及为什么四路展开的整数会是2.33,还不是很清楚)。
5.18
void inner4(vec_ptr u, vec_ptr v, data_t *dest)
{
long int i;
int length = vec_length(u);
data_t *udata = get_vec_start(u);
data_t *vdata = get_vec_start(v);
data_t sum = (data_t) 0;
int limit = length - 2;
for (i = 0; i < limit; i++) {
int x1 = udata[i] * vdata[i];
int x2 = udata[i+1] * vdata[i+1];
int x3 = udata[i+2] * vdata[i+2];
sum = sum + (x1 + x2 + x3);
}
for(; i<length; ++i)
sum = sum + udata[i] * vdata[i];
*dest = sum;
}
{
long int i;
int length = vec_length(u);
data_t *udata = get_vec_start(u);
data_t *vdata = get_vec_start(v);
data_t sum = (data_t) 0;
int limit = length - 2;
for (i = 0; i < limit; i++) {
int x1 = udata[i] * vdata[i];
int x2 = udata[i+1] * vdata[i+1];
int x3 = udata[i+2] * vdata[i+2];
sum = sum + (x1 + x2 + x3);
}
for(; i<length; ++i)
sum = sum + udata[i] * vdata[i];
*dest = sum;
}
5.19
void *optimized_memset(void *s, int c, size_t n)
{
unsigned int K = sizeof(unsigned long);
unsigned char *schar = (unsigned char*)s;
unsigned long *lchar;
unsigned long fill = 0;
int i = 0;
for(i = 0; i < K; i++)
fill += (c&0xff) << (i<<3);
// n如果是个负数,会变成一个很大的正数,这应该不需要处理吧?
// size_t应该是unsigned int,n应该不可能是
//一般K都是2的整数次幂,也可以用schar&(K-1)来求schar%K
while((unsigned)schar%K && n)
{
*schar++ = (unsigned char)c;
n--;
}
lchar = (unsigned long*) schar;
while ( n >= K ) {
*lchar++ = fill;
n -= K; //不知道这里如果用++和--会不会影响整体的效率
}
schar = (unsigned char*) lchar;
while(n) //剩余的n
{
*schar++ = (unsigned char)c;
--n;
}
return s;
}
{
unsigned int K = sizeof(unsigned long);
unsigned char *schar = (unsigned char*)s;
unsigned long *lchar;
unsigned long fill = 0;
int i = 0;
for(i = 0; i < K; i++)
fill += (c&0xff) << (i<<3);
// n如果是个负数,会变成一个很大的正数,这应该不需要处理吧?
// size_t应该是unsigned int,n应该不可能是
//一般K都是2的整数次幂,也可以用schar&(K-1)来求schar%K
while((unsigned)schar%K && n)
{
*schar++ = (unsigned char)c;
n--;
}
lchar = (unsigned long*) schar;
while ( n >= K ) {
*lchar++ = fill;
n -= K; //不知道这里如果用++和--会不会影响整体的效率
}
schar = (unsigned char*) lchar;
while(n) //剩余的n
{
*schar++ = (unsigned char)c;
--n;
}
return s;
}
5.20
double poly_optimized(double a[], double x, int degree)
{
long int i;
double result = 0;
double s = 0, powx4 = 1;
double x2 = x*x;
double x4 = x2*x2;
long int limit = degree-3;
for(i = 0; i <= limit; i += 4)
{
double v1 = a[i] + a[i+1]*x;
double v2 = a[i+2] + a[i+3]*x;
v1 = v1 + v2 * x2;
s = s + v1 * powx4;
powx4 *= x4;
}
for(; i <= degree; ++i)
{
s += a[i]*powx4;
powx4 *= x;
}
return s;
}
{
long int i;
double result = 0;
double s = 0, powx4 = 1;
double x2 = x*x;
double x4 = x2*x2;
long int limit = degree-3;
for(i = 0; i <= limit; i += 4)
{
double v1 = a[i] + a[i+1]*x;
double v2 = a[i+2] + a[i+3]*x;
v1 = v1 + v2 * x2;
s = s + v1 * powx4;
powx4 *= x4;
}
for(; i <= degree; ++i)
{
s += a[i]*powx4;
powx4 *= x;
}
return s;
}
关键路径就是一个浮点数乘法,因此CPE是浮点乘法延迟的1/4,然而每次计算都需要load 4个值,所以CPE还是1.00。
5.21
void psum(float a[], float p[], long int n)
{
long int i;
int v = 0;
for(i=0; i<n-1; i+=2)
{
int v1 = a[i];
int v2 = a[i+1];
v2 = v1 + v2;
{
long int i;
int v = 0;
for(i=0; i<n-1; i+=2)
{
int v1 = a[i];
int v2 = a[i+1];
v2 = v1 + v2;
p[i] = v + v1;
p[i+1] = v + v2;
p[i+1] = v + v2;
v = v + v2;
}
for(; i<n; i++)
{
v = v + a[i];
p[i] = v;
}
}
}
for(; i<n; i++)
{
v = v + a[i];
p[i] = v;
}
}
5.22
假设最开始需要100T的时间,那么A需要20T,B需要30T,C需要50T。
将B提到3倍,也就是B需要10T,那么总时间为80T。
将C提到1.5倍,也就是C需要33.3T,那么总时间为83.3T。
所以提高B会使得性能更优。