Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

LINUX / KOZLOV2_1

.pdf
Скачиваний:
86
Добавлен:
27.03.2016
Размер:
4.3 Mб
Скачать

36: инициализация генератора случайных чисел (функция srand) системным временем

(time(NULL));

38:решение игрока А в текущей партии;

39:решение игрока Б в текущей партии;

41:очки игрока А;

42:очки игрока Б;

44:играть GAMES_AMN партий;

46:игрок А принимает решение;

47:игрок Б принимает решение;

49:если верно, что (вызов функции did_i_win) победил игрок А;

51:присвоить А одно очко;

52:перейти к следующей партии;

55:если верно, что (вызов функции did_i_win) победил игрок Б;

57:присвоить Б одно очко;

58:перейти к следующей партии;

62:вывести общее число партий;

64:вывести число побед игрока А;

65:вывести число побед игрока Б.

Прежде чем начать профилирование, необходимо скомпилировать и скомпоновать программу со специальной опцией, которая укажет исполняемому коду генерировать информацию для gprof, в случае gcc опция -pg:

$ g++ -pg main.cpp -o prog

main.cpp - имя файла с исходным кодом;

-o prog - указывает имя выходного (в данном случае исполняемого) файла. Затем следует запустить программу и дождаться успешного завершения: $ ./prog

Total games: 1000000 Score:

Player A: 250628 Player B: 249688

В этой серии удачливей оказался игрок А. Теперь, если выполнить команду

$ ls

gmon.out main.cpp prog

211

PDF created with pdfFactory Pro trial version www.pdffactory.com

Появится gmon.out - файл с информацией для gprof. В этом файле собрана статистика, анализ которой даст информацию для принятия решения об оптимизации. Для того чтобы ознакомиться с данной статистикой запустим gprof с опциями -b (без вывода легенды) и - no-graph (не выводить граф, который показывает откуда вызываются отдельные функции). Таким образом, мы получим «простой профиль»:

$ gprof -b -no-graph prog

Flat profile:

Each sample counts as 0.01 seconds.

%

cumulative

self

self

 

total

 

 

 

Time

seconds

 

seconds

calls

 

ns/call ns/call name

56.25

0.04

 

0.04

 

 

 

 

[1]

25.00

0.06

 

0.02

2000000

10.00

12.50

[2]

12.50

0.07

 

0.01

1749372

5.72

5.72

[3]

6.25

0.08

0.01

2000002

2.50

2.50

[4]

 

...

 

 

 

 

 

 

 

 

По причине ограниченной ширины печатного листа в последнем столбце полные сигнатуры функций заменены на условные обозначения, раскроем их:

[1]: main

[2]: make_decision()

[3]: did_i_win(std::pair<bool, bool> const&, std::pair<bool, bool> const&) [4]: std::pair<bool, bool>::pair()

Вотображаемой информации приводятся следующие столбцы:

% time - процент от общего времени исполнения программы, затраченный на выполнение этой функции. Сумма по всем строкам должна составлять 100%;

cumulative seconds - общее время, в секундах, затраченное на выполнение этой функции, плюс время, затраченное на выполнение всех функций, перечисленных выше в таблице;

self seconds - количество секунд затраченных на выполнение только этой функции;

calls - общее количество вызовов функции. Если функция ни разу не вызывалась или количество вызовов не может быть определено, то поле остается пустым;

self ns call - среднее количество наносекунд, затраченных этой функцией на вызов, если функция профилируется, иначе - поле остается пустым;

212

PDF created with pdfFactory Pro trial version www.pdffactory.com

total ns call - среднее количество наносекунд, затраченных этой функцией и ее подпрограммами на вызов, если эта функция профилируется, иначе - поле остается пустым;

name - имя функции.

Дадим объяснение относительно количества вызовов отдельных функций. Простой профиль сообщает о том, что больше всего времени тратится в функции main. Так как проводится 1000000 партий, в каждой из которых отдельный игрок принимает по одному решению, получается всего 2000000 вызовов функции make_decision.

Разберемся, почему было произведено именно 1749372 вызовов did_i_win. Если бы всегда выигрывал игрок Б или были одни ничьи, или оба игрока делали верные предположения, то потребовалось бы 2000000 вызовов did_i_win. 250007 раз победил игрок А, значит, в циклах, соответствующих его победе, второго вызова did_i_win для проверки победы игрока Б не производилось. Получаем: 2000000-250628=1749372.

Было произведено 2000002 вызова конструктора std::pair: 2000000 в рамках make_decision и 2 в рамках main.

Таким образом, мы показали, что gprof правильно подсчитала количество вызовов отдельных функций.

Вернемся к главной цели запуска профилировщика, а именно, установим, какие функции занимают основную часть времени выполнения. Согласно «простому профилю» возможность оптимизации функций следует рассматривать в следующем порядке: main, make_decision, did_i_win.

