takeshi-inoue tinoue ggerganov commited on
Commit
41e000d
·
unverified ·
1 Parent(s): dee26b8

whisper.android : support benchmark for Android example. (#542)

Browse files

* whisper.android: Support benchmark for Android example.

* whisper.android: update screenshot in README.

* update: Make text selectable for copy & paste.

* Update whisper.h to restore API name

Co-authored-by: Georgi Gerganov <[email protected]>

* whisper.android: Restore original API names.

---------

Co-authored-by: tinoue <[email protected]>
Co-authored-by: Georgi Gerganov <[email protected]>

examples/whisper.android/README.md CHANGED
@@ -9,4 +9,4 @@ To use:
9
  5. Select the "release" active build variant, and use Android Studio to run and deploy to your device.
10
  [^1]: I recommend the tiny or base models for running on an Android device.
11
 
12
- <img width="300" alt="image" src="https://user-images.githubusercontent.com/1991296/208154256-82d972dc-221b-48c4-bfcb-36ce68602f93.png">
 
9
  5. Select the "release" active build variant, and use Android Studio to run and deploy to your device.
10
  [^1]: I recommend the tiny or base models for running on an Android device.
11
 
12
+ <img width="300" alt="image" src="https://user-images.githubusercontent.com/1670775/221613663-a17bf770-27ef-45ab-9a46-a5f99ba65d2a.jpg">
examples/whisper.android/app/src/main/java/com/whispercppdemo/ui/main/MainScreen.kt CHANGED
@@ -2,6 +2,7 @@ package com.whispercppdemo.ui.main
2
 
3
  import androidx.compose.foundation.layout.*
4
  import androidx.compose.foundation.rememberScrollState
 
5
  import androidx.compose.foundation.verticalScroll
6
  import androidx.compose.material3.*
7
  import androidx.compose.runtime.Composable
@@ -19,6 +20,7 @@ fun MainScreen(viewModel: MainScreenViewModel) {
19
  canTranscribe = viewModel.canTranscribe,
20
  isRecording = viewModel.isRecording,
21
  messageLog = viewModel.dataLog,
 
22
  onTranscribeSampleTapped = viewModel::transcribeSample,
23
  onRecordTapped = viewModel::toggleRecord
24
  )
@@ -30,6 +32,7 @@ private fun MainScreen(
30
  canTranscribe: Boolean,
31
  isRecording: Boolean,
32
  messageLog: String,
 
33
  onTranscribeSampleTapped: () -> Unit,
34
  onRecordTapped: () -> Unit
35
  ) {
@@ -45,8 +48,11 @@ private fun MainScreen(
45
  .padding(innerPadding)
46
  .padding(16.dp)
47
  ) {
48
- Row(horizontalArrangement = Arrangement.SpaceBetween) {
49
- TranscribeSampleButton(enabled = canTranscribe, onClick = onTranscribeSampleTapped)
 
 
 
50
  RecordButton(
51
  enabled = canTranscribe,
52
  isRecording = isRecording,
@@ -60,7 +66,16 @@ private fun MainScreen(
60
 
61
  @Composable
62
  private fun MessageLog(log: String) {
63
- Text(modifier = Modifier.verticalScroll(rememberScrollState()), text = log)
 
 
 
 
 
 
 
 
 
64
  }
65
 
66
  @Composable
 
2
 
3
  import androidx.compose.foundation.layout.*
4
  import androidx.compose.foundation.rememberScrollState
5
+ import androidx.compose.foundation.text.selection.SelectionContainer
6
  import androidx.compose.foundation.verticalScroll
7
  import androidx.compose.material3.*
8
  import androidx.compose.runtime.Composable
 
20
  canTranscribe = viewModel.canTranscribe,
21
  isRecording = viewModel.isRecording,
22
  messageLog = viewModel.dataLog,
23
+ onBenchmarkTapped = viewModel::benchmark,
24
  onTranscribeSampleTapped = viewModel::transcribeSample,
25
  onRecordTapped = viewModel::toggleRecord
26
  )
 
32
  canTranscribe: Boolean,
33
  isRecording: Boolean,
34
  messageLog: String,
35
+ onBenchmarkTapped: () -> Unit,
36
  onTranscribeSampleTapped: () -> Unit,
37
  onRecordTapped: () -> Unit
38
  ) {
 
48
  .padding(innerPadding)
49
  .padding(16.dp)
50
  ) {
51
+ Column(verticalArrangement = Arrangement.SpaceBetween) {
52
+ Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
53
+ BenchmarkButton(enabled = canTranscribe, onClick = onBenchmarkTapped)
54
+ TranscribeSampleButton(enabled = canTranscribe, onClick = onTranscribeSampleTapped)
55
+ }
56
  RecordButton(
57
  enabled = canTranscribe,
58
  isRecording = isRecording,
 
66
 
67
  @Composable
68
  private fun MessageLog(log: String) {
69
+ SelectionContainer() {
70
+ Text(modifier = Modifier.verticalScroll(rememberScrollState()), text = log)
71
+ }
72
+ }
73
+
74
+ @Composable
75
+ private fun BenchmarkButton(enabled: Boolean, onClick: () -> Unit) {
76
+ Button(onClick = onClick, enabled = enabled) {
77
+ Text("Benchmark")
78
+ }
79
  }
80
 
81
  @Composable
examples/whisper.android/app/src/main/java/com/whispercppdemo/ui/main/MainScreenViewModel.kt CHANGED
@@ -41,10 +41,15 @@ class MainScreenViewModel(private val application: Application) : ViewModel() {
41
 
42
  init {
43
  viewModelScope.launch {
 
44
  loadData()
45
  }
46
  }
47
 
 
 
 
 
48
  private suspend fun loadData() {
49
  printMessage("Loading data...\n")
50
  try {
@@ -81,10 +86,29 @@ class MainScreenViewModel(private val application: Application) : ViewModel() {
81
  //whisperContext = WhisperContext.createContextFromFile(firstModel.absolutePath)
82
  }
83
 
 
 
 
 
84
  fun transcribeSample() = viewModelScope.launch {
85
  transcribeAudio(getFirstSample())
86
  }
87
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  private suspend fun getFirstSample(): File = withContext(Dispatchers.IO) {
89
  samplesPath.listFiles()!!.first()
90
  }
@@ -114,11 +138,14 @@ class MainScreenViewModel(private val application: Application) : ViewModel() {
114
  canTranscribe = false
115
 
116
  try {
117
- printMessage("Reading wave samples...\n")
118
  val data = readAudioSamples(file)
 
119
  printMessage("Transcribing data...\n")
 
120
  val text = whisperContext?.transcribeData(data)
121
- printMessage("Done: $text\n")
 
122
  } catch (e: Exception) {
123
  Log.w(LOG_TAG, e)
124
  printMessage("${e.localizedMessage}\n")
 
41
 
42
  init {
43
  viewModelScope.launch {
44
+ printSystemInfo()
45
  loadData()
46
  }
47
  }
48
 
49
+ private suspend fun printSystemInfo() {
50
+ printMessage(String.format("System Info: %s\n", WhisperContext.getSystemInfo()));
51
+ }
52
+
53
  private suspend fun loadData() {
54
  printMessage("Loading data...\n")
55
  try {
 
86
  //whisperContext = WhisperContext.createContextFromFile(firstModel.absolutePath)
87
  }
88
 
89
+ fun benchmark() = viewModelScope.launch {
90
+ runBenchmark(6)
91
+ }
92
+
93
  fun transcribeSample() = viewModelScope.launch {
94
  transcribeAudio(getFirstSample())
95
  }
96
 
97
+ private suspend fun runBenchmark(nthreads: Int) {
98
+ if (!canTranscribe) {
99
+ return
100
+ }
101
+
102
+ canTranscribe = false
103
+
104
+ printMessage("Running benchmark. This will take minutes...\n")
105
+ whisperContext?.benchMemory(nthreads)?.let{ printMessage(it) }
106
+ printMessage("\n")
107
+ whisperContext?.benchGgmlMulMat(nthreads)?.let{ printMessage(it) }
108
+
109
+ canTranscribe = true
110
+ }
111
+
112
  private suspend fun getFirstSample(): File = withContext(Dispatchers.IO) {
113
  samplesPath.listFiles()!!.first()
114
  }
 
138
  canTranscribe = false
139
 
140
  try {
141
+ printMessage("Reading wave samples... ")
142
  val data = readAudioSamples(file)
143
+ printMessage("${data.size / (16000 / 1000)} ms\n")
144
  printMessage("Transcribing data...\n")
145
+ val start = System.currentTimeMillis()
146
  val text = whisperContext?.transcribeData(data)
147
+ val elapsed = System.currentTimeMillis() - start
148
+ printMessage("Done ($elapsed ms): $text\n")
149
  } catch (e: Exception) {
150
  Log.w(LOG_TAG, e)
151
  printMessage("${e.localizedMessage}\n")
examples/whisper.android/app/src/main/java/com/whispercppdemo/whisper/LibWhisper.kt CHANGED
@@ -27,6 +27,14 @@ class WhisperContext private constructor(private var ptr: Long) {
27
  }
28
  }
29
 
 
 
 
 
 
 
 
 
30
  suspend fun release() = withContext(scope.coroutineContext) {
31
  if (ptr != 0L) {
32
  WhisperLib.freeContext(ptr)
@@ -66,6 +74,10 @@ class WhisperContext private constructor(private var ptr: Long) {
66
  }
67
  return WhisperContext(ptr)
68
  }
 
 
 
 
69
  }
70
  }
71
 
@@ -117,6 +129,9 @@ private class WhisperLib {
117
  external fun fullTranscribe(contextPtr: Long, audioData: FloatArray)
118
  external fun getTextSegmentCount(contextPtr: Long): Int
119
  external fun getTextSegment(contextPtr: Long, index: Int): String
 
 
 
120
  }
121
  }
122
 
 
27
  }
28
  }
29
 
30
+ suspend fun benchMemory(nthreads: Int): String = withContext(scope.coroutineContext) {
31
+ return@withContext WhisperLib.benchMemcpy(nthreads)
32
+ }
33
+
34
+ suspend fun benchGgmlMulMat(nthreads: Int): String = withContext(scope.coroutineContext) {
35
+ return@withContext WhisperLib.benchGgmlMulMat(nthreads)
36
+ }
37
+
38
  suspend fun release() = withContext(scope.coroutineContext) {
39
  if (ptr != 0L) {
40
  WhisperLib.freeContext(ptr)
 
74
  }
75
  return WhisperContext(ptr)
76
  }
77
+
78
+ fun getSystemInfo(): String {
79
+ return WhisperLib.getSystemInfo()
80
+ }
81
  }
82
  }
83
 
 
129
  external fun fullTranscribe(contextPtr: Long, audioData: FloatArray)
130
  external fun getTextSegmentCount(contextPtr: Long): Int
131
  external fun getTextSegment(contextPtr: Long, index: Int): String
132
+ external fun getSystemInfo(): String
133
+ external fun benchMemcpy(nthread: Int): String
134
+ external fun benchGgmlMulMat(nthread: Int): String
135
  }
136
  }
137
 
examples/whisper.android/app/src/main/jni/whisper/jni.c CHANGED
@@ -6,6 +6,7 @@
6
  #include <sys/sysinfo.h>
7
  #include <string.h>
8
  #include "whisper.h"
 
9
 
10
  #define UNUSED(x) (void)(x)
11
  #define TAG "JNI"
@@ -213,4 +214,30 @@ Java_com_whispercppdemo_whisper_WhisperLib_00024Companion_getTextSegment(
213
  const char *text = whisper_full_get_segment_text(context, index);
214
  jstring string = (*env)->NewStringUTF(env, text);
215
  return string;
216
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  #include <sys/sysinfo.h>
7
  #include <string.h>
8
  #include "whisper.h"
9
+ #include "ggml.h"
10
 
11
  #define UNUSED(x) (void)(x)
12
  #define TAG "JNI"
 
214
  const char *text = whisper_full_get_segment_text(context, index);
215
  jstring string = (*env)->NewStringUTF(env, text);
216
  return string;
217
+ }
218
+
219
+ JNIEXPORT jstring JNICALL
220
+ Java_com_whispercppdemo_whisper_WhisperLib_00024Companion_getSystemInfo(
221
+ JNIEnv *env, jobject thiz
222
+ ) {
223
+ UNUSED(thiz);
224
+ const char *sysinfo = whisper_print_system_info();
225
+ jstring string = (*env)->NewStringUTF(env, sysinfo);
226
+ return string;
227
+ }
228
+
229
+ JNIEXPORT jstring JNICALL
230
+ Java_com_whispercppdemo_whisper_WhisperLib_00024Companion_benchMemcpy(JNIEnv *env, jobject thiz,
231
+ jint n_threads) {
232
+ UNUSED(thiz);
233
+ const char *bench_ggml_memcpy = whisper_bench_memcpy_str(n_threads);
234
+ jstring string = (*env)->NewStringUTF(env, bench_ggml_memcpy);
235
+ }
236
+
237
+ JNIEXPORT jstring JNICALL
238
+ Java_com_whispercppdemo_whisper_WhisperLib_00024Companion_benchGgmlMulMat(JNIEnv *env, jobject thiz,
239
+ jint n_threads) {
240
+ UNUSED(thiz);
241
+ const char *bench_ggml_mul_mat = whisper_bench_ggml_mul_mat_str(n_threads);
242
+ jstring string = (*env)->NewStringUTF(env, bench_ggml_mul_mat);
243
+ }
whisper.cpp CHANGED
@@ -4551,6 +4551,15 @@ float whisper_full_get_token_p(struct whisper_context * ctx, int i_segment, int
4551
  //
4552
 
4553
  WHISPER_API int whisper_bench_memcpy(int n_threads) {
 
 
 
 
 
 
 
 
 
4554
  ggml_time_init();
4555
 
4556
  size_t n = 50;
@@ -4580,7 +4589,8 @@ WHISPER_API int whisper_bench_memcpy(int n_threads) {
4580
  src[0] = rand();
4581
  }
4582
 
4583
- fprintf(stderr, "memcpy: %.2f GB/s\n", (double) (n*size)/(tsum*1024llu*1024llu*1024llu));
 
4584
 
4585
  // needed to prevent the compile from optimizing the memcpy away
4586
  {
@@ -4588,16 +4598,26 @@ WHISPER_API int whisper_bench_memcpy(int n_threads) {
4588
 
4589
  for (size_t i = 0; i < size; i++) sum += dst[i];
4590
 
4591
- fprintf(stderr, "sum: %s %f\n", sum == -536870910.00 ? "ok" : "error", sum);
 
4592
  }
4593
 
4594
  free(src);
4595
  free(dst);
4596
 
4597
- return 0;
4598
  }
4599
 
4600
  WHISPER_API int whisper_bench_ggml_mul_mat(int n_threads) {
 
 
 
 
 
 
 
 
 
4601
  ggml_time_init();
4602
 
4603
  const int n_max = 128;
@@ -4673,11 +4693,12 @@ WHISPER_API int whisper_bench_ggml_mul_mat(int n_threads) {
4673
  s = ((2.0*N*N*N*n)/tsum)*1e-9;
4674
  }
4675
 
4676
- fprintf(stderr, "ggml_mul_mat: %5zu x %5zu: F16 %8.1f GFLOPS (%3d runs) / F32 %8.1f GFLOPS (%3d runs)\n",
4677
  N, N, s_fp16, n_fp16, s_fp32, n_fp32);
 
4678
  }
4679
 
4680
- return 0;
4681
  }
4682
 
4683
  // =================================================================================================
 
4551
  //
4552
 
4553
  WHISPER_API int whisper_bench_memcpy(int n_threads) {
4554
+ fputs(whisper_bench_memcpy_str(n_threads), stderr);
4555
+ return 0;
4556
+ }
4557
+
4558
+ WHISPER_API const char * whisper_bench_memcpy_str(int n_threads) {
4559
+ static std::string s;
4560
+ s = "";
4561
+ char strbuf[256];
4562
+
4563
  ggml_time_init();
4564
 
4565
  size_t n = 50;
 
4589
  src[0] = rand();
4590
  }
4591
 
4592
+ snprintf(strbuf, sizeof(strbuf), "memcpy: %.2f GB/s\n", (double) (n*size)/(tsum*1024llu*1024llu*1024llu));
4593
+ s += strbuf;
4594
 
4595
  // needed to prevent the compile from optimizing the memcpy away
4596
  {
 
4598
 
4599
  for (size_t i = 0; i < size; i++) sum += dst[i];
4600
 
4601
+ snprintf(strbuf, sizeof(strbuf), "sum: %s %f\n", sum == -536870910.00 ? "ok" : "error", sum);
4602
+ s += strbuf;
4603
  }
4604
 
4605
  free(src);
4606
  free(dst);
4607
 
4608
+ return s.c_str();
4609
  }
4610
 
4611
  WHISPER_API int whisper_bench_ggml_mul_mat(int n_threads) {
4612
+ fputs(whisper_bench_ggml_mul_mat_str(n_threads), stderr);
4613
+ return 0;
4614
+ }
4615
+
4616
+ WHISPER_API const char * whisper_bench_ggml_mul_mat_str(int n_threads) {
4617
+ static std::string s;
4618
+ s = "";
4619
+ char strbuf[256];
4620
+
4621
  ggml_time_init();
4622
 
4623
  const int n_max = 128;
 
4693
  s = ((2.0*N*N*N*n)/tsum)*1e-9;
4694
  }
4695
 
4696
+ snprintf(strbuf, sizeof(strbuf), "ggml_mul_mat: %5zu x %5zu: F16 %8.1f GFLOPS (%3d runs) / F32 %8.1f GFLOPS (%3d runs)\n",
4697
  N, N, s_fp16, n_fp16, s_fp32, n_fp32);
4698
+ s += strbuf;
4699
  }
4700
 
4701
+ return s.c_str();
4702
  }
4703
 
4704
  // =================================================================================================
whisper.h CHANGED
@@ -462,7 +462,9 @@ extern "C" {
462
  // Temporary helpers needed for exposing ggml interface
463
 
464
  WHISPER_API int whisper_bench_memcpy(int n_threads);
 
465
  WHISPER_API int whisper_bench_ggml_mul_mat(int n_threads);
 
466
 
467
  #ifdef __cplusplus
468
  }
 
462
  // Temporary helpers needed for exposing ggml interface
463
 
464
  WHISPER_API int whisper_bench_memcpy(int n_threads);
465
+ WHISPER_API const char * whisper_bench_memcpy_str(int n_threads);
466
  WHISPER_API int whisper_bench_ggml_mul_mat(int n_threads);
467
+ WHISPER_API const char * whisper_bench_ggml_mul_mat_str(int n_threads);
468
 
469
  #ifdef __cplusplus
470
  }