Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .DS_Store
Binary file not shown.
5 changes: 5 additions & 0 deletions .codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
coverage:
status:
patch:
default:
target:80%
36 changes: 36 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@

dist: focal

language: c

os: linux

compiler: gcc

install:
- sudo apt-get install valgrind
- sudo apt-get install clang
- sudo apt-get install cppcheck
- sudo pip install cpplint

script:
- cd project/
- mkdir build
- cd build
- cmake ..
- make clean && make
- cppcheck --inconclusive --enable=all --language=c ../include/*.h ../src/*.c
- cpplint ../include/*.h ../src/*.c ../tests/*.cpp
- LC_CTYPE=C tr -dc A-Za-z0-9 < /dev/urandom |fold -w 100 | head -n 100000 > bigfile.txt
- echo "2 ab" | valgrind --leak-check=full --track-origins=yes -s ./main.out bigfile.txt
- valgrind --leak-check=full --track-origins=yes ./linear_tests
- valgrind --leak-check=full --track-origins=yes ./parallel_tests
- LC_CTYPE=C tr -dc A-Za-z0-9 < /dev/urandom |fold -w 100 | head -n 1000000 > bigfile2.txt
- echo "these are clean times, no valgrind, on 100MB"
- echo "2 ab" | ./main.out bigfile2.txt




after_success:
- bash <(curl -s https://codecov.io/bash)
4 changes: 4 additions & 0 deletions CPPLINT.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
filter=-legal/copyright
filter=-build/include_subdir
filter=-build/include
filter=-readability/casting
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,24 @@
# cpp_2021

[![Build Status](https://travis-ci.com/BorisKoz/cpp_2021.svg?branch=HW-2)](https://travis-ci.com/BorisKoz/cpp_2021)

[![codecov](https://codecov.io/gh/BorisKoz/cpp_2021/branch/HW-2/graph/badge.svg)](https://codecov.io/gh/BorisKoz/cpp_2021)

On my machine : parallel is faster : 0.152859 vs 1.536328s
Remote : parallel is faster : 0.588880 vs 0.765509

# Вариант #4
Перед вами поставлена задача подсчета количества вхождений заданных символов в загруженный в оперативную память файл размером 100 Мб. Составьте наивный алгоритм подсчета вхождений символов, в затем реализуйте параллельную обработку текста несколькими процессами с учетом оптимизации работы с кэш-памятью.

# На что необходимо обратить внимание:
- основная информация описана в https://park.mail.ru/blog/topic/view/14270/
- параллельная реализация не должна быть осуществлена с помощью процессов, когда требуется реализация с помощью потоков (и наоборот);
- компиляция должна происходить с флагами -Wall -Werror -Wpedantic, то есть необработанных ворнингов быть не должно;
- количество потоков/процессов должно быть не захардкожено, а определяться в зависимости от возможностей системы (например, в зависимости от количества ядер процессора);
- при разработке обеих библиотек стоит делать общий интерфейс, не раскрывая особенностей реализации;
- библиотеки должны быть взаимозаменяемыми - конкретная реализация (последовательная/параллельная) - использоваться в зависимости от конфигурации сборки;
- юнит-тесты должны быть реализованы для обеих реализаций (последовательной/параллельной). Покрытие тестами должно быть максимально возможным;
- должны присутствовать стресс-тесты. Они могут быть реализованы внешним образом, запуская две разные программы - одну со статической библиотекой с последовательной реализацией, вторую - с динамической библиотекой с параллельной реализацией, и сравнивая их выводы друг с другом.
- для организации ввода/вывода больших данных полезно работать с файлами, а в программе - предусмотреть работу с универсальными потоками входных/выходных данных (или хотя бы перенаправлять ввод/вывод на уровне их запуска)
- если в задании сказано, что программа должна обрабатывать файлы объёмом 100 Мб – это лишь ориентир, на которых программа точно должна работать, и на котором имеет смысл делать замеры производительности и эффективности алгоритмов. Поэтому тесты на такой объём должны быть. Однако сама программа должна уметь работать с произвольными размерами входных данных
- измерение времени должно осуществляться внешним образом, а не внутри кода библиотек. При этом необходимо делать несколько замеров и усреднять. Стоит помнить о том, что clock() в многопоточных приложениях работает не так, как ожидается.
Binary file added project/.DS_Store
Binary file not shown.
54 changes: 54 additions & 0 deletions project/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
cmake_minimum_required(VERSION 3.15)
project(files)

configure_file(CMakeLists.txt.in
googletest-download/CMakeLists.txt)
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download )
execute_process(COMMAND ${CMAKE_COMMAND} --build .
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download )

add_subdirectory(${CMAKE_BINARY_DIR}/googletest-src
${CMAKE_BINARY_DIR}/googletest-build)

set(CMAKE_C_FLAGS "-pedantic -fprofile-arcs -ftest-coverage -Wall -Werror -Wpedantic")
set(CMAKE_CXX_FLAGS "-pedantic -fprofile-arcs -ftest-coverage -Wall -Werror -Wpedantic")

enable_testing()


include_directories("${PROJECT_SOURCE_DIR}/include")


set(INCLUDE ${PROJECT_SOURCE_DIR}/include)
set(SOURCE ${PROJECT_SOURCE_DIR}/src)

add_library(bigfile_linear STATIC
${INCLUDE}/search.h
${SOURCE}/linear.c)
# changed to MODULE, otherwise is built different local-remote
add_library(bigfile_parallel MODULE
${INCLUDE}/search.h
${SOURCE}/parallel.c)

file(GLOB prod_sources
"${PROJECT_SOURCE_DIR}/include/*.h"
"${PROJECT_SOURCE_DIR}/src/main.c")

add_executable(main.out ${PROJECT_SOURCE_DIR}/src/main.c)
target_link_libraries(main.out -ldl bigfile_linear)


file(GLOB tests "${PROJECT_SOURCE_DIR}/tests/*.cpp")
list(REMOVE_ITEM tests "${PROJECT_SOURCE_DIR}/tests/main.cpp")

foreach(file ${tests})
set(name)
get_filename_component(name ${file} NAME_WE)
add_executable("${name}_tests"
${PROJECT_SOURCE_DIR}/src/${name}.c
${file}
"${PROJECT_SOURCE_DIR}/tests/main.cpp")
target_link_libraries("${name}_tests" gtest_main)
add_test(NAME ${name} COMMAND "${name}_tests")
endforeach()
15 changes: 15 additions & 0 deletions project/CMakeLists.txt.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@


project(googletest-download NONE)

include(ExternalProject)
ExternalProject_Add(googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG release-1.8.1
SOURCE_DIR "${CMAKE_BINARY_DIR}/googletest-src"
BINARY_DIR "${CMAKE_BINARY_DIR}/googletest-build"
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
TEST_COMMAND ""
)
16 changes: 16 additions & 0 deletions project/include/search.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright 2021 bkz
#ifndef PROJECT_INCLUDE_SEARCH_H_
#define PROJECT_INCLUDE_SEARCH_H_
#include <stdio.h>

#define NULL_ENTRY (-1)
#define MMAP_ERR 2
#define PID_ERR 3
#define WRONG_OPEN 4
#define MAX_PROCESS 10
#define READ_SIZE 20
#define BUFFER_SIZE 30
#define FORMAT_STRING "%020d"

int file_search(FILE** , const char* , size_t* , size_t size_to_find);
#endif // PROJECT_INCLUDE_SEARCH_H_
43 changes: 43 additions & 0 deletions project/src/linear.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright 2021 bkz

#include <sys/stat.h>
#include <sys/mman.h>
#include "../include/search.h"

int compare_to_array(const char* to_compare, char buffer, size_t size_to_find) {
for (int i = 0; i < size_to_find; i++) {
if (to_compare[i] == buffer) {
return i;
}
}
return -1;
}

int file_search(FILE** fp, const char* to_find,
size_t* found, size_t size_to_find) {
if (fp == NULL || *fp == NULL || to_find == NULL || found == NULL) {
return NULL_ENTRY;
}
struct stat file_stat;
fstat(fileno(*fp), &file_stat);
if ((file_stat.st_mode & S_IRUSR) == 0) {
return WRONG_OPEN;
}
if (file_stat.st_size == 0) {
return NULL_ENTRY;
}
char* file_in_memory = (char *)mmap(NULL, file_stat.st_size,
PROT_READ, MAP_SHARED, fileno(*fp), 0);
if (file_in_memory == NULL) {
return MMAP_ERR;
}
for (size_t i = 0; i < file_stat.st_size; i++) {
int position = compare_to_array(to_find,
file_in_memory[i], size_to_find);
if (position != -1) {
found[position]++;
}
}
munmap(file_in_memory, file_stat.st_size);
return 0;
}
102 changes: 102 additions & 0 deletions project/src/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// Copyright 2021 bkz

#include <stdio.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <stdlib.h>
#include "../include/search.h"
#include <dlfcn.h>
#define TEST_SERIES_SIZE 3
#define RESULTS_FILE "res.txt"

int main(int argc, char* argv[]) {
if (argc != 2) {
fprintf(stderr, "No file input");
return -1;
}
// открытие файла
FILE* p = fopen(argv[1], "r");
if (!p) {
fprintf(stderr, "No such file");
return -1;
}

// считывание символов
int size_to_find = 0;
char to_find[BUFFER_SIZE] = "";
size_t found[BUFFER_SIZE];
memset(found, 0, BUFFER_SIZE * sizeof(*found));
scanf("%d", &size_to_find);
if (size_to_find > 30) {
fprintf(stderr, "Too much symbols");
fclose(p);
return -1;
}
scanf("%c", &to_find[0]);
for (int i = 0; i < size_to_find; i++) {
scanf("%c", &to_find[i]);
}

// работа с файлом последовательно
double elapsed[TEST_SERIES_SIZE], average = 0;
memset(found, 0, TEST_SERIES_SIZE * sizeof(*elapsed));
for (int i = 0; i < TEST_SERIES_SIZE; i++) {
struct timespec start, finish;

clock_gettime(CLOCK_MONOTONIC, &start);

file_search(&p, to_find, found, size_to_find);

clock_gettime(CLOCK_MONOTONIC, &finish);
elapsed[i] = (double)(finish.tv_sec - start.tv_sec);
elapsed[i] += (double)(finish.tv_nsec - start.tv_nsec) / 1000000000.0;
printf("elapsed: %lf\n", elapsed[i]);
average += elapsed[i];
memset(found, 0, BUFFER_SIZE * sizeof(*found));
}
average = average / TEST_SERIES_SIZE;
printf("Linear series average: %lf\n", average);
if (p) {
fclose(p);
}

// parallel run
p = fopen(argv[1], "r");
void *parallel_lib = dlopen("./libbigfile_parallel.so", RTLD_LAZY);
if (!parallel_lib) {
fprintf(stderr, "LIBRARY NOT FOUND");
return -1;
}

int (*pointer)();
*(void **)(&pointer) = dlsym(parallel_lib, "file_search");

double average_parallel = 0;
memset(found, 0, TEST_SERIES_SIZE * sizeof(*elapsed));
for (int i = 0; i < TEST_SERIES_SIZE; i++) {
struct timespec start, finish;

clock_gettime(CLOCK_MONOTONIC, &start);

(*pointer)(&p, to_find, found, size_to_find);

clock_gettime(CLOCK_MONOTONIC, &finish);
elapsed[i] = (double)(finish.tv_sec - start.tv_sec);
elapsed[i] += (double)(finish.tv_nsec - start.tv_nsec) / 1000000000.0;
printf("elapsed: %lf\n", elapsed[i]);
average_parallel += elapsed[i];
memset(found, 0, BUFFER_SIZE * sizeof(*found));
}
average_parallel = average_parallel / TEST_SERIES_SIZE;
printf("Parallel series average: %lf\n", average_parallel);
average > average_parallel ?
printf("parallel is faster : %lf vs %lf\n", average_parallel, average) :
printf("linear is faster : %lf vs %lf\n", average, average_parallel);
dlclose(parallel_lib);

if (p) {
fclose(p);
}
return 0;
}
Loading