2026-rff_mp/stepushovgs/data-structures/source/tests/benchmark/main.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)))
}