/*
 * Copyright 2022-2024, ttcoder
 * All rights reserved. Distributed under the terms of the MIT license.
 */

// USAGE:
//
//		jam hog-bench.emu1
//		jam hog-bench.emu6


// base.a
#include <base/log.h>

// libc.lib.so
#include <math.h>
#include <stdlib.h>

// stdcxx.lib.so
#include <string>

// haiku.lib.so
#include <OS.h>



//#define COMPARE( elapsed_expr, wanted, label )  {
//	float elapsed = (elapsed_expr);
//	float dif = fabs(elapsed - (wanted));
//	float ten_percent = (wanted)*0.1;
//	if( dif > ten_percent )

#define COMPARE( elapsed_expr, wanted, label )  { \
	float elapsed = (elapsed_expr); \
	if( elapsed > wanted ) \
		Genode::log( "\033[35m", " ** FAIL: ", label, ": elapsed ", elapsed, " higher than ceiling ", wanted, "\033[0m " ); /*purple tint (col #35)*/ \
	else Genode::log( "    Pass: ", label, " (", elapsed, " < ", wanted, ")" ); \
}

#define ASSERT( val ) { if(! (val)) { Genode::error( "Assert failed: ", #val ); exit(0); } }


#include <support/Locker.h>

static void benchKernel()
{
	// find_thread()
	{
		// warm-up with a "dry run" call (seems to make a difference in Qemu ?)
		find_thread( NULL );
		
		bigtime_t start = system_time();
		
		for( int i = 0; i < 1000; i++ )
		{
			find_thread( NULL );
			//if( i%100 == 0 ) printf("%ld\n", system_time()-start);
		}
		
		COMPARE( system_time() - start, 6*1000, "find_thread(NULL) x 1000" );
	}
	
	// BLocker: atomic_add() and create_sem() etc
	{
		bigtime_t start = system_time();
		
		for( int i = 0; i < 1000; i++ )
		{
			BLocker lock_unlock;
		}
		
		COMPARE( system_time() - start, 31*1000, "BLocker ctor/dtor x 1000" );
		///later: 0.05 seconds for a create_sem/delete_sem pair seems high, even if it's in Qemu
	}
}



#include <dirent.h>
#include <storage/Node.h>

static void benchStorage()
{
#if 0
	also bench readdir(), not just opendir() ?
			DIR * dir = opendir( "/" );
			ASSERT( dir != NULL );
			printf("opendir -> %p\n", dir);
			
			while (dirent *entry = readdir(dir)) {
				printf("     << %s >> \n", entry->d_name);
			}
			
			closedir( dir );
#endif
	// LibC stat()
	{
		struct stat dummy = {};
		
		// T0
		bigtime_t start = system_time();
		
		for( int i = 0; i < 1000; i++ )
		{
			stat( "/", &dummy );
		}
		
		COMPARE( system_time() - start, 259*1000, "stat( '/' ) x 1000" );
	}
	
	// Haiku's normalize_dir_path()
	// (NOTE: remove the "static" qualifier in fs.cpp to make normalize_dir_path() available here)
#if 0
	extern status_t normalize_dir_path(const char *path, std::string &normalizedPath);
	
	{
		// these poor results here, from libroot-build, are critical, and probably
		// explain why even navigating a BFilePanel is painful (see e.g. tts-distro/AK)
		//UPD: cherry-picked an upstream optimization a few months back, no longer need to benchmark this!
		
		std::string s;
		
		// T0 ("/dev")
		//
		bigtime_t start = system_time();
		
		for( int i = 0; i < 1000; i++ )
		{
			s = "";
			ASSERT( B_OK == normalize_dir_path("/dev", s) );  // in fact, just adding a "/" (as in "/dev/") goes from 600 ms to 6000 ms, ten times longer !
			ASSERT( s == "/dev" );
		}
		
		COMPARE( system_time() - start, 610*1000, "normalize_dir_path( '/dev' ) x 1000" );
		
		/*********/
		
		// T0 ("/dev/./null") -- takes 15+ times (!) longer for some reason
		//
		start = system_time();
		
		for( int i = 0; i < 1000; i++ )
		{
			s = "";
			ASSERT( B_OK == normalize_dir_path("/dev/./null", s) );
			ASSERT( s == "/dev/null" );
		}
		
		COMPARE( system_time() - start, 11000*1000, "normalize_dir_path( '/dev/./null' ) x 1000" );
	}
#endif
	
	// Haiku's stat() aka _kern_read_stat()
	{
		struct stat dummy = {};
		BNode node( "/" );
		ASSERT( B_OK == node.InitCheck());
		
		// T0
		bigtime_t start = system_time();
		
		for( int i = 0; i < 1000; i++ )
		{
			node.GetStat( &dummy );
		}
		
		COMPARE( system_time() - start, 258*1000, "GetStat( '/' ) x 1000" );
	}
	
	// LibC opendir()/closedir()
	{
		// T0
		bigtime_t start = system_time();
		
		for( int i = 0; i < 1000; i++ )
		{
			DIR * dir = opendir( "/" );
			ASSERT( dir != NULL );
			
			closedir( dir );
		}
		
		COMPARE( system_time() - start, 2890*1000, "opendir/closedir( '/' ) x 1000" );
	}
	
	// BNode, _kern_open()
	{
		// T0
		bigtime_t start = system_time();
		
		for( int i = 0; i < 1000; i++ )
		{
			BNode node( "/" );
			ASSERT( B_OK == node.InitCheck() );
		}
		
		COMPARE( system_time() - start, 1710*1000, "BNode( '/' ) x 1000" );
	}
///ToDo: well that is slow... One possible lead : save on calls to libc and vfs (cache previous results)...
}



int main()
{
	Genode::log( "------- benchmarks main() -------" );
	
	benchKernel();
	benchStorage();
	
	Genode::log( "------- END - benchmarks complete - END -------" );
	
	return 0;
}

