На главную страницу

Решения к упражнениям из книги "Язык программирования С" Б. Керниган и Д. Ритчи

Упражнение 3.1.


В нашем двоичном поиске каждый цикл содержит две проверки, тогда как достаточно было бы одной (зато взамен их потребовалось бы больше снаружи цикла). Напишите версию функции с одной проверкой внутри цикла и сравните быстродействие (затраты времени).

Комментарий: Большинство современных процессоров имеют конвейерную архитектуру. В центральных процессорах, используемых в универсальных ЭВМ, например, AMD и Intel, количество стадий конвейера больше 10. Это означает, что одновременно процессор выполняет несколько команд в разных стадиях. Причем часто входные данные текущей команды зависят от выходных данных предыдущей команды. Например:
a = 1
b = a + 1
c = b + 2
d = c + 3
e = d + 4
f = e + 5
Для процессора такой ход вычисления, когда последовательность самих вычислений постоянна, не представляет труда. Однако все меняется, когда возникают условия. Например:
a = *вводиться пользователем*
b = a + 1
b = 2 ?
да:
с = b + 2
d = c + 3
e = d + 4
f = e + 5
нет:
c = b + 2000
d = c + 3000
e = d + 4000
f = e + 5000
Какую ветку выполнения ("да" или "нет") процессор должен поместить в конвейер - зависит от результата b. Однако, пока результат b не известен, конвейер-то все-равно работает и помещать туда что-то надо. Поэтому процессор выполняет предсказание относительно условия b = 2?. Если предсказание верно, то все вычисления в конвейере идут дальше. Если предсказание не верно, то необходимо сбросить все результаты в начале конвейера (вычисление d, e, f), а это лишние потери времени. Поэтому очень важно выполнять правильные предсказание относительно истинности условия.
Именно поэтому в этой программе гораздо лучше вынести условие за пределы цикла, чтобы предсказание выполнялось 1 раз вместо N раз, что теоретически должно привести к более быстрым вычислениям, т. к. ошибок предсказания станет меньше.

Комментарий2: функция clock() возвращает время от начала выполнения программы в тиках процессора. Это время предоставляется операционной системой с шагом 1000 (верно для Linux и Windows). То есть, значения могут быть 0, 1000, 2000, .... i * 1000. И не могут быть, например, 528.
Функции binsearch выполняются очень быстро - так быстро, что время выполнения не длится более 1000 тиков, поэтому clock() возвращает ноль.
----------
/* Exerecise 3.1 from "The C programming language" book by K&R */

#include <stdio.h>
#include <time.h>
#define N 10000		/* size of massive */

/* search x in v[0] <= v[1] <= ... <= v[n-1]. function from book */
int binsearch(int x, int v[], int n)
{
	int low, high, mid;
	low = 0;
	high = n - 1;
	while (low <= high) {
		mid = (low+high) / 2;
		if (x < v[mid])
			high = mid - 1;
		else if (x > v[mid])
			low = mid + 1;
		else
			return mid;
	}
	return -1;
}

/* search x in v[0] <= v[1] <= ... <= v[n-1]. function by me */
int binsearch2(int x, int v[], int n)
{
	int low, high, mid;
	low = 0;
	high = n - 1;
	mid = (low+high) / 2;
	while (low <= high && x != v[mid]) {
		if (x < v[mid])
			high = mid - 1;
		else
			low = mid + 1;
		mid = (low+high) / 2;
	}
	if (x == v[mid])
		return mid;
	else
		return -1;	
}

int main(void)
{
	int mas[N];
	int i;
	clock_t clock1;
	clock_t clock2;

	for (i = 0; i < N; i++)		/* fill in initial massive */
		mas[i] = i;

	clock1 = clock();
	binsearch(1, mas, N);
	clock2 = clock();
	printf("binsearch() time = %lu\n", (unsigned long int)(clock2-clock1));

	clock1 = clock();
	binsearch2(1, mas, N);
	clock2 = clock();
	printf("binsearch2() time = %lu\n", (unsigned long int)(clock2-clock1));
	return 0;
}
	
----------

Последнее изменение: 22 май 2011