mirror of
https://github.com/bpatrik/pigallery2.git
synced 2024-12-21 01:22:08 +02:00
Adding benchmark to release code
This commit is contained in:
parent
03789ecaf8
commit
0a8af49752
@ -2,7 +2,6 @@
|
||||
.idea/
|
||||
.git/
|
||||
node_modules/
|
||||
benchmark/
|
||||
release/
|
||||
demo/
|
||||
dist/
|
||||
|
@ -11,6 +11,7 @@ import {ConfigProperty} from 'typeconfig/common';
|
||||
enumsAsString: true,
|
||||
softReadonly: true,
|
||||
cli: {
|
||||
prefix: 'bm-config',
|
||||
enable: {
|
||||
configPath: true,
|
||||
attachState: true,
|
||||
@ -31,6 +32,8 @@ export class PrivateConfigClass {
|
||||
path: string = 'demo/images';
|
||||
@ConfigProperty({description: 'Describe your system setup'})
|
||||
system: string = '';
|
||||
@ConfigProperty({description: 'Number of times to run the benchmark'})
|
||||
RUNS: number = 50;
|
||||
|
||||
|
||||
}
|
||||
|
125
benchmark/Benchmark.ts
Normal file
125
benchmark/Benchmark.ts
Normal file
@ -0,0 +1,125 @@
|
||||
import {BenchmarkResult} from './BenchmarkRunner';
|
||||
import {ContentWrapper} from '../src/common/entities/ConentWrapper';
|
||||
|
||||
export interface BenchmarkStep {
|
||||
name: string;
|
||||
fn: ((input?: any) => Promise<ContentWrapper | any[] | void>);
|
||||
}
|
||||
|
||||
export class Benchmark {
|
||||
|
||||
|
||||
steps: BenchmarkStep[] = [];
|
||||
name: string;
|
||||
inputCB: () => any;
|
||||
beforeEach: () => Promise<any>;
|
||||
afterEach: () => Promise<any>;
|
||||
|
||||
|
||||
constructor(name: string,
|
||||
inputCB?: () => any,
|
||||
beforeEach?: () => Promise<any>,
|
||||
afterEach?: () => Promise<any>) {
|
||||
this.name = name;
|
||||
this.inputCB = inputCB;
|
||||
this.beforeEach = beforeEach;
|
||||
this.afterEach = afterEach;
|
||||
}
|
||||
|
||||
async run(RUNS: number): Promise<BenchmarkResult> {
|
||||
console.log('Running benchmark: ' + this.name);
|
||||
const scanned = await this.scanSteps();
|
||||
const start = process.hrtime();
|
||||
let skip = 0;
|
||||
const stepTimer = new Array(this.steps.length).fill(0);
|
||||
for (let i = 0; i < RUNS; i++) {
|
||||
if (this.beforeEach) {
|
||||
const startSkip = process.hrtime();
|
||||
await this.beforeEach();
|
||||
const endSkip = process.hrtime(startSkip);
|
||||
skip += (endSkip[0] * 1000 + endSkip[1] / 1000000);
|
||||
}
|
||||
await this.runOneRound(stepTimer);
|
||||
if (this.afterEach) {
|
||||
const startSkip = process.hrtime();
|
||||
await this.afterEach();
|
||||
const endSkip = process.hrtime(startSkip);
|
||||
skip += (endSkip[0] * 1000 + endSkip[1] / 1000000);
|
||||
}
|
||||
}
|
||||
const end = process.hrtime(start);
|
||||
const duration = (end[0] * 1000 + end[1] / 1000000 - skip) / RUNS;
|
||||
|
||||
|
||||
const ret = this.outputToBMResult(this.name, scanned[scanned.length - 1]);
|
||||
ret.duration = duration;
|
||||
ret.subBenchmarks = scanned.map((o, i) => {
|
||||
const stepBm = this.outputToBMResult(this.steps[i].name, o);
|
||||
stepBm.duration = stepTimer[i] / RUNS;
|
||||
return stepBm;
|
||||
}
|
||||
);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
outputToBMResult(name: string, output: any[] | ContentWrapper): BenchmarkResult {
|
||||
if (output) {
|
||||
if (Array.isArray(output)) {
|
||||
return {
|
||||
name: name,
|
||||
duration: null,
|
||||
items: output.length,
|
||||
};
|
||||
}
|
||||
|
||||
if (output.directory || output.searchResult) {
|
||||
return {
|
||||
name: name,
|
||||
duration: null,
|
||||
contentWrapper: output
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
return {
|
||||
name: name,
|
||||
duration: null
|
||||
};
|
||||
}
|
||||
|
||||
async scanSteps(): Promise<any[]> {
|
||||
let pipe = this.inputCB ? this.inputCB() : null;
|
||||
const stepOutput = new Array(this.steps.length);
|
||||
|
||||
for (let j = 0; j < this.steps.length; ++j) {
|
||||
if (this.beforeEach) {
|
||||
await this.beforeEach();
|
||||
}
|
||||
for (let i = 0; i <= j; ++i) {
|
||||
pipe = await this.steps[i].fn(pipe);
|
||||
}
|
||||
stepOutput[j] = pipe;
|
||||
if (this.afterEach) {
|
||||
await this.afterEach();
|
||||
}
|
||||
}
|
||||
return stepOutput;
|
||||
}
|
||||
|
||||
async runOneRound(stepTimer: number[]): Promise<number[]> {
|
||||
let pipe = this.inputCB ? this.inputCB() : null;
|
||||
for (let i = 0; i < this.steps.length; ++i) {
|
||||
const start = process.hrtime();
|
||||
pipe = await this.steps[i].fn(pipe);
|
||||
const end = process.hrtime(start);
|
||||
stepTimer[i] += (end[0] * 1000 + end[1] / 1000000);
|
||||
}
|
||||
return stepTimer;
|
||||
}
|
||||
|
||||
addAStep(step: BenchmarkStep) {
|
||||
this.steps.push(step);
|
||||
}
|
||||
|
||||
}
|
215
benchmark/BenchmarkRunner.ts
Normal file
215
benchmark/BenchmarkRunner.ts
Normal file
@ -0,0 +1,215 @@
|
||||
import {Config} from '../src/common/config/private/Config';
|
||||
import {ObjectManagers} from '../src/backend/model/ObjectManagers';
|
||||
import {DiskMangerWorker} from '../src/backend/model/threading/DiskMangerWorker';
|
||||
import {IndexingManager} from '../src/backend/model/database/sql/IndexingManager';
|
||||
import {SearchManager} from '../src/backend/model/database/sql/SearchManager';
|
||||
import * as util from 'util';
|
||||
import * as rimraf from 'rimraf';
|
||||
import {SearchTypes} from '../src/common/entities/AutoCompleteItem';
|
||||
import {Utils} from '../src/common/Utils';
|
||||
import {DirectoryDTO} from '../src/common/entities/DirectoryDTO';
|
||||
import {ServerConfig} from '../src/common/config/private/PrivateConfig';
|
||||
import {ProjectPath} from '../src/backend/ProjectPath';
|
||||
import {PersonMWs} from '../src/backend/middlewares/PersonMWs';
|
||||
import {ThumbnailGeneratorMWs} from '../src/backend/middlewares/thumbnail/ThumbnailGeneratorMWs';
|
||||
import {Benchmark} from './Benchmark';
|
||||
import {IndexingJob} from '../src/backend/model/jobs/jobs/IndexingJob';
|
||||
import {IJob} from '../src/backend/model/jobs/jobs/IJob';
|
||||
import {JobProgressStates} from '../src/common/entities/job/JobProgressDTO';
|
||||
import {JobProgress} from '../src/backend/model/jobs/jobs/JobProgress';
|
||||
import {GalleryMWs} from '../src/backend/middlewares/GalleryMWs';
|
||||
import {UserDTO, UserRoles} from '../src/common/entities/UserDTO';
|
||||
import {ContentWrapper} from '../src/common/entities/ConentWrapper';
|
||||
|
||||
const rimrafPR = util.promisify(rimraf);
|
||||
|
||||
export interface BenchmarkResult {
|
||||
name: string;
|
||||
duration: number;
|
||||
contentWrapper?: ContentWrapper;
|
||||
items?: number;
|
||||
subBenchmarks?: BenchmarkResult[];
|
||||
}
|
||||
|
||||
export class BMIndexingManager extends IndexingManager {
|
||||
|
||||
public async saveToDB(scannedDirectory: DirectoryDTO): Promise<void> {
|
||||
return super.saveToDB(scannedDirectory);
|
||||
}
|
||||
}
|
||||
|
||||
export class BenchmarkRunner {
|
||||
|
||||
|
||||
constructor(public RUNS: number) {
|
||||
|
||||
}
|
||||
|
||||
async bmSaveDirectory(): Promise<BenchmarkResult> {
|
||||
await this.resetDB();
|
||||
const dir = await DiskMangerWorker.scanDirectory('./');
|
||||
const bm = new Benchmark('Saving directory to DB', null, () => this.resetDB());
|
||||
bm.addAStep({
|
||||
name: 'Saving directory to DB',
|
||||
fn: () => {
|
||||
const im = new BMIndexingManager();
|
||||
return im.saveToDB(dir);
|
||||
}
|
||||
});
|
||||
return await bm.run(this.RUNS);
|
||||
}
|
||||
|
||||
async bmScanDirectory(): Promise<BenchmarkResult> {
|
||||
const bm = new Benchmark('Scanning directory');
|
||||
bm.addAStep({
|
||||
name: 'Scanning directory',
|
||||
fn: async () => new ContentWrapper(await DiskMangerWorker.scanDirectory('./'))
|
||||
});
|
||||
return await bm.run(this.RUNS);
|
||||
}
|
||||
|
||||
async bmListDirectory(): Promise<BenchmarkResult> {
|
||||
await this.setupDB();
|
||||
Config.Server.Indexing.reIndexingSensitivity = ServerConfig.ReIndexingSensitivity.low;
|
||||
const bm = new Benchmark('List directory',
|
||||
null,
|
||||
async () => {
|
||||
await ObjectManagers.reset();
|
||||
await ObjectManagers.InitSQLManagers();
|
||||
});
|
||||
bm.addAStep({
|
||||
name: 'List directory',
|
||||
fn: (input) => this.nextToPromise(GalleryMWs.listDirectory, input, {directory: '/'})
|
||||
});
|
||||
bm.addAStep({
|
||||
name: 'Add Thumbnail information',
|
||||
fn: (input) => this.nextToPromise(ThumbnailGeneratorMWs.addThumbnailInformation, input)
|
||||
});
|
||||
bm.addAStep({
|
||||
name: 'Clean Up Gallery Result',
|
||||
fn: (input) => this.nextToPromise(GalleryMWs.cleanUpGalleryResults, input)
|
||||
});
|
||||
return await bm.run(this.RUNS);
|
||||
}
|
||||
|
||||
async bmListPersons(): Promise<BenchmarkResult> {
|
||||
await this.setupDB();
|
||||
Config.Server.Indexing.reIndexingSensitivity = ServerConfig.ReIndexingSensitivity.low;
|
||||
const bm = new Benchmark('Listing Faces', null, async () => {
|
||||
await ObjectManagers.reset();
|
||||
await ObjectManagers.InitSQLManagers();
|
||||
});
|
||||
bm.addAStep({
|
||||
name: 'List Persons',
|
||||
fn: (input) => this.nextToPromise(PersonMWs.listPersons, input)
|
||||
});
|
||||
bm.addAStep({
|
||||
name: 'Add sample photo',
|
||||
fn: (input) => this.nextToPromise(PersonMWs.addSamplePhotoForAll, input)
|
||||
});
|
||||
bm.addAStep({
|
||||
name: 'Add thumbnail info',
|
||||
fn: (input) => this.nextToPromise(ThumbnailGeneratorMWs.addThumbnailInfoForPersons, input)
|
||||
});
|
||||
bm.addAStep({
|
||||
name: 'Remove sample photo',
|
||||
fn: (input) => this.nextToPromise(PersonMWs.removeSamplePhotoForAll, input)
|
||||
});
|
||||
return await bm.run(this.RUNS);
|
||||
}
|
||||
|
||||
async bmAllSearch(text: string): Promise<{ result: BenchmarkResult, searchType: SearchTypes }[]> {
|
||||
await this.setupDB();
|
||||
const types = Utils.enumToArray(SearchTypes).map(a => a.key).concat([null]);
|
||||
const results: { result: BenchmarkResult, searchType: SearchTypes }[] = [];
|
||||
|
||||
for (let i = 0; i < types.length; i++) {
|
||||
const bm = new Benchmark('Searching');
|
||||
bm.addAStep({
|
||||
name: 'Searching',
|
||||
fn: async () => {
|
||||
const sm = new SearchManager();
|
||||
return new ContentWrapper(null, await sm.search(text, types[i]));
|
||||
}
|
||||
});
|
||||
results.push({result: await bm.run(this.RUNS), searchType: types[i]});
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
async bmInstantSearch(text: string): Promise<BenchmarkResult> {
|
||||
await this.setupDB();
|
||||
const bm = new Benchmark('Instant search');
|
||||
bm.addAStep({
|
||||
name: 'Instant search',
|
||||
fn: async () => {
|
||||
const sm = new SearchManager();
|
||||
return new ContentWrapper(null, await sm.instantSearch(text));
|
||||
}
|
||||
});
|
||||
return await bm.run(this.RUNS);
|
||||
}
|
||||
|
||||
async bmAutocomplete(text: string): Promise<BenchmarkResult> {
|
||||
await this.setupDB();
|
||||
const bm = new Benchmark('Auto complete');
|
||||
bm.addAStep({
|
||||
name: 'Auto complete',
|
||||
fn: () => {
|
||||
const sm = new SearchManager();
|
||||
return sm.autocomplete(text);
|
||||
}
|
||||
});
|
||||
return await bm.run(this.RUNS);
|
||||
}
|
||||
|
||||
private nextToPromise(fn: (req: any, res: any, next: Function) => void, input?: any, params = {}) {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const request = {
|
||||
resultPipe: input,
|
||||
params: params,
|
||||
query: {},
|
||||
session: {user: <UserDTO>{name: UserRoles[UserRoles.Admin], role: UserRoles.Admin}}
|
||||
};
|
||||
fn(request, resolve, (err?: any) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve(request.resultPipe);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private resetDB = async () => {
|
||||
Config.Server.Threading.enabled = false;
|
||||
await ObjectManagers.reset();
|
||||
await rimrafPR(ProjectPath.DBFolder);
|
||||
Config.Server.Database.type = ServerConfig.DatabaseType.sqlite;
|
||||
Config.Server.Jobs.scheduled = [];
|
||||
await ObjectManagers.InitSQLManagers();
|
||||
};
|
||||
|
||||
private setupDB(): Promise<void> {
|
||||
Config.Server.Threading.enabled = false;
|
||||
return new Promise<void>(async (resolve, reject) => {
|
||||
try {
|
||||
await this.resetDB();
|
||||
const indexingJob = new IndexingJob();
|
||||
|
||||
indexingJob.JobListener = {
|
||||
onJobFinished: (job: IJob<any>, state: JobProgressStates, soloRun: boolean) => {
|
||||
resolve();
|
||||
},
|
||||
|
||||
onProgressUpdate: (progress: JobProgress) => {
|
||||
}
|
||||
};
|
||||
indexingJob.start().catch(console.error);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -1,164 +0,0 @@
|
||||
import {SQLConnection} from '../src/backend/model/database/sql/SQLConnection';
|
||||
import {Config} from '../src/common/config/private/Config';
|
||||
import {ObjectManagers} from '../src/backend/model/ObjectManagers';
|
||||
import {DiskMangerWorker} from '../src/backend/model/threading/DiskMangerWorker';
|
||||
import {IndexingManager} from '../src/backend/model/database/sql/IndexingManager';
|
||||
import {SearchManager} from '../src/backend/model/database/sql/SearchManager';
|
||||
import * as util from 'util';
|
||||
import * as rimraf from 'rimraf';
|
||||
import {SearchTypes} from '../src/common/entities/AutoCompleteItem';
|
||||
import {Utils} from '../src/common/Utils';
|
||||
import {GalleryManager} from '../src/backend/model/database/sql/GalleryManager';
|
||||
import {DirectoryDTO} from '../src/common/entities/DirectoryDTO';
|
||||
import {ServerConfig} from '../src/common/config/private/PrivateConfig';
|
||||
import {ProjectPath} from '../src/backend/ProjectPath';
|
||||
import {PersonMWs} from '../src/backend/middlewares/PersonMWs';
|
||||
import {ThumbnailGeneratorMWs} from '../src/backend/middlewares/thumbnail/ThumbnailGeneratorMWs';
|
||||
|
||||
const rimrafPR = util.promisify(rimraf);
|
||||
|
||||
export interface BenchmarkResult {
|
||||
duration: number;
|
||||
directories?: number;
|
||||
media?: number;
|
||||
items?: number;
|
||||
}
|
||||
|
||||
export class BMIndexingManager extends IndexingManager {
|
||||
|
||||
public async saveToDB(scannedDirectory: DirectoryDTO): Promise<void> {
|
||||
return super.saveToDB(scannedDirectory);
|
||||
}
|
||||
}
|
||||
|
||||
export class Benchmarks {
|
||||
|
||||
constructor(public RUNS: number) {
|
||||
|
||||
}
|
||||
|
||||
async bmSaveDirectory(): Promise<BenchmarkResult> {
|
||||
await this.resetDB();
|
||||
const dir = await DiskMangerWorker.scanDirectory('./');
|
||||
const im = new BMIndexingManager();
|
||||
return await this.benchmark(() => im.saveToDB(dir), () => this.resetDB());
|
||||
}
|
||||
|
||||
async bmScanDirectory(): Promise<BenchmarkResult> {
|
||||
return await this.benchmark(() => DiskMangerWorker.scanDirectory('./'));
|
||||
}
|
||||
|
||||
async bmListDirectory(): Promise<BenchmarkResult> {
|
||||
const gm = new GalleryManager();
|
||||
await this.setupDB();
|
||||
Config.Server.Indexing.reIndexingSensitivity = ServerConfig.ReIndexingSensitivity.low;
|
||||
return await this.benchmark(() => gm.listDirectory('./'));
|
||||
}
|
||||
|
||||
async bmListPersons(): Promise<BenchmarkResult> {
|
||||
await this.setupDB();
|
||||
Config.Server.Indexing.reIndexingSensitivity = ServerConfig.ReIndexingSensitivity.low;
|
||||
return await this.benchmark(async () => {
|
||||
await ObjectManagers.reset();
|
||||
const req = {resultPipe: <any>null};
|
||||
await this.nextToPromise(PersonMWs.listPersons, req, null);
|
||||
await this.nextToPromise(PersonMWs.addSamplePhotoForAll, req, null);
|
||||
await this.nextToPromise(ThumbnailGeneratorMWs.addThumbnailInfoForPersons, req, null);
|
||||
await this.nextToPromise(PersonMWs.removeSamplePhotoForAll, req, null);
|
||||
return req.resultPipe;
|
||||
});
|
||||
}
|
||||
|
||||
async bmAllSearch(text: string): Promise<{ result: BenchmarkResult, searchType: SearchTypes }[]> {
|
||||
await this.setupDB();
|
||||
const types = Utils.enumToArray(SearchTypes).map(a => a.key).concat([null]);
|
||||
const results: { result: BenchmarkResult, searchType: SearchTypes }[] = [];
|
||||
const sm = new SearchManager();
|
||||
for (let i = 0; i < types.length; i++) {
|
||||
results.push({result: await this.benchmark(() => sm.search(text, types[i])), searchType: types[i]});
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
async bmInstantSearch(text: string): Promise<BenchmarkResult> {
|
||||
await this.setupDB();
|
||||
const sm = new SearchManager();
|
||||
return await this.benchmark(() => sm.instantSearch(text));
|
||||
}
|
||||
|
||||
async bmAutocomplete(text: string): Promise<BenchmarkResult> {
|
||||
await this.setupDB();
|
||||
const sm = new SearchManager();
|
||||
return await this.benchmark(() => sm.autocomplete(text));
|
||||
}
|
||||
|
||||
private nextToPromise(fn: (req: any, res: any, next: Function) => void, request: any, response: any) {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
fn(request, resolve, (err?: any) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private async benchmark(fn: () => Promise<{ media: any[], directories: any[] } | any[] | void>,
|
||||
beforeEach: () => Promise<any> = null,
|
||||
afterEach: () => Promise<any> = null) {
|
||||
const scanned = await fn();
|
||||
const start = process.hrtime();
|
||||
let skip = 0;
|
||||
for (let i = 0; i < this.RUNS; i++) {
|
||||
if (beforeEach) {
|
||||
const startSkip = process.hrtime();
|
||||
await beforeEach();
|
||||
const endSkip = process.hrtime(startSkip);
|
||||
skip += (endSkip[0] * 1000 + endSkip[1] / 1000000);
|
||||
}
|
||||
await fn();
|
||||
if (afterEach) {
|
||||
const startSkip = process.hrtime();
|
||||
await afterEach();
|
||||
const endSkip = process.hrtime(startSkip);
|
||||
skip += (endSkip[0] * 1000 + endSkip[1] / 1000000);
|
||||
}
|
||||
}
|
||||
const end = process.hrtime(start);
|
||||
const duration = (end[0] * 1000 + end[1] / 1000000) / this.RUNS;
|
||||
|
||||
if (!scanned) {
|
||||
return {
|
||||
duration: duration
|
||||
};
|
||||
}
|
||||
|
||||
if (Array.isArray(scanned)) {
|
||||
return {
|
||||
duration: duration,
|
||||
items: scanned.length
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
duration: duration,
|
||||
media: scanned.media.length,
|
||||
directories: scanned.directories.length
|
||||
};
|
||||
}
|
||||
|
||||
private resetDB = async () => {
|
||||
await SQLConnection.close();
|
||||
await rimrafPR(ProjectPath.DBFolder);
|
||||
Config.Server.Database.type = ServerConfig.DatabaseType.sqlite;
|
||||
await ObjectManagers.InitSQLManagers();
|
||||
};
|
||||
|
||||
private async setupDB() {
|
||||
const im = new BMIndexingManager();
|
||||
await this.resetDB();
|
||||
const dir = await DiskMangerWorker.scanDirectory('./');
|
||||
await im.saveToDB(dir);
|
||||
}
|
||||
|
||||
}
|
@ -2,6 +2,93 @@
|
||||
|
||||
These results are created mostly for development, but the results are public for curious users.
|
||||
|
||||
## PiGallery2 v1.8.2, 30.12.2020
|
||||
**System**: Intel(R) Core(TM) i7-6700HQ CPU @ 2.60GHz, 16GB Ram, SHDD: 1TB, 5400 rpm
|
||||
|
||||
**Gallery**: directories: 0 media: 341, faces: 65
|
||||
|
||||
| Action | Sub action | Action details | Average Duration | Details |
|
||||
|:------:|:----------:|:--------------:|:----------------:|:-------:|
|
||||
| **Scanning directory** | | | 1526.7 ms | media: 341, directories:0 |
|
||||
| **Saving directory to DB** | | | 679.9 ms | - |
|
||||
| **List directory** | | | 61.1 ms | media: 341, directories:0 |
|
||||
| | List directory | | 39.7 ms | media: 341, directories:0 |
|
||||
| | Add Thumbnail information | | 19.3 ms | media: 341, directories:0 |
|
||||
| | Clean Up Gallery Result | | 2.0 ms | media: 341, directories:0 |
|
||||
| **Listing Faces** | | | 8.4 ms | items: 1 |
|
||||
| | List Persons | | 1.0 ms | items: 1 |
|
||||
| | Add sample photo | | 7.1 ms | items: 1 |
|
||||
| | Add thumbnail info | | 0.1 ms | items: 1 |
|
||||
| | Remove sample photo | | 0.0 ms | items: 1 |
|
||||
| **Searching** | | `a` as `directory` | 3.3 ms | media: 0, directories:0 |
|
||||
| **Searching** | | `a` as `person` | 11.6 ms | media: 65, directories:0 |
|
||||
| **Searching** | | `a` as `keyword` | 38.0 ms | media: 339, directories:0 |
|
||||
| **Searching** | | `a` as `position` | 31.7 ms | media: 282, directories:0 |
|
||||
| **Searching** | | `a` as `photo` | 3.2 ms | media: 0, directories:0 |
|
||||
| **Searching** | | `a` as `video` | 3.2 ms | media: 0, directories:0 |
|
||||
| **Searching** | | `a` as `any` | 39.3 ms | media: 339, directories:0 |
|
||||
| **Instant search** | | `a` | 6.7 ms | media: 10, directories:0 |
|
||||
| **Auto complete** | | `a` | 6.7 ms | items: 10 |
|
||||
*Measurements run 2 times and an average was calculated.
|
||||
|
||||
|
||||
## PiGallery2 v1.8.2, 30.12.2020
|
||||
**System**: Intel(R) Core(TM) i7-6700HQ CPU @ 2.60GHz, 16GB Ram, SHDD: 1TB, 5400 rpm
|
||||
|
||||
**Gallery**: directories: 0 media: 341, faces: 65
|
||||
|
||||
| Action | Sub action | Action details | Average Duration | Details |
|
||||
|:------:|:----------:|:--------------:|:----------------:|:-------:|
|
||||
| **Scanning directory** | | | 1459.3 ms | media: 341, directories:0 |
|
||||
| **Saving directory to DB** | | | 654.9 ms | - |
|
||||
| **List directory** | | | 70.4 ms | - |
|
||||
| | List directory | | 42.1 ms | - |
|
||||
| | Add Thumbnail information | | 25.8 ms | - |
|
||||
| | Clean Up Gallery Result | | 2.3 ms | - |
|
||||
| **Listing Faces** | | | 10.5 ms | items: 1 |
|
||||
| | List Persons | | 1.0 ms | items: 1 |
|
||||
| | Add sample photo | | 9.1 ms | items: 1 |
|
||||
| | Add thumbnail info | | 0.2 ms | items: 1 |
|
||||
| | Remove sample photo | | 0.0 ms | items: 1 |
|
||||
| **Searching** | | `a` as `directory` | 3.3 ms | - |
|
||||
| **Searching** | | `a` as `person` | 11.7 ms | media: 65, directories:0 |
|
||||
| **Searching** | | `a` as `keyword` | 40.4 ms | media: 339, directories:0 |
|
||||
| **Searching** | | `a` as `position` | 30.4 ms | media: 282, directories:0 |
|
||||
| **Searching** | | `a` as `photo` | 2.7 ms | - |
|
||||
| **Searching** | | `a` as `video` | 3.5 ms | - |
|
||||
| **Searching** | | `a` as `any` | 36.9 ms | media: 339, directories:0 |
|
||||
| **Instant search** | | `a` | 5.4 ms | media: 10, directories:0 |
|
||||
| **Auto complete** | | `a` | 6.7 ms | items: 10 |
|
||||
*Measurements run 2 times and an average was calculated.
|
||||
|
||||
|
||||
## PiGallery2 v1.8.2, 30.12.2020
|
||||
**System**: Intel(R) Core(TM) i7-6700HQ CPU @ 2.60GHz, 16GB Ram, SHDD: 1TB, 5400 rpm
|
||||
|
||||
**Gallery**: directories: 0 media: 341, faces: 65
|
||||
|
||||
| Action | Sub action | Action details | Average Duration | Details |
|
||||
|:------:|:----------:|:--------------:|:----------------:|:-------:|
|
||||
| Scanning directory | | | 1746.5 ms | media: 341, directories:0 |
|
||||
| Saving directory to DB | | | 994.1 ms | - |
|
||||
| Scanning directory | | | 65.3 ms | media: 341, directories:0 |
|
||||
| Listing Faces | | | 19.0 ms | items: 1 |
|
||||
| | List Persons | | 1.9 ms | items: 1 |
|
||||
| | Add sample photo | | 16.6 ms | items: 1 |
|
||||
| | Add thumbnail info | | 0.3 ms | items: 1 |
|
||||
| | Remove sample photo | | 0.0 ms | items: 1 |
|
||||
| Searching | | `a` as `directory` | 4.1 ms | - |
|
||||
| Searching | | `a` as `person` | 16.1 ms | media: 65, directories:0 |
|
||||
| Searching | | `a` as `keyword` | 41.6 ms | media: 339, directories:0 |
|
||||
| Searching | | `a` as `position` | 67.1 ms | media: 282, directories:0 |
|
||||
| Searching | | `a` as `photo` | 5.4 ms | - |
|
||||
| Searching | | `a` as `video` | 4.3 ms | - |
|
||||
| Searching | | `a` as `any` | 53.5 ms | media: 339, directories:0 |
|
||||
| Instant search | | `a` | 5.3 ms | media: 10, directories:0 |
|
||||
| Auto complete | | `a` | 7.2 ms | items: 10 |
|
||||
*Measurements run 2 times and an average was calculated.
|
||||
|
||||
|
||||
## PiGallery2 v1.5.8, 26.01.2019
|
||||
|
||||
**System**: Intel(R) Core(TM) i7-6700HQ CPU @ 2.60GHz, 16GB Ram, SHDD: 1TB, 5400 rpm
|
||||
|
19
benchmark/docker-compose/docker-compose.yml
Normal file
19
benchmark/docker-compose/docker-compose.yml
Normal file
@ -0,0 +1,19 @@
|
||||
version: '3'
|
||||
services:
|
||||
pigallery2:
|
||||
entrypoint: [ "node", "./benchmark/index", "--config-path=/app/data/config/config.json", "--bm-config-path=/app/data/config/bm_config.json" ]
|
||||
image: bpatrik/pigallery2:latest
|
||||
container_name: pigallery2
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
volumes:
|
||||
- "./pigallery2/benchmark_config:/app/data/config" # CHANGE ME
|
||||
- "db-benchmark-data:/app/data/db"
|
||||
- "./pigallery2/images:/app/data/images" # CHANGE ME
|
||||
- "./pigallery2/tmp:/app/data/tmp" # CHANGE ME
|
||||
expose:
|
||||
- "80"
|
||||
restart: always
|
||||
|
||||
volumes:
|
||||
db-benchmark-data:
|
@ -1,18 +1,17 @@
|
||||
import {Config} from '../src/common/config/private/Config';
|
||||
import * as path from 'path';
|
||||
import {ProjectPath} from '../src/backend/ProjectPath';
|
||||
import {BenchmarkResult, Benchmarks} from './Benchmarks';
|
||||
import {BenchmarkResult, BenchmarkRunner} from './BenchmarkRunner';
|
||||
import {SearchTypes} from '../src/common/entities/AutoCompleteItem';
|
||||
import {Utils} from '../src/common/Utils';
|
||||
import {DiskMangerWorker} from '../src/backend/model/threading/DiskMangerWorker';
|
||||
import {BMConfig} from './BMConfig';
|
||||
import {GalleryManager} from '../src/backend/model/database/sql/GalleryManager';
|
||||
import {PersonManager} from '../src/backend/model/database/sql/PersonManager';
|
||||
|
||||
|
||||
Config.Server.Media.folder = BMConfig.path;
|
||||
const dbFolder = path.join(__dirname, './../');
|
||||
ProjectPath.reset();
|
||||
const RUNS = 50;
|
||||
const RUNS = BMConfig.RUNS;
|
||||
|
||||
let resultsText = '';
|
||||
const printLine = (text: string) => {
|
||||
@ -28,54 +27,75 @@ const printHeader = async () => {
|
||||
printLine('**System**: ' + BMConfig.system);
|
||||
const dir = await DiskMangerWorker.scanDirectory('./');
|
||||
const gm = new GalleryManager();
|
||||
printLine('**Gallery**: directories: ' +
|
||||
const pm = new PersonManager();
|
||||
printLine('\n**Gallery**: directories: ' +
|
||||
dir.directories.length +
|
||||
' media: ' + dir.media.length +
|
||||
// @ts-ignore
|
||||
', faces: ' + dir.media.reduce((p, c) => p + (c.metadata.faces || []).length, 0));
|
||||
', all persons: ' + dir.media.reduce((p, c) => p + (c.metadata.faces || []).length, 0) +
|
||||
',unique persons (faces): ' + (await pm.getAll()).length + '\n');
|
||||
};
|
||||
|
||||
|
||||
const printTableHeader = () => {
|
||||
printLine('| action | action details | average time | details |');
|
||||
printLine('|:------:|:--------------:|:------------:|:-------:|');
|
||||
printLine('| Action | Sub action | Action details | Average Duration | Details |');
|
||||
printLine('|:------:|:----------:|:--------------:|:----------------:|:-------:|');
|
||||
};
|
||||
const printResult = (result: BenchmarkResult, action: string, actionDetails: string = '') => {
|
||||
console.log('benchmarked: ' + action);
|
||||
const printResult = (result: BenchmarkResult, actionDetails: string = '', isSubResult = false) => {
|
||||
console.log('benchmarked: ' + result.name);
|
||||
let details = '-';
|
||||
if (result.items) {
|
||||
details = 'items: ' + result.items;
|
||||
}
|
||||
if (result.media) {
|
||||
details = 'media: ' + result.media + ', directories:' + result.directories;
|
||||
if (result.contentWrapper) {
|
||||
if (result.contentWrapper.directory) {
|
||||
details = 'media: ' + result.contentWrapper.directory.media.length +
|
||||
', directories:' + result.contentWrapper.directory.directories.length;
|
||||
} else {
|
||||
details = 'media: ' + result.contentWrapper.searchResult.media.length +
|
||||
', directories:' + result.contentWrapper.searchResult.directories.length;
|
||||
}
|
||||
}
|
||||
if (isSubResult) {
|
||||
printLine('| | ' + result.name + ' | ' + actionDetails +
|
||||
' | ' + (result.duration).toFixed(1) + ' ms | ' + details + ' |');
|
||||
} else {
|
||||
printLine('| **' + result.name + '** | | ' + actionDetails +
|
||||
' | ' + (result.duration).toFixed(1) + ' ms | ' + details + ' |');
|
||||
}
|
||||
if (result.subBenchmarks && result.subBenchmarks.length > 1) {
|
||||
for (let i = 0; i < result.subBenchmarks.length; i++) {
|
||||
printResult(result.subBenchmarks[i], '', true);
|
||||
}
|
||||
}
|
||||
printLine('| ' + action + ' | ' + actionDetails +
|
||||
' | ' + (result.duration).toFixed(1) + 'ms | ' + details + ' |');
|
||||
};
|
||||
|
||||
const run = async () => {
|
||||
console.log('Running, RUNS:' + RUNS);
|
||||
const start = Date.now();
|
||||
const bm = new Benchmarks(RUNS);
|
||||
const bm = new BenchmarkRunner(RUNS);
|
||||
|
||||
// header
|
||||
await printHeader();
|
||||
printTableHeader();
|
||||
printResult(await bm.bmScanDirectory(), 'Scanning directory');
|
||||
printResult(await bm.bmSaveDirectory(), 'Saving directory');
|
||||
printResult(await bm.bmListDirectory(), 'Listing Directory');
|
||||
printResult(await bm.bmListPersons(), 'Listing Faces');
|
||||
|
||||
printResult(await bm.bmScanDirectory());
|
||||
printResult(await bm.bmSaveDirectory());
|
||||
printResult(await bm.bmListDirectory());
|
||||
printResult(await bm.bmListPersons());
|
||||
(await bm.bmAllSearch('a')).forEach(res => {
|
||||
if (res.searchType !== null) {
|
||||
printResult(res.result, 'searching', '`a` as `' + SearchTypes[res.searchType] + '`');
|
||||
printResult(res.result, '`a` as `' + SearchTypes[res.searchType] + '`');
|
||||
} else {
|
||||
printResult(res.result, 'searching', '`a` as `any`');
|
||||
printResult(res.result, '`a` as `any`');
|
||||
}
|
||||
});
|
||||
printResult(await bm.bmInstantSearch('a'), 'instant search', '`a`');
|
||||
printResult(await bm.bmAutocomplete('a'), 'auto complete', '`a`');
|
||||
printResult(await bm.bmInstantSearch('a'), '`a`');
|
||||
printResult(await bm.bmAutocomplete('a'), '`a`');
|
||||
printLine('*Measurements run ' + RUNS + ' times and an average was calculated.');
|
||||
console.log(resultsText);
|
||||
console.log('run for : ' + ((Date.now() - start)).toFixed(1) + 'ms');
|
||||
};
|
||||
|
||||
run();
|
||||
run().then(console.log).catch(console.error);
|
||||
|
||||
|
@ -38,7 +38,8 @@ const getSwitch = (name: string, def: string = null): string => {
|
||||
gulp.task('build-backend', function () {
|
||||
return gulp.src([
|
||||
'src/common/**/*.ts',
|
||||
'src/backend/**/*.ts'], {base: '.'})
|
||||
'src/backend/**/*.ts',
|
||||
'benchmark/**/*.ts'], {base: '.'})
|
||||
.pipe(tsBackendProject())
|
||||
.js
|
||||
.pipe(gulp.dest('./release'));
|
||||
|
@ -1,6 +1,9 @@
|
||||
import {DirectoryDTO} from '../../../../common/entities/DirectoryDTO';
|
||||
|
||||
export interface IIndexingManager {
|
||||
SavingReady: Promise<void>
|
||||
IsSavingInProgress: boolean;
|
||||
|
||||
indexDirectory(relativeDirectoryName: string): Promise<DirectoryDTO>;
|
||||
|
||||
resetDB(): Promise<void>;
|
||||
|
@ -22,9 +22,15 @@ const LOG_TAG = '[IndexingManager]';
|
||||
|
||||
export class IndexingManager implements IIndexingManager {
|
||||
|
||||
SavingReady: Promise<void> = null;
|
||||
SavingReadyPR: () => void = null;
|
||||
private savingQueue: DirectoryDTO[] = [];
|
||||
private isSaving = false;
|
||||
|
||||
get IsSavingInProgress() {
|
||||
return this.SavingReady !== null;
|
||||
}
|
||||
|
||||
public indexDirectory(relativeDirectoryName: string): Promise<DirectoryDTO> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
@ -67,10 +73,19 @@ export class IndexingManager implements IIndexingManager {
|
||||
return;
|
||||
}
|
||||
this.savingQueue.push(scannedDirectory);
|
||||
if (!this.SavingReady) {
|
||||
this.SavingReady = new Promise<void>((resolve) => {
|
||||
this.SavingReadyPR = resolve;
|
||||
});
|
||||
}
|
||||
while (this.isSaving === false && this.savingQueue.length > 0) {
|
||||
await this.saveToDB(this.savingQueue[0]);
|
||||
this.savingQueue.shift();
|
||||
}
|
||||
if (this.savingQueue.length === 0) {
|
||||
this.SavingReady = null;
|
||||
this.SavingReadyPR();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -106,6 +106,7 @@ export class SQLConnection {
|
||||
this.connection = null;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error during closing sql db:');
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,9 @@ export class IndexingJob extends Job {
|
||||
|
||||
protected async step(): Promise<boolean> {
|
||||
if (this.directoriesToIndex.length === 0) {
|
||||
if (ObjectManagers.getInstance().IndexingManager.IsSavingInProgress) {
|
||||
await ObjectManagers.getInstance().IndexingManager.SavingReady;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
const directory = this.directoriesToIndex.shift();
|
||||
|
Loading…
Reference in New Issue
Block a user