forked from UNN/2026-rff_mp
289 lines
7.5 KiB
Go
289 lines
7.5 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"math/rand"
|
|
csvwriter "source/pkg/csv_writer"
|
|
ds "source/pkg/data_struct"
|
|
dg "source/pkg/gen_data"
|
|
bst "source/pkg/structures/bin_search_tree"
|
|
ht "source/pkg/structures/hash_table"
|
|
ll "source/pkg/structures/linked_list"
|
|
|
|
// csv "source/pkg/csv_ri"
|
|
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
countUsers = 20_000
|
|
countRepeat = 20
|
|
countRandomSearch = 1000
|
|
countNotExitstSearch = 500
|
|
countDeletes = 1000
|
|
)
|
|
|
|
type TestData struct {
|
|
Items []ds.MyData // все записи
|
|
ItemsSorted []ds.MyData // все записи отсортированные
|
|
Search []ds.MyData // для поиска (существующие и несуществующие)
|
|
ToDelete []ds.MyData // для удаления
|
|
UniqueItems []ds.MyData // Уникальные элементы для тестов
|
|
}
|
|
|
|
type DataStructure interface {
|
|
Insert(data ds.MyData)
|
|
InsertAll(data []ds.MyData)
|
|
Search(name string) (string, bool)
|
|
Delete(name string) bool
|
|
Len() int
|
|
}
|
|
|
|
// Создатели структур
|
|
type StructureFactory func() DataStructure
|
|
|
|
func NewLinkedList() DataStructure {
|
|
return ll.NewLinkedList()
|
|
}
|
|
|
|
func NewHashTable() DataStructure {
|
|
return ht.NewHashTable(256, 0.75)
|
|
}
|
|
|
|
func NewBinSearchTree() DataStructure {
|
|
return bst.NewBinSearchTree()
|
|
}
|
|
|
|
func uniqueElements(data []ds.MyData) []ds.MyData {
|
|
res := make([]ds.MyData, 0, len(data))
|
|
|
|
for _, el := range data {
|
|
isUnique := true
|
|
for _, resEl := range res {
|
|
if el == resEl {
|
|
isUnique = false
|
|
break
|
|
}
|
|
}
|
|
if isUnique {
|
|
res = append(res, el)
|
|
}
|
|
}
|
|
|
|
return res
|
|
}
|
|
|
|
func GenerateTestData() TestData {
|
|
items := dg.RecordsShuffled(countUsers)
|
|
// fmt.Println("isSorted:", isSorted(items))
|
|
itemsSort := ds.QSort(items, 0, len(items)-1)
|
|
|
|
uniqueItems := uniqueElements(items)
|
|
existing := make([]ds.MyData, countRandomSearch)
|
|
// notExisting := [countNotExitstSearch]ds.MyData{}
|
|
notExisting := make([]ds.MyData, countNotExitstSearch)
|
|
toDelete := make([]ds.MyData, countDeletes)
|
|
|
|
countUniq := len(uniqueItems)
|
|
for i := 0; i < countRandomSearch; i++ {
|
|
// randInd := rand.Intn(countUsers)
|
|
randInd := rand.Intn(countUniq)
|
|
existing[i] = uniqueItems[randInd]
|
|
// fmt.Println(randInd)
|
|
}
|
|
|
|
for i := 0; i < countNotExitstSearch; i++ {
|
|
// randInd := rand.Intn(countUsers)
|
|
randInd := rand.Intn(10)
|
|
name := fmt.Sprintf("User_%d", randInd)
|
|
notExisting[i] = *ds.NewData(name, "")
|
|
// fmt.Println(randInd)
|
|
}
|
|
|
|
for _, el := range notExisting {
|
|
existing = append(existing, el)
|
|
}
|
|
|
|
// toDelete = make([]ds.MyData, countDeletes)
|
|
usedIndices := make(map[int]bool)
|
|
for i := 0; i < countDeletes; i++ {
|
|
var randInd int
|
|
for {
|
|
randInd = rand.Intn(countUniq)
|
|
if !usedIndices[randInd] {
|
|
usedIndices[randInd] = true
|
|
break
|
|
}
|
|
}
|
|
toDelete[i] = uniqueItems[randInd]
|
|
}
|
|
|
|
return TestData{
|
|
Items: items,
|
|
ItemsSorted: itemsSort,
|
|
Search: existing,
|
|
ToDelete: toDelete,
|
|
UniqueItems: uniqueItems,
|
|
}
|
|
}
|
|
|
|
// Тест вставки массива данных (один раз)
|
|
func testOnesInsert(structure DataStructure, data []ds.MyData) float64 {
|
|
start := time.Now()
|
|
|
|
for _, item := range data {
|
|
structure.Insert(item)
|
|
}
|
|
|
|
return time.Since(start).Seconds()
|
|
}
|
|
|
|
// Тест поиска массива данных (один раз)
|
|
func testOnesSearch(structure DataStructure, data []ds.MyData) float64 {
|
|
start := time.Now()
|
|
|
|
// flag := true
|
|
|
|
for _, item := range data {
|
|
structure.Search(item.Name)
|
|
// p, ok := structure.Search(item.Name)
|
|
|
|
// if flag {
|
|
// flag = ((p == item.Phone) == ok)
|
|
// }
|
|
}
|
|
|
|
// fmt.Println(flag)
|
|
|
|
return time.Since(start).Seconds()
|
|
}
|
|
|
|
// Тест удаления массива данных (один раз)
|
|
func testOnesDelete(structure DataStructure, data []ds.MyData) float64 {
|
|
start := time.Now()
|
|
|
|
for _, item := range data {
|
|
structure.Delete(item.Name)
|
|
}
|
|
|
|
return time.Since(start).Seconds()
|
|
}
|
|
|
|
func testForData(nameStruct, mode string, factory StructureFactory, data_insert, data_search, data_delete []ds.MyData) {
|
|
BenchRes := make([]csvwriter.BenchmarkResult, 0, countRepeat*3+3) // Массив строк отчёта
|
|
|
|
averageTimeInsert := 0.
|
|
averageTimeSearch := 0.
|
|
averageTimeDelete := 0.
|
|
|
|
for iteration := 0; iteration < countRepeat; iteration++ {
|
|
|
|
structure := factory()
|
|
|
|
insertTime := testOnesInsert(structure, data_insert)
|
|
averageTimeInsert += insertTime
|
|
|
|
// Отладочная информация для бинарного дерева (проверка на вырождение)
|
|
if bst, ok := structure.(*bst.BinSearchTree); ok {
|
|
fmt.Printf(
|
|
"Высота дерева: %d, элементов: %d\n",
|
|
bst.Height(), bst.Len(),
|
|
)
|
|
}
|
|
|
|
BenchRes = append(BenchRes, csvwriter.BenchmarkResult{
|
|
Structure: nameStruct,
|
|
Mode: mode,
|
|
Operation: "Вставка",
|
|
Time: insertTime,
|
|
})
|
|
|
|
searchTime := testOnesSearch(structure, data_search)
|
|
averageTimeSearch += searchTime
|
|
|
|
BenchRes = append(BenchRes, csvwriter.BenchmarkResult{
|
|
Structure: nameStruct,
|
|
Mode: mode,
|
|
Operation: "Поиск",
|
|
Time: searchTime,
|
|
})
|
|
|
|
deleteTime := testOnesDelete(structure, data_delete)
|
|
averageTimeDelete += deleteTime
|
|
|
|
BenchRes = append(BenchRes, csvwriter.BenchmarkResult{
|
|
Structure: nameStruct,
|
|
Mode: mode,
|
|
Operation: "Удаление",
|
|
Time: deleteTime,
|
|
})
|
|
fmt.Printf("%s | Вставка | %s | Время: %f\n", nameStruct, mode, insertTime)
|
|
fmt.Printf("%s | Поиск | %s | Время: %f\n", nameStruct, mode, searchTime)
|
|
fmt.Printf("%s | Удаление | %s | Время: %.9f\n", nameStruct, mode, deleteTime)
|
|
}
|
|
|
|
averageTimeInsert /= countRepeat
|
|
averageTimeSearch /= countRepeat
|
|
averageTimeDelete /= countRepeat
|
|
|
|
BenchRes = append(BenchRes, csvwriter.BenchmarkResult{
|
|
Structure: nameStruct,
|
|
Mode: mode,
|
|
Operation: "Вставка (среднее)",
|
|
Time: averageTimeInsert,
|
|
})
|
|
BenchRes = append(BenchRes, csvwriter.BenchmarkResult{
|
|
Structure: nameStruct,
|
|
Mode: mode,
|
|
Operation: "Поиск (среднее)",
|
|
Time: averageTimeSearch,
|
|
})
|
|
BenchRes = append(BenchRes, csvwriter.BenchmarkResult{
|
|
Structure: nameStruct,
|
|
Mode: mode,
|
|
Operation: "Удаление (среднее)",
|
|
Time: averageTimeDelete,
|
|
})
|
|
|
|
fmt.Printf("%s | Вставка | %s | Время (среднее): %f\n", nameStruct, mode, averageTimeInsert)
|
|
fmt.Printf("%s | Поиск | %s | Время (среднее): %f\n", nameStruct, mode, averageTimeSearch)
|
|
fmt.Printf("%s | Удаление | %s | Время (среднее): %f\n", nameStruct, mode, averageTimeDelete)
|
|
|
|
csvwriter.AppendRaw(BenchRes)
|
|
}
|
|
|
|
func isSorted(data []ds.MyData) bool {
|
|
for i := 0; i < len(data)-1; i++ {
|
|
if data[i].Name > data[i+1].Name {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func Test(nameStruct string, factory StructureFactory) {
|
|
data := GenerateTestData()
|
|
|
|
// fmt.Println("items", isSorted(data.Items))
|
|
// fmt.Println("items sort", isSorted(data.ItemsSorted))
|
|
|
|
testForData(nameStruct, "Случайный", factory, data.Items, data.Search, data.ToDelete)
|
|
|
|
testForData(nameStruct, "Отсортированный", factory, data.ItemsSorted, data.Search, data.ToDelete)
|
|
|
|
}
|
|
|
|
func main() {
|
|
|
|
csvwriter.CreateEmptyCSV("results", "benchmarks.csv")
|
|
|
|
fmt.Println("============= Начало тестов =============")
|
|
|
|
Test("Связный список", NewLinkedList)
|
|
Test("Хеш таблица", NewHashTable)
|
|
Test("Бинарное дерево поиска", NewBinSearchTree)
|
|
|
|
// fmt.Println("User_0001" < "User_00100")
|
|
// fmt.Println(isSorted(dg.RecordsShuffled(10000)))
|
|
}
|