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 ADDED
@@ -0,0 +1,5 @@
1
+ module.exports = {
2
+ root: true,
3
+ extends: ['universe/native', 'universe/web'],
4
+ ignorePatterns: ['build'],
5
+ };
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,2 @@
1
+ <manifest>
2
+ </manifest>
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ExpoBenchmark.types.js.map
@@ -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,4 @@
1
+ import { requireNativeModule } from 'expo';
2
+ // This call loads the native module object from the JSI.
3
+ export default requireNativeModule('ExpoBenchmark');
4
+ //# sourceMappingURL=ExpoBenchmarkModule.js.map
@@ -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"]}
@@ -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,9 @@
1
+ {
2
+ "platforms": ["apple", "android", "web"],
3
+ "apple": {
4
+ "modules": ["ExpoBenchmarkModule"]
5
+ },
6
+ "android": {
7
+ "modules": ["expo.modules.benchmark.ExpoBenchmarkModule"]
8
+ }
9
+ }
@@ -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
@@ -0,0 +1,9 @@
1
+ // @generated by expo-module-scripts
2
+ {
3
+ "extends": "expo-module-scripts/tsconfig.base",
4
+ "compilerOptions": {
5
+ "outDir": "./build"
6
+ },
7
+ "include": ["./src"],
8
+ "exclude": ["**/__mocks__/*", "**/__tests__/*", "**/__rsc_tests__/*"]
9
+ }