13.3. Контроль динамической памяти

Язык C++ предоставляет средства управления динамическим распределением памяти и механизмы работы с этой памятью. Благодаря этому можно во время

выполнения программы запрашивать у системы дополнительную память для нужд программы. Однако используя данные средства, программист берет на себя ответственность сообщать системе, когда выделенная память далее не нужна, отвечает за корректность использования выделенной памяти.

Небрежное обращение с данными средствами приводит к утечкам памяти и обращениям по некорректным адресам в памяти, что в свою очередь ведет к краху программы.

Существует ряд утилит для упрощения контроля корректности работы с динамически распределенной памятью. Одной из таких утилит является valgrind.

213

PDF created with pdfFactory Pro trial version www.pdffactory.com

Утилита valgrind позволяет выявлять акты доступа к памяти, находящейся вне рабочего пространства программы, попытки повторного освобождения блоков памяти, а также другие ошибки.

Утилита контролирует обращения к памяти, производимые из вашей программы; выводит подробные сообщения о попытках доступа к памяти, лежащей за рабочим пространством программы; помогает выявить утечки памяти, выводя сообщение с указанием строк кода, в которых была выделена память, которая не освобождается.

Для контроля доступа к памяти, valgrind помещает в программу специальный код, контролирующий операции записи и чтения памяти. За использование возможностей утилиты приходится платить замедлением работы программы, поэтому ей пользуются во время разработки и тестирования кода.

Обобщенная схема вызова утилиты выглядит следующим образом: valgrind [valgrind-options] [your-program] [your-program-options] [valgrind-options] - опции (см. man valgrind);

[your-program] - имя исследуемой программы; [your-program-options] - аргументы исследуемой программы.

Рассмотрим программу, которая помещает в стандартный контейнер вектор MAX целых чисел, создаваемых динамически:

1// main.cpp

2#include <vector>

4int main()

5{

6typedef std::vector<int> intVector;

7intVector cont;

9const int MAX = 100;

10

11for(int i = 0; i < MAX; ++i)

12{

13cont.push_back(*(new int(i)));

14}

15

16return 0;

17}

214

PDF created with pdfFactory Pro trial version www.pdffactory.com

1:комментарий, содержащий имя файла;

2:заголовок, содержащий объявление контейнера вектор;

4: функция main, принимает нуль аргументов, возвращает целое;

6:вводим (typedef) синоним (intVector) для вектора, хранящего в себе целые числа

(std::vector<int>);

7:определяем вектор целых cont;

9: определяем целочисленную (int) постоянную (const) MAX;

11: цикл со счетчиком (int i), изменяющим свое значение от 0 (i = 0) до MAX-1 (i < MAX) путем увеличения на 1 (++i) на каждой итерации;

13: помещаем в конец контейнера (.push_back(...)) cont новое целое (*(... int(...))), которое было динамически создано (new …) из i (int(i));

16: возврат из main с успешным кодом (0).

Проверим корректность работы с памятью в данной программе с помощью valgrid. Для этого соберем программу с отладочной информацией:

$ g++ -g main.cpp -o proj

-g - компилировать с отладочной информацией; main.cpp - исходный код программы;

-o proj - имя исполняемого файла.

Запустим valgrind с указанием производить все проверки (--leak_check=full) в программе proj:

$ valgrind --leak-check=full ./proj

1==13550== Memcheck, a memory error detector.

2==13550== Copyright (C) 2002-2008, and GNU GPL'd, by Julian Seward et al.

3==13550== Using LibVEX rev 1884, a library for dynamic binary translation.

4==13550== Copyright (C) 2004-2008, and GNU GPL'd, by OpenWorks LLP.

5==13550== Using valgrind-3.4.1-Debian, a dynamic binary instrumentation framework.

6==13550== Copyright (C) 2000-2008, and GNU GPL'd, by Julian Seward et al.

7==13550== For more details, rerun with: -v

8==13550==

9==13550==

10==13550== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 17 from 1)

11==13550== malloc/free: in use at exit: 400 bytes in 100 blocks.

12==13550== malloc/free: 108 allocs, 8 frees, 1,420 bytes allocated.

13==13550== For counts of detected errors, rerun with: -v

14==13550== searching for pointers to 100 not-freed blocks.

215

PDF created with pdfFactory Pro trial version www.pdffactory.com

15 ==13550== checked 93,148 bytes.

16==13550==

17==13550== 400 bytes in 100 blocks are definitely lost in loss record 1 of 1

18 ==13550==

at 0x40269EE: operator new(unsigned int) (vg_replace_malloc.c:224)

19 ==13550==

by 0x804865D: main (main.cpp:13)

20==13550==

21==13550== LEAK SUMMARY:

22 ==13550==

definitely lost: 400 bytes in 100 blocks.

23 ==13550==

possibly lost: 0 bytes in 0 blocks.

24 ==13550==

still reachable: 0 bytes in 0 blocks.

25 ==13550==

suppressed: 0 bytes in 0 blocks.

В строках 21 – 25 дана сводка по результатам работы. Согласно ей программа допускает суммарные утечки памяти в 400 байт. Дальнейшие подробности даны в строках 17 – 19. Утилита сообщает о том, что образовалась утечка в 400 байт (400 bytes in 100 blocks are definitely lost in loss record 1 of 1) из-за динамического выделения памяти (operator new(unsigned int)) в строке 13 функции main (main (main.cpp:13)).

Причина утечки кроется в том, что динамически распределенная в

13-й строке память не освобождается (отсутствует вызов оператора delete). Попробуем решить проблему следующим образом:

1// main.cpp

2#include <vector>

3#include <memory>

5int main()

6{

7typedef std::vector<int> intVector;

8intVector cont;

10 const int MAX = 100;

11

12for(int i = 0; i < MAX; ++i)

13{

14std::auto_ptr<int> holder(new int(i));

15cont.push_back(*holder.get());

16}

216

PDF created with pdfFactory Pro trial version www.pdffactory.com

17

18return 0;

19}

1:комментарий, содержащий имя файла;

2:заголовок, содержащий объявление контейнера вектор;

3:заголовок, содержащий объявление стандартного «умного» указателя auto_ptr, который будет отвечать за освобождение динамически выделенной памяти;

5:функция main, принимает нуль аргументов, возвращает целое;

7:вводим (typedef) синоним (intVector) для вектора, хранящего в себе целые числа

(std::vector<int>);

8:определяем вектор целых cont;

10:определяем целочисленную (int) постоянную (const) MAX;

12:цикл со счетчиком (int i), изменяющим свое значение от 0 (i = 0) до MAX-1(i < MAX) путем увеличения на 1(++i) на каждой итерации;

14:язык C++ позволяет распределять память динамически (с помощью оператора new), при этом за освобождение (с помощью явного вызова delete) этой памяти отвечает разработчик. Стандартный «умный» указатель auto_ptr берет на себя освобождение динамически выделенной памяти, если передать ему указатель на эту память. Перед тем как разрушиться, auto_ptr вызывает delete для указателя, который был передан ему при создании. Механизм работы прост: так как «умный» указатель holder в строке 14 создается в стеке, то по выходу из области, в которой он виден (строки 13 – 16), память будет гарантированно освобождена;

15:помещаем в конец контейнера (.push_back(...)) cont новое целое (*(...)), предварительно взяв (holder.get()) указатель на него у holder;

18:возврат из main с успешным кодом (0). Проверим новую версию на утечки:

1 ==13724== Memcheck, a memory error detector.

2 ==13724== Copyright (C) 2002-2008, and GNU GPL'd, by Julian Seward et al. 3 ==13724== Using LibVEX rev 1884, a library for dynamic binary translation. 4 ==13724== Copyright (C) 2004-2008, and GNU GPL'd, by OpenWorks LLP.

5 ==13724== Using valgrind-3.4.1-Debian, a dynamic binary instrumentation framework. 6 ==13724== Copyright (C) 2000-2008, and GNU GPL'd, by Julian Seward et al.

7 ==13724== For more details, rerun with: -v

8 ==13724== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 17 from 1) 9 ==13724== malloc/free: in use at exit: 0 bytes in 0 blocks.

217

PDF created with pdfFactory Pro trial version www.pdffactory.com

10 ==13724== malloc/free: 108 allocs, 108 frees, 1,420 bytes allocated.

11==13724== For counts of detected errors, rerun with: -v

12==13724== All heap blocks were freed -- no leaks are possible.

Утечки отсутствуют.

13.4. Отслеживание обращений к ОС

Если в вашей программе используется вывод в консоль, обращение к файлам или другие средства, предоставляемые операционной системой, и эта программа завершается аварийной остановкой, возможно, проблема кроется в том, что программа некорректно обращается с ресурсами, предоставляемыми системой. Например, программа пытается получить доступ к несуществующему или недоступному для чтения файлу.

В качестве примера утилиты контроля системных вызовов, осуществляемых приложением, рассмотрим strace. Она позволяет отследить системные вызовы, осуществляемые работающей программой. Информация, выводимая strace, может показаться пространной, так как отображаются не вызовы С++ функций работы с потоками, а системные вызовы, лежащие в их основе. К тому же количество этих системных вызовов может быть много больше, чем ожидает программист. Несомненным плюсом утилиты является возможность увидеть, как именно происходит общение программы с системой.

Обобщенная схема вызова утилиты: strace [options] [program_name]

[options] - опции (см. man strace). [program_name] - имя исследуемой программы.

Приведем пример работы утилиты на классической программе «Hello world!»: 1 // main.cpp

2

3 #include <iostream>

4

5int main()

6{

7std::cout << «Hello World!\n»;

8return 0;

9}

1:комментарий, сообщающий имя файла;

218

PDF created with pdfFactory Pro trial version www.pdffactory.com

3: стандартный заголовок для работы с консолью;

5: одна из стандартных сигнатур функции main, принимает нуль аргументов, возвращает целое;

7:вывод (<<) в консоль (std::cout) строки «Hello World!»;

8:возврат 0 (успешное завершение).

Скомпилируем программу: $ g++ main.cpp -o proj

main.cpp - имя файла с исходным кодом;

-o proj - имя создаваемого объектного файла.

Запустим strace:

 

 

$ strace ./proj

 

 

1

execve(«./proj», [«./proj»], [/* 39 vars */]) = 0

2

brk(0)

= 0x91ee000

 

3

access(«/etc/ld.so.nohwcap», F_OK)

= -1 ENOENT (No such file or directory)

4

mmap2(NULL, 8192, PROT_READ|PROT_WRITE,

MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7f1c000

5

access(«/etc/ld.so.preload», R_OK)

= -1 ENOENT (No such file or directory)

6

open(«/etc/ld.so.cache», O_RDONLY)

= 3

7fstat64(3, {st_mode=S_IFREG|0644, st_size=67134, ...}) = 0

8mmap2(NULL, 67134, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7f0b000

9

close(3)

= 0

10

access(«/etc/ld.so.nohwcap», F_OK) = -1 ENOENT (No such file or directory)

11open(«/usr/lib/libstdc++.so.6», O_RDONLY) = 3

12read(3, «\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0000B\4\0004\0\0\0\350»..., 512) =

13fstat64(3, {st_mode=S_IFREG|0644, st_size=950424, ...}) = 0

14mmap2(NULL, 977132, PROT_READ|PROT_EXEC,

MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb7e1c000

15

mmap2(0xb7f00000, 20480, PROT_READ|PROT_WRITE,

MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xe3) = 0xb7f00000

16

mmap2(0xb7f05000, 22764, PROT_READ|PROT_WRITE,

MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb7f05000

17

close(3)

= 0

18

access(«/etc/ld.so.nohwcap», F_OK) = -1 ENOENT (No such file or directory)

19

open(«/lib/tls/i686/cmov/libm.so.6», O_RDONLY) = 3

 

 

219

PDF created with pdfFactory Pro trial version www.pdffactory.com

20read(3, «\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0@4\0\0004\0\0\0P»..., 512) = 512

21fstat64(3, {st_mode=S_IFREG|0644, st_size=149328, ...}) = 0

22mmap2(NULL, 151680, PROT_READ|PROT_EXEC,

MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb7df6000

23

mmap2(0xb7e1a000, 8192, PROT_READ|PROT_WRITE,

MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x23) = 0xb7e1a000

24

close(3)

= 0

25

access(«/etc/ld.so.nohwcap», F_OK) = -1 ENOENT (No such file or directory)

26

open(«/lib/libgcc_s.so.1», O_RDONLY) = 3

27read(3, «\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\260\34\0\0004\0\0\0\234»..., 512) = 512

28fstat64(3, {st_mode=S_IFREG|0644, st_size=54740, ...}) = 0

29mmap2(NULL, 4096, PROT_READ|PROT_WRITE,

MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7df5000

30mmap2(NULL, 57864, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb7de6000

31mmap2(0xb7df3000, 8192, PROT_READ|PROT_WRITE,

MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xc) = 0xb7df3000

32

close(3)

= 0

33

access(«/etc/ld.so.nohwcap», F_OK) = -1 ENOENT (No such file or directory)

34open(«/lib/tls/i686/cmov/libc.so.6», O_RDONLY) = 3

35read(3, «\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\320h\1\0004\0\0\0\344»..., 512) =

36fstat64(3, {st_mode=S_IFREG|0755, st_size=1442180, ...}) = 0

37mmap2(NULL, 1451632, PROT_READ|PROT_EXEC,

MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb7c83000

38mprotect(0xb7ddf000, 4096, PROT_NONE) = 0

39mmap2(0xb7de0000, 12288, PROT_READ|PROT_WRITE,

MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x15c) = 0xb7de0000 40 mmap2(0xb7de3000, 9840, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb7de3000

41

close(3)

= 0

42

mmap2(NULL, 4096, PROT_READ|PROT_WRITE,

MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7c82000

220

PDF created with pdfFactory Pro trial version www.pdffactory.com

Соседние файлы в папке LINUX