expo-benchmark 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +5 -0
- package/README.md +35 -0
- package/android/build.gradle +48 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/expo/modules/benchmark/ExpoBenchmarkModule.kt +125 -0
- package/build/ExpoBenchmark.types.d.ts +47 -0
- package/build/ExpoBenchmark.types.d.ts.map +1 -0
- package/build/ExpoBenchmark.types.js +2 -0
- package/build/ExpoBenchmark.types.js.map +1 -0
- package/build/ExpoBenchmarkModule.d.ts +28 -0
- package/build/ExpoBenchmarkModule.d.ts.map +1 -0
- package/build/ExpoBenchmarkModule.js +4 -0
- package/build/ExpoBenchmarkModule.js.map +1 -0
- package/build/index.d.ts +11 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +11 -0
- package/build/index.js.map +1 -0
- package/expo-module.config.json +9 -0
- package/ios/ExpoBenchmark.podspec +29 -0
- package/ios/ExpoBenchmarkModule.swift +150 -0
- package/package.json +43 -0
- package/src/ExpoBenchmark.types.ts +49 -0
- package/src/ExpoBenchmarkModule.ts +37 -0
- package/src/index.ts +20 -0
- package/tsconfig.json +9 -0
package/.eslintrc.js
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# expo-benchmark
|
|
2
|
+
|
|
3
|
+
My new module
|
|
4
|
+
|
|
5
|
+
# API documentation
|
|
6
|
+
|
|
7
|
+
- [Documentation for the latest stable release](https://docs.expo.dev/versions/latest/sdk/benchmark/)
|
|
8
|
+
- [Documentation for the main branch](https://docs.expo.dev/versions/unversioned/sdk/benchmark/)
|
|
9
|
+
|
|
10
|
+
# Installation in managed Expo projects
|
|
11
|
+
|
|
12
|
+
For [managed](https://docs.expo.dev/archive/managed-vs-bare/) Expo projects, please follow the installation instructions in the [API documentation for the latest stable release](#api-documentation). If you follow the link and there is no documentation available then this library is not yet usable within managed projects — it is likely to be included in an upcoming Expo SDK release.
|
|
13
|
+
|
|
14
|
+
# Installation in bare React Native projects
|
|
15
|
+
|
|
16
|
+
For bare React Native projects, you must ensure that you have [installed and configured the `expo` package](https://docs.expo.dev/bare/installing-expo-modules/) before continuing.
|
|
17
|
+
|
|
18
|
+
### Add the package to your npm dependencies
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
npm install expo-benchmark
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Configure for Android
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
### Configure for iOS
|
|
30
|
+
|
|
31
|
+
Run `npx pod-install` after installing the npm package.
|
|
32
|
+
|
|
33
|
+
# Contributing
|
|
34
|
+
|
|
35
|
+
Contributions are very welcome! Please refer to guidelines described in the [contributing guide]( https://github.com/expo/expo#contributing).
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
apply plugin: 'com.android.library'
|
|
2
|
+
|
|
3
|
+
group = 'expo.modules.benchmark'
|
|
4
|
+
version = '0.1.0'
|
|
5
|
+
|
|
6
|
+
def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
|
|
7
|
+
apply from: expoModulesCorePlugin
|
|
8
|
+
applyKotlinExpoModulesCorePlugin()
|
|
9
|
+
useCoreDependencies()
|
|
10
|
+
useExpoPublishing()
|
|
11
|
+
|
|
12
|
+
// If you want to use the managed Android SDK versions from expo-modules-core, set this to true.
|
|
13
|
+
// The Android SDK versions will be bumped from time to time in SDK releases and may introduce breaking changes in your module code.
|
|
14
|
+
// Most of the time, you may like to manage the Android SDK versions yourself.
|
|
15
|
+
def useManagedAndroidSdkVersions = false
|
|
16
|
+
if (useManagedAndroidSdkVersions) {
|
|
17
|
+
useDefaultAndroidSdkVersions()
|
|
18
|
+
} else {
|
|
19
|
+
buildscript {
|
|
20
|
+
// Simple helper that allows the root project to override versions declared by this library.
|
|
21
|
+
ext.safeExtGet = { prop, fallback ->
|
|
22
|
+
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
project.android {
|
|
26
|
+
compileSdkVersion safeExtGet("compileSdkVersion", 36)
|
|
27
|
+
defaultConfig {
|
|
28
|
+
minSdkVersion safeExtGet("minSdkVersion", 24)
|
|
29
|
+
targetSdkVersion safeExtGet("targetSdkVersion", 36)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
android {
|
|
35
|
+
namespace "expo.modules.benchmark"
|
|
36
|
+
defaultConfig {
|
|
37
|
+
versionCode 1
|
|
38
|
+
versionName "0.1.0"
|
|
39
|
+
}
|
|
40
|
+
lintOptions {
|
|
41
|
+
abortOnError false
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
dependencies {
|
|
46
|
+
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3"
|
|
47
|
+
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3"
|
|
48
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
package expo.modules.benchmark
|
|
2
|
+
|
|
3
|
+
import expo.modules.kotlin.modules.Module
|
|
4
|
+
import expo.modules.kotlin.modules.ModuleDefinition
|
|
5
|
+
import kotlinx.coroutines.*
|
|
6
|
+
import java.security.MessageDigest
|
|
7
|
+
import java.util.concurrent.atomic.AtomicLong
|
|
8
|
+
import kotlin.random.Random
|
|
9
|
+
|
|
10
|
+
class ExpoBenchmarkModule : Module() {
|
|
11
|
+
override fun definition() = ModuleDefinition {
|
|
12
|
+
Name("ExpoBenchmark")
|
|
13
|
+
|
|
14
|
+
// Events for progress updates
|
|
15
|
+
Events("onProgress", "onComplete")
|
|
16
|
+
|
|
17
|
+
// Get the number of CPU cores
|
|
18
|
+
Function("getCpuCoreCount") {
|
|
19
|
+
Runtime.getRuntime().availableProcessors()
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Run a configurable benchmark
|
|
23
|
+
AsyncFunction("runBenchmarkAsync") { options: Map<String, Any>? ->
|
|
24
|
+
val durationSeconds = (options?.get("durationSeconds") as? Number)?.toDouble() ?: 5.0
|
|
25
|
+
val threads = (options?.get("threads") as? Number)?.toInt() ?: 1
|
|
26
|
+
val actualThreads = if (threads == 0) Runtime.getRuntime().availableProcessors() else threads
|
|
27
|
+
|
|
28
|
+
runBenchmark(durationSeconds, actualThreads)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Quick single-threaded benchmark
|
|
32
|
+
AsyncFunction("runQuickBenchmarkAsync") {
|
|
33
|
+
runBenchmark(1.0, 1)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Multi-threaded benchmark using all cores
|
|
37
|
+
AsyncFunction("runMultiThreadedBenchmarkAsync") { durationSeconds: Double? ->
|
|
38
|
+
val duration = durationSeconds ?: 5.0
|
|
39
|
+
val threads = Runtime.getRuntime().availableProcessors()
|
|
40
|
+
runBenchmark(duration, threads)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
private suspend fun runBenchmark(durationSeconds: Double, threads: Int): Map<String, Any> {
|
|
45
|
+
val durationMs = (durationSeconds * 1000).toLong()
|
|
46
|
+
val startTime = System.currentTimeMillis()
|
|
47
|
+
val endTime = startTime + durationMs
|
|
48
|
+
|
|
49
|
+
val totalHashes: Long
|
|
50
|
+
|
|
51
|
+
if (threads == 1) {
|
|
52
|
+
// Single-threaded benchmark
|
|
53
|
+
totalHashes = withContext(Dispatchers.Default) {
|
|
54
|
+
var hashCount = 0L
|
|
55
|
+
var data = ByteArray(32)
|
|
56
|
+
Random.nextBytes(data)
|
|
57
|
+
|
|
58
|
+
val digest = MessageDigest.getInstance("SHA-256")
|
|
59
|
+
|
|
60
|
+
while (System.currentTimeMillis() < endTime) {
|
|
61
|
+
data = digest.digest(data)
|
|
62
|
+
hashCount++
|
|
63
|
+
|
|
64
|
+
// Report progress every 100000 hashes
|
|
65
|
+
if (hashCount % 100000 == 0L) {
|
|
66
|
+
val elapsed = System.currentTimeMillis() - startTime
|
|
67
|
+
val currentHashrate = hashCount.toDouble() / (elapsed.toDouble() / 1000)
|
|
68
|
+
|
|
69
|
+
sendEvent("onProgress", mapOf(
|
|
70
|
+
"currentHashCount" to hashCount,
|
|
71
|
+
"elapsedMs" to elapsed,
|
|
72
|
+
"currentHashrate" to currentHashrate
|
|
73
|
+
))
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
hashCount
|
|
78
|
+
}
|
|
79
|
+
} else {
|
|
80
|
+
// Multi-threaded benchmark
|
|
81
|
+
totalHashes = withContext(Dispatchers.Default) {
|
|
82
|
+
val hashCounts = Array(threads) { AtomicLong(0) }
|
|
83
|
+
|
|
84
|
+
val jobs = (0 until threads).map { threadIndex ->
|
|
85
|
+
async {
|
|
86
|
+
var hashCount = 0L
|
|
87
|
+
var data = ByteArray(32)
|
|
88
|
+
Random.nextBytes(data)
|
|
89
|
+
|
|
90
|
+
val digest = MessageDigest.getInstance("SHA-256")
|
|
91
|
+
|
|
92
|
+
while (System.currentTimeMillis() < endTime) {
|
|
93
|
+
data = digest.digest(data)
|
|
94
|
+
hashCount++
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
hashCounts[threadIndex].set(hashCount)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
jobs.awaitAll()
|
|
102
|
+
|
|
103
|
+
hashCounts.sumOf { it.get() }
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
val actualEndTime = System.currentTimeMillis()
|
|
108
|
+
val actualDurationMs = (actualEndTime - startTime).toDouble()
|
|
109
|
+
val hashesPerSecond = totalHashes.toDouble() / (actualDurationMs / 1000)
|
|
110
|
+
|
|
111
|
+
val result = mapOf(
|
|
112
|
+
"hashCount" to totalHashes,
|
|
113
|
+
"durationMs" to actualDurationMs,
|
|
114
|
+
"hashesPerSecond" to hashesPerSecond,
|
|
115
|
+
"kiloHashesPerSecond" to hashesPerSecond / 1000,
|
|
116
|
+
"megaHashesPerSecond" to hashesPerSecond / 1_000_000,
|
|
117
|
+
"threads" to threads,
|
|
118
|
+
"algorithm" to "SHA-256"
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
sendEvent("onComplete", result)
|
|
122
|
+
|
|
123
|
+
return result
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Result of a hashrate benchmark
|
|
3
|
+
*/
|
|
4
|
+
export type BenchmarkResult = {
|
|
5
|
+
/** Total number of hashes computed */
|
|
6
|
+
hashCount: number;
|
|
7
|
+
/** Duration of the benchmark in milliseconds */
|
|
8
|
+
durationMs: number;
|
|
9
|
+
/** Hashrate in hashes per second */
|
|
10
|
+
hashesPerSecond: number;
|
|
11
|
+
/** Hashrate in kilohashes per second */
|
|
12
|
+
kiloHashesPerSecond: number;
|
|
13
|
+
/** Hashrate in megahashes per second */
|
|
14
|
+
megaHashesPerSecond: number;
|
|
15
|
+
/** Number of threads used */
|
|
16
|
+
threads: number;
|
|
17
|
+
/** Algorithm used for hashing */
|
|
18
|
+
algorithm: string;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Options for running a benchmark
|
|
22
|
+
*/
|
|
23
|
+
export type BenchmarkOptions = {
|
|
24
|
+
/** Duration to run the benchmark in seconds (default: 5) */
|
|
25
|
+
durationSeconds?: number;
|
|
26
|
+
/** Number of threads to use (default: 1, use 0 for auto/max cores) */
|
|
27
|
+
threads?: number;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Progress event payload during benchmark
|
|
31
|
+
*/
|
|
32
|
+
export type BenchmarkProgressPayload = {
|
|
33
|
+
/** Current hash count */
|
|
34
|
+
currentHashCount: number;
|
|
35
|
+
/** Elapsed time in milliseconds */
|
|
36
|
+
elapsedMs: number;
|
|
37
|
+
/** Current hashrate */
|
|
38
|
+
currentHashrate: number;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Events emitted by the benchmark module
|
|
42
|
+
*/
|
|
43
|
+
export type ExpoBenchmarkModuleEvents = {
|
|
44
|
+
onProgress: (params: BenchmarkProgressPayload) => void;
|
|
45
|
+
onComplete: (params: BenchmarkResult) => void;
|
|
46
|
+
};
|
|
47
|
+
//# sourceMappingURL=ExpoBenchmark.types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExpoBenchmark.types.d.ts","sourceRoot":"","sources":["../src/ExpoBenchmark.types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,sCAAsC;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,gDAAgD;IAChD,UAAU,EAAE,MAAM,CAAC;IACnB,oCAAoC;IACpC,eAAe,EAAE,MAAM,CAAC;IACxB,wCAAwC;IACxC,mBAAmB,EAAE,MAAM,CAAC;IAC5B,wCAAwC;IACxC,mBAAmB,EAAE,MAAM,CAAC;IAC5B,6BAA6B;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,iCAAiC;IACjC,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,4DAA4D;IAC5D,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,wBAAwB,GAAG;IACrC,yBAAyB;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,mCAAmC;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,uBAAuB;IACvB,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,yBAAyB,GAAG;IACtC,UAAU,EAAE,CAAC,MAAM,EAAE,wBAAwB,KAAK,IAAI,CAAC;IACvD,UAAU,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,CAAC;CAC/C,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExpoBenchmark.types.js","sourceRoot":"","sources":["../src/ExpoBenchmark.types.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Result of a hashrate benchmark\n */\nexport type BenchmarkResult = {\n /** Total number of hashes computed */\n hashCount: number;\n /** Duration of the benchmark in milliseconds */\n durationMs: number;\n /** Hashrate in hashes per second */\n hashesPerSecond: number;\n /** Hashrate in kilohashes per second */\n kiloHashesPerSecond: number;\n /** Hashrate in megahashes per second */\n megaHashesPerSecond: number;\n /** Number of threads used */\n threads: number;\n /** Algorithm used for hashing */\n algorithm: string;\n};\n\n/**\n * Options for running a benchmark\n */\nexport type BenchmarkOptions = {\n /** Duration to run the benchmark in seconds (default: 5) */\n durationSeconds?: number;\n /** Number of threads to use (default: 1, use 0 for auto/max cores) */\n threads?: number;\n};\n\n/**\n * Progress event payload during benchmark\n */\nexport type BenchmarkProgressPayload = {\n /** Current hash count */\n currentHashCount: number;\n /** Elapsed time in milliseconds */\n elapsedMs: number;\n /** Current hashrate */\n currentHashrate: number;\n};\n\n/**\n * Events emitted by the benchmark module\n */\nexport type ExpoBenchmarkModuleEvents = {\n onProgress: (params: BenchmarkProgressPayload) => void;\n onComplete: (params: BenchmarkResult) => void;\n};\n"]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { NativeModule } from 'expo';
|
|
2
|
+
import { ExpoBenchmarkModuleEvents, BenchmarkResult, BenchmarkOptions } from './ExpoBenchmark.types';
|
|
3
|
+
declare class ExpoBenchmarkModule extends NativeModule<ExpoBenchmarkModuleEvents> {
|
|
4
|
+
/**
|
|
5
|
+
* Get the number of CPU cores available on the device
|
|
6
|
+
*/
|
|
7
|
+
getCpuCoreCount(): number;
|
|
8
|
+
/**
|
|
9
|
+
* Run a SHA-256 hashrate benchmark
|
|
10
|
+
* @param options - Benchmark options (duration, threads)
|
|
11
|
+
* @returns Promise with benchmark results
|
|
12
|
+
*/
|
|
13
|
+
runBenchmarkAsync(options?: BenchmarkOptions): Promise<BenchmarkResult>;
|
|
14
|
+
/**
|
|
15
|
+
* Run a quick single-threaded benchmark (1 second)
|
|
16
|
+
* @returns Promise with benchmark results
|
|
17
|
+
*/
|
|
18
|
+
runQuickBenchmarkAsync(): Promise<BenchmarkResult>;
|
|
19
|
+
/**
|
|
20
|
+
* Run a multi-threaded benchmark using all available cores
|
|
21
|
+
* @param durationSeconds - Duration in seconds (default: 5)
|
|
22
|
+
* @returns Promise with benchmark results
|
|
23
|
+
*/
|
|
24
|
+
runMultiThreadedBenchmarkAsync(durationSeconds?: number): Promise<BenchmarkResult>;
|
|
25
|
+
}
|
|
26
|
+
declare const _default: ExpoBenchmarkModule;
|
|
27
|
+
export default _default;
|
|
28
|
+
//# sourceMappingURL=ExpoBenchmarkModule.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExpoBenchmarkModule.d.ts","sourceRoot":"","sources":["../src/ExpoBenchmarkModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,MAAM,MAAM,CAAC;AAEzD,OAAO,EACL,yBAAyB,EACzB,eAAe,EACf,gBAAgB,EACjB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,OAAO,mBAAoB,SAAQ,YAAY,CAAC,yBAAyB,CAAC;IAC/E;;OAEG;IACH,eAAe,IAAI,MAAM;IAEzB;;;;OAIG;IACH,iBAAiB,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC;IAEvE;;;OAGG;IACH,sBAAsB,IAAI,OAAO,CAAC,eAAe,CAAC;IAElD;;;;OAIG;IACH,8BAA8B,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;CACnF;;AAGD,wBAAyE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExpoBenchmarkModule.js","sourceRoot":"","sources":["../src/ExpoBenchmarkModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAE,MAAM,MAAM,CAAC;AAmCzD,yDAAyD;AACzD,eAAe,mBAAmB,CAAsB,eAAe,CAAC,CAAC","sourcesContent":["import { NativeModule, requireNativeModule } from 'expo';\n\nimport {\n ExpoBenchmarkModuleEvents,\n BenchmarkResult,\n BenchmarkOptions,\n} from './ExpoBenchmark.types';\n\ndeclare class ExpoBenchmarkModule extends NativeModule<ExpoBenchmarkModuleEvents> {\n /**\n * Get the number of CPU cores available on the device\n */\n getCpuCoreCount(): number;\n\n /**\n * Run a SHA-256 hashrate benchmark\n * @param options - Benchmark options (duration, threads)\n * @returns Promise with benchmark results\n */\n runBenchmarkAsync(options?: BenchmarkOptions): Promise<BenchmarkResult>;\n\n /**\n * Run a quick single-threaded benchmark (1 second)\n * @returns Promise with benchmark results\n */\n runQuickBenchmarkAsync(): Promise<BenchmarkResult>;\n\n /**\n * Run a multi-threaded benchmark using all available cores\n * @param durationSeconds - Duration in seconds (default: 5)\n * @returns Promise with benchmark results\n */\n runMultiThreadedBenchmarkAsync(durationSeconds?: number): Promise<BenchmarkResult>;\n}\n\n// This call loads the native module object from the JSI.\nexport default requireNativeModule<ExpoBenchmarkModule>('ExpoBenchmark');\n"]}
|
package/build/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import ExpoBenchmarkModule from './ExpoBenchmarkModule';
|
|
2
|
+
export * from './ExpoBenchmark.types';
|
|
3
|
+
export default ExpoBenchmarkModule;
|
|
4
|
+
export declare const getCpuCoreCount: () => number;
|
|
5
|
+
export declare const runBenchmark: (options?: {
|
|
6
|
+
durationSeconds?: number;
|
|
7
|
+
threads?: number;
|
|
8
|
+
}) => Promise<import("./ExpoBenchmark.types").BenchmarkResult>;
|
|
9
|
+
export declare const runQuickBenchmark: () => Promise<import("./ExpoBenchmark.types").BenchmarkResult>;
|
|
10
|
+
export declare const runMultiThreadedBenchmark: (durationSeconds?: number) => Promise<import("./ExpoBenchmark.types").BenchmarkResult>;
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,mBAAmB,MAAM,uBAAuB,CAAC;AAGxD,cAAc,uBAAuB,CAAC;AAGtC,eAAe,mBAAmB,CAAC;AAGnC,eAAO,MAAM,eAAe,cAA8C,CAAC;AAE3E,eAAO,MAAM,YAAY,GAAI,UAAU;IACrC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,6DAAmD,CAAC;AAErD,eAAO,MAAM,iBAAiB,gEAAqD,CAAC;AAEpF,eAAO,MAAM,yBAAyB,GAAI,kBAAkB,MAAM,6DACG,CAAC"}
|
package/build/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import ExpoBenchmarkModule from './ExpoBenchmarkModule';
|
|
2
|
+
// Export types
|
|
3
|
+
export * from './ExpoBenchmark.types';
|
|
4
|
+
// Export the native module
|
|
5
|
+
export default ExpoBenchmarkModule;
|
|
6
|
+
// Convenience functions
|
|
7
|
+
export const getCpuCoreCount = () => ExpoBenchmarkModule.getCpuCoreCount();
|
|
8
|
+
export const runBenchmark = (options) => ExpoBenchmarkModule.runBenchmarkAsync(options);
|
|
9
|
+
export const runQuickBenchmark = () => ExpoBenchmarkModule.runQuickBenchmarkAsync();
|
|
10
|
+
export const runMultiThreadedBenchmark = (durationSeconds) => ExpoBenchmarkModule.runMultiThreadedBenchmarkAsync(durationSeconds);
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,mBAAmB,MAAM,uBAAuB,CAAC;AAExD,eAAe;AACf,cAAc,uBAAuB,CAAC;AAEtC,2BAA2B;AAC3B,eAAe,mBAAmB,CAAC;AAEnC,wBAAwB;AACxB,MAAM,CAAC,MAAM,eAAe,GAAG,GAAG,EAAE,CAAC,mBAAmB,CAAC,eAAe,EAAE,CAAC;AAE3E,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,OAG5B,EAAE,EAAE,CAAC,mBAAmB,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;AAErD,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,EAAE,CAAC,mBAAmB,CAAC,sBAAsB,EAAE,CAAC;AAEpF,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,eAAwB,EAAE,EAAE,CACpE,mBAAmB,CAAC,8BAA8B,CAAC,eAAe,CAAC,CAAC","sourcesContent":["import ExpoBenchmarkModule from './ExpoBenchmarkModule';\n\n// Export types\nexport * from './ExpoBenchmark.types';\n\n// Export the native module\nexport default ExpoBenchmarkModule;\n\n// Convenience functions\nexport const getCpuCoreCount = () => ExpoBenchmarkModule.getCpuCoreCount();\n\nexport const runBenchmark = (options?: {\n durationSeconds?: number;\n threads?: number;\n}) => ExpoBenchmarkModule.runBenchmarkAsync(options);\n\nexport const runQuickBenchmark = () => ExpoBenchmarkModule.runQuickBenchmarkAsync();\n\nexport const runMultiThreadedBenchmark = (durationSeconds?: number) =>\n ExpoBenchmarkModule.runMultiThreadedBenchmarkAsync(durationSeconds);\n"]}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
|
|
3
|
+
package = JSON.parse(File.read(File.join(__dir__, '..', 'package.json')))
|
|
4
|
+
|
|
5
|
+
Pod::Spec.new do |s|
|
|
6
|
+
s.name = 'ExpoBenchmark'
|
|
7
|
+
s.version = package['version']
|
|
8
|
+
s.summary = package['description']
|
|
9
|
+
s.description = package['description']
|
|
10
|
+
s.license = package['license']
|
|
11
|
+
s.author = package['author']
|
|
12
|
+
s.homepage = package['homepage']
|
|
13
|
+
s.platforms = {
|
|
14
|
+
:ios => '15.1',
|
|
15
|
+
:tvos => '15.1'
|
|
16
|
+
}
|
|
17
|
+
s.swift_version = '5.9'
|
|
18
|
+
s.source = { git: 'https://github.com/duychip0909/expo-benchmark' }
|
|
19
|
+
s.static_framework = true
|
|
20
|
+
|
|
21
|
+
s.dependency 'ExpoModulesCore'
|
|
22
|
+
|
|
23
|
+
# Swift/Objective-C compatibility
|
|
24
|
+
s.pod_target_xcconfig = {
|
|
25
|
+
'DEFINES_MODULE' => 'YES',
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
s.source_files = "**/*.{h,m,mm,swift,hpp,cpp}"
|
|
29
|
+
end
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import ExpoModulesCore
|
|
2
|
+
import CommonCrypto
|
|
3
|
+
import Foundation
|
|
4
|
+
|
|
5
|
+
public class ExpoBenchmarkModule: Module {
|
|
6
|
+
public func definition() -> ModuleDefinition {
|
|
7
|
+
Name("ExpoBenchmark")
|
|
8
|
+
|
|
9
|
+
// Events for progress updates
|
|
10
|
+
Events("onProgress", "onComplete")
|
|
11
|
+
|
|
12
|
+
// Get the number of CPU cores
|
|
13
|
+
Function("getCpuCoreCount") {
|
|
14
|
+
return ProcessInfo.processInfo.activeProcessorCount
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Run a configurable benchmark
|
|
18
|
+
AsyncFunction("runBenchmarkAsync") { (options: [String: Any]?) -> [String: Any] in
|
|
19
|
+
let durationSeconds = (options?["durationSeconds"] as? Double) ?? 5.0
|
|
20
|
+
let threads = (options?["threads"] as? Int) ?? 1
|
|
21
|
+
let actualThreads = threads == 0 ? ProcessInfo.processInfo.activeProcessorCount : threads
|
|
22
|
+
|
|
23
|
+
return await self.runBenchmark(durationSeconds: durationSeconds, threads: actualThreads)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Quick single-threaded benchmark
|
|
27
|
+
AsyncFunction("runQuickBenchmarkAsync") { () -> [String: Any] in
|
|
28
|
+
return await self.runBenchmark(durationSeconds: 1.0, threads: 1)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Multi-threaded benchmark using all cores
|
|
32
|
+
AsyncFunction("runMultiThreadedBenchmarkAsync") { (durationSeconds: Double?) -> [String: Any] in
|
|
33
|
+
let duration = durationSeconds ?? 5.0
|
|
34
|
+
let threads = ProcessInfo.processInfo.activeProcessorCount
|
|
35
|
+
return await self.runBenchmark(durationSeconds: duration, threads: threads)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
private func runBenchmark(durationSeconds: Double, threads: Int) async -> [String: Any] {
|
|
40
|
+
let durationNs = UInt64(durationSeconds * 1_000_000_000)
|
|
41
|
+
let startTime = DispatchTime.now().uptimeNanoseconds
|
|
42
|
+
let endTime = startTime + durationNs
|
|
43
|
+
|
|
44
|
+
var totalHashes: UInt64 = 0
|
|
45
|
+
|
|
46
|
+
if threads == 1 {
|
|
47
|
+
// Single-threaded benchmark
|
|
48
|
+
totalHashes = await withCheckedContinuation { continuation in
|
|
49
|
+
DispatchQueue.global(qos: .userInitiated).async {
|
|
50
|
+
var hashCount: UInt64 = 0
|
|
51
|
+
var data = Data(count: 32)
|
|
52
|
+
|
|
53
|
+
// Initialize with random data
|
|
54
|
+
for i in 0..<32 {
|
|
55
|
+
data[i] = UInt8.random(in: 0...255)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
while DispatchTime.now().uptimeNanoseconds < endTime {
|
|
59
|
+
// Perform SHA-256 hash
|
|
60
|
+
data = self.sha256(data: data)
|
|
61
|
+
hashCount += 1
|
|
62
|
+
|
|
63
|
+
// Report progress every 100000 hashes
|
|
64
|
+
if hashCount % 100000 == 0 {
|
|
65
|
+
let elapsed = DispatchTime.now().uptimeNanoseconds - startTime
|
|
66
|
+
let elapsedMs = Double(elapsed) / 1_000_000
|
|
67
|
+
let currentHashrate = Double(hashCount) / (elapsedMs / 1000)
|
|
68
|
+
|
|
69
|
+
self.sendEvent("onProgress", [
|
|
70
|
+
"currentHashCount": hashCount,
|
|
71
|
+
"elapsedMs": elapsedMs,
|
|
72
|
+
"currentHashrate": currentHashrate
|
|
73
|
+
])
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
continuation.resume(returning: hashCount)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
} else {
|
|
81
|
+
// Multi-threaded benchmark
|
|
82
|
+
totalHashes = await withCheckedContinuation { continuation in
|
|
83
|
+
DispatchQueue.global(qos: .userInitiated).async {
|
|
84
|
+
let hashCounts = UnsafeMutablePointer<UInt64>.allocate(capacity: threads)
|
|
85
|
+
hashCounts.initialize(repeating: 0, count: threads)
|
|
86
|
+
|
|
87
|
+
let group = DispatchGroup()
|
|
88
|
+
let queue = DispatchQueue(label: "benchmark", attributes: .concurrent)
|
|
89
|
+
|
|
90
|
+
for threadIndex in 0..<threads {
|
|
91
|
+
group.enter()
|
|
92
|
+
queue.async {
|
|
93
|
+
var hashCount: UInt64 = 0
|
|
94
|
+
var data = Data(count: 32)
|
|
95
|
+
|
|
96
|
+
// Initialize with random data for each thread
|
|
97
|
+
for i in 0..<32 {
|
|
98
|
+
data[i] = UInt8.random(in: 0...255)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
while DispatchTime.now().uptimeNanoseconds < endTime {
|
|
102
|
+
data = self.sha256(data: data)
|
|
103
|
+
hashCount += 1
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
hashCounts[threadIndex] = hashCount
|
|
107
|
+
group.leave()
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
group.wait()
|
|
112
|
+
|
|
113
|
+
var total: UInt64 = 0
|
|
114
|
+
for i in 0..<threads {
|
|
115
|
+
total += hashCounts[i]
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
hashCounts.deallocate()
|
|
119
|
+
continuation.resume(returning: total)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
let actualEndTime = DispatchTime.now().uptimeNanoseconds
|
|
125
|
+
let actualDurationMs = Double(actualEndTime - startTime) / 1_000_000
|
|
126
|
+
let hashesPerSecond = Double(totalHashes) / (actualDurationMs / 1000)
|
|
127
|
+
|
|
128
|
+
let result: [String: Any] = [
|
|
129
|
+
"hashCount": totalHashes,
|
|
130
|
+
"durationMs": actualDurationMs,
|
|
131
|
+
"hashesPerSecond": hashesPerSecond,
|
|
132
|
+
"kiloHashesPerSecond": hashesPerSecond / 1000,
|
|
133
|
+
"megaHashesPerSecond": hashesPerSecond / 1_000_000,
|
|
134
|
+
"threads": threads,
|
|
135
|
+
"algorithm": "SHA-256"
|
|
136
|
+
]
|
|
137
|
+
|
|
138
|
+
self.sendEvent("onComplete", result)
|
|
139
|
+
|
|
140
|
+
return result
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
private func sha256(data: Data) -> Data {
|
|
144
|
+
var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
|
|
145
|
+
data.withUnsafeBytes { buffer in
|
|
146
|
+
_ = CC_SHA256(buffer.baseAddress, CC_LONG(buffer.count), &hash)
|
|
147
|
+
}
|
|
148
|
+
return Data(hash)
|
|
149
|
+
}
|
|
150
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "expo-benchmark",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "My new module",
|
|
5
|
+
"main": "build/index.js",
|
|
6
|
+
"types": "build/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "expo-module build",
|
|
9
|
+
"clean": "expo-module clean",
|
|
10
|
+
"lint": "expo-module lint",
|
|
11
|
+
"test": "expo-module test",
|
|
12
|
+
"prepare": "expo-module prepare",
|
|
13
|
+
"prepublishOnly": "expo-module prepublishOnly",
|
|
14
|
+
"expo-module": "expo-module",
|
|
15
|
+
"open:ios": "xed example/ios",
|
|
16
|
+
"open:android": "open -a \"Android Studio\" example/android"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"react-native",
|
|
20
|
+
"expo",
|
|
21
|
+
"expo-benchmark",
|
|
22
|
+
"ExpoBenchmark"
|
|
23
|
+
],
|
|
24
|
+
"repository": "https://github.com/duychip0909/expo-benchmark",
|
|
25
|
+
"bugs": {
|
|
26
|
+
"url": "https://github.com/duychip0909/expo-benchmark/issues"
|
|
27
|
+
},
|
|
28
|
+
"author": "duychip9901 <duychip9901@gmail.com> (https://github.com/duychip0909)",
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"homepage": "https://github.com/duychip0909/expo-benchmark#readme",
|
|
31
|
+
"dependencies": {},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/react": "~19.1.0",
|
|
34
|
+
"expo-module-scripts": "^5.0.8",
|
|
35
|
+
"expo": "^54.0.27",
|
|
36
|
+
"react-native": "0.81.5"
|
|
37
|
+
},
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"expo": "*",
|
|
40
|
+
"react": "*",
|
|
41
|
+
"react-native": "*"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Result of a hashrate benchmark
|
|
3
|
+
*/
|
|
4
|
+
export type BenchmarkResult = {
|
|
5
|
+
/** Total number of hashes computed */
|
|
6
|
+
hashCount: number;
|
|
7
|
+
/** Duration of the benchmark in milliseconds */
|
|
8
|
+
durationMs: number;
|
|
9
|
+
/** Hashrate in hashes per second */
|
|
10
|
+
hashesPerSecond: number;
|
|
11
|
+
/** Hashrate in kilohashes per second */
|
|
12
|
+
kiloHashesPerSecond: number;
|
|
13
|
+
/** Hashrate in megahashes per second */
|
|
14
|
+
megaHashesPerSecond: number;
|
|
15
|
+
/** Number of threads used */
|
|
16
|
+
threads: number;
|
|
17
|
+
/** Algorithm used for hashing */
|
|
18
|
+
algorithm: string;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Options for running a benchmark
|
|
23
|
+
*/
|
|
24
|
+
export type BenchmarkOptions = {
|
|
25
|
+
/** Duration to run the benchmark in seconds (default: 5) */
|
|
26
|
+
durationSeconds?: number;
|
|
27
|
+
/** Number of threads to use (default: 1, use 0 for auto/max cores) */
|
|
28
|
+
threads?: number;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Progress event payload during benchmark
|
|
33
|
+
*/
|
|
34
|
+
export type BenchmarkProgressPayload = {
|
|
35
|
+
/** Current hash count */
|
|
36
|
+
currentHashCount: number;
|
|
37
|
+
/** Elapsed time in milliseconds */
|
|
38
|
+
elapsedMs: number;
|
|
39
|
+
/** Current hashrate */
|
|
40
|
+
currentHashrate: number;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Events emitted by the benchmark module
|
|
45
|
+
*/
|
|
46
|
+
export type ExpoBenchmarkModuleEvents = {
|
|
47
|
+
onProgress: (params: BenchmarkProgressPayload) => void;
|
|
48
|
+
onComplete: (params: BenchmarkResult) => void;
|
|
49
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { NativeModule, requireNativeModule } from 'expo';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
ExpoBenchmarkModuleEvents,
|
|
5
|
+
BenchmarkResult,
|
|
6
|
+
BenchmarkOptions,
|
|
7
|
+
} from './ExpoBenchmark.types';
|
|
8
|
+
|
|
9
|
+
declare class ExpoBenchmarkModule extends NativeModule<ExpoBenchmarkModuleEvents> {
|
|
10
|
+
/**
|
|
11
|
+
* Get the number of CPU cores available on the device
|
|
12
|
+
*/
|
|
13
|
+
getCpuCoreCount(): number;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Run a SHA-256 hashrate benchmark
|
|
17
|
+
* @param options - Benchmark options (duration, threads)
|
|
18
|
+
* @returns Promise with benchmark results
|
|
19
|
+
*/
|
|
20
|
+
runBenchmarkAsync(options?: BenchmarkOptions): Promise<BenchmarkResult>;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Run a quick single-threaded benchmark (1 second)
|
|
24
|
+
* @returns Promise with benchmark results
|
|
25
|
+
*/
|
|
26
|
+
runQuickBenchmarkAsync(): Promise<BenchmarkResult>;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Run a multi-threaded benchmark using all available cores
|
|
30
|
+
* @param durationSeconds - Duration in seconds (default: 5)
|
|
31
|
+
* @returns Promise with benchmark results
|
|
32
|
+
*/
|
|
33
|
+
runMultiThreadedBenchmarkAsync(durationSeconds?: number): Promise<BenchmarkResult>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// This call loads the native module object from the JSI.
|
|
37
|
+
export default requireNativeModule<ExpoBenchmarkModule>('ExpoBenchmark');
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import ExpoBenchmarkModule from './ExpoBenchmarkModule';
|
|
2
|
+
|
|
3
|
+
// Export types
|
|
4
|
+
export * from './ExpoBenchmark.types';
|
|
5
|
+
|
|
6
|
+
// Export the native module
|
|
7
|
+
export default ExpoBenchmarkModule;
|
|
8
|
+
|
|
9
|
+
// Convenience functions
|
|
10
|
+
export const getCpuCoreCount = () => ExpoBenchmarkModule.getCpuCoreCount();
|
|
11
|
+
|
|
12
|
+
export const runBenchmark = (options?: {
|
|
13
|
+
durationSeconds?: number;
|
|
14
|
+
threads?: number;
|
|
15
|
+
}) => ExpoBenchmarkModule.runBenchmarkAsync(options);
|
|
16
|
+
|
|
17
|
+
export const runQuickBenchmark = () => ExpoBenchmarkModule.runQuickBenchmarkAsync();
|
|
18
|
+
|
|
19
|
+
export const runMultiThreadedBenchmark = (durationSeconds?: number) =>
|
|
20
|
+
ExpoBenchmarkModule.runMultiThreadedBenchmarkAsync(durationSeconds);
|
package/tsconfig.json
ADDED