mirror of https://github.com/google/gemma.cpp.git
Support building with Emscripten
Update CMake configuration and utility functions to enable compilation with Emscripten. This includes setting Wasm-specific flags like memory64 and SIMD, implementing platform-specific memory detection, and adding guards for features like OpenSSL that may be unavailable in a web environment.
This commit is contained in:
parent
a649bc3557
commit
94648da6f2
|
|
@ -22,6 +22,19 @@ set(CMAKE_CXX_STANDARD 17)
|
|||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
if(EMSCRIPTEN)
|
||||
add_compile_options("-sMEMORY64")
|
||||
add_compile_options("-msimd128")
|
||||
add_compile_options("-pthread")
|
||||
add_link_options("-sALLOW_MEMORY_GROWTH")
|
||||
add_link_options("-sMAXIMUM_MEMORY=16GB")
|
||||
add_link_options("-sNODERAWFS")
|
||||
add_link_options("-sMEMORY64")
|
||||
add_link_options("-sSTACK_SIZE=2MB")
|
||||
add_link_options("-pthread")
|
||||
add_link_options("-sPROXY_TO_PTHREAD")
|
||||
endif()
|
||||
|
||||
FetchContent_Declare(highway GIT_REPOSITORY https://github.com/google/highway.git GIT_TAG 3b680cde3a556bead9cc23c8f595d07a44d5a0d5 EXCLUDE_FROM_ALL)
|
||||
FetchContent_MakeAvailable(highway)
|
||||
|
||||
|
|
@ -60,6 +73,9 @@ set(BENCHMARK_ENABLE_GTEST_TESTS OFF)
|
|||
|
||||
FetchContent_Declare(benchmark GIT_REPOSITORY https://github.com/google/benchmark.git GIT_TAG v1.8.2 EXCLUDE_FROM_ALL)
|
||||
FetchContent_MakeAvailable(benchmark)
|
||||
if(EMSCRIPTEN)
|
||||
target_compile_options(benchmark PRIVATE -Wno-c2y-extensions)
|
||||
endif()
|
||||
|
||||
# Base source files
|
||||
set(SOURCES
|
||||
|
|
|
|||
|
|
@ -50,6 +50,8 @@ namespace gcpp {
|
|||
// yet use any AVX 10.2 features.
|
||||
#define GEMMA_DISABLED_TARGETS \
|
||||
(HWY_SCALAR | HWY_SSE2 | HWY_SSSE3 | HWY_SSE4 | HWY_AVX10_2)
|
||||
#elif HWY_ARCH_WASM
|
||||
#define GEMMA_DISABLED_TARGETS HWY_SCALAR
|
||||
#endif // HWY_ARCH_*
|
||||
|
||||
#endif // GEMMA_DISABLED_TARGETS
|
||||
|
|
|
|||
|
|
@ -51,10 +51,15 @@ class APIClient {
|
|||
use_https_(port == 443),
|
||||
interactive_mode_(false) {
|
||||
if (use_https_) {
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
ssl_client_ = std::make_unique<httplib::SSLClient>(host, port);
|
||||
ssl_client_->set_read_timeout(60, 0);
|
||||
ssl_client_->set_write_timeout(60, 0);
|
||||
ssl_client_->enable_server_certificate_verification(false);
|
||||
#else
|
||||
std::cerr << "Error: HTTPS requested but OpenSSL not found." << std::endl;
|
||||
exit(1);
|
||||
#endif
|
||||
} else {
|
||||
client_ = std::make_unique<httplib::Client>(host, port);
|
||||
client_->set_read_timeout(60, 0);
|
||||
|
|
@ -109,8 +114,17 @@ class APIClient {
|
|||
if (!api_key_.empty()) {
|
||||
headers.emplace("X-goog-api-key", api_key_);
|
||||
}
|
||||
auto res = use_https_ ? ssl_client_->Get("/v1beta/models", headers)
|
||||
: client_->Get("/v1beta/models", headers);
|
||||
httplib::Result res;
|
||||
if (use_https_) {
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
res = ssl_client_->Get("/v1beta/models", headers);
|
||||
#else
|
||||
std::cerr << "Error: HTTPS requested but OpenSSL not found." << std::endl;
|
||||
exit(1);
|
||||
#endif
|
||||
} else {
|
||||
res = client_->Get("/v1beta/models", headers);
|
||||
}
|
||||
|
||||
if (res && res->status == 200) {
|
||||
json response = json::parse(res->body);
|
||||
|
|
@ -213,11 +227,17 @@ class APIClient {
|
|||
if (!api_key_.empty()) {
|
||||
headers.emplace("X-goog-api-key", api_key_);
|
||||
}
|
||||
|
||||
auto res = use_https_ ? ssl_client_->Post(endpoint, headers, request.dump(),
|
||||
"application/json")
|
||||
: client_->Post(endpoint, headers, request.dump(),
|
||||
"application/json");
|
||||
httplib::Result res;
|
||||
if (use_https_) {
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
res = ssl_client_->Post(endpoint, headers, request.dump(), "application/json");
|
||||
#else
|
||||
std::cerr << "Error: HTTPS requested but OpenSSL not found." << std::endl;
|
||||
exit(1);
|
||||
#endif
|
||||
} else {
|
||||
res = client_->Post(endpoint, headers, request.dump(), "application/json");
|
||||
}
|
||||
|
||||
if (res && res->status == 200) {
|
||||
json response = json::parse(res->body);
|
||||
|
|
@ -300,8 +320,17 @@ class APIClient {
|
|||
|
||||
httplib::Response res;
|
||||
httplib::Error error;
|
||||
bool success = use_https_ ? ssl_client_->send(req, res, error)
|
||||
: client_->send(req, res, error);
|
||||
bool success = false;
|
||||
if (use_https_) {
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
success = ssl_client_->send(req, res, error);
|
||||
#else
|
||||
std::cerr << "Error: HTTPS requested but OpenSSL not found." << std::endl;
|
||||
exit(1);
|
||||
#endif
|
||||
} else {
|
||||
success = client_->send(req, res, error);
|
||||
}
|
||||
|
||||
if (res.status == 200 && !accumulated_response.empty()) {
|
||||
return json{
|
||||
|
|
@ -322,7 +351,9 @@ class APIClient {
|
|||
|
||||
private:
|
||||
std::unique_ptr<httplib::Client> client_;
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
std::unique_ptr<httplib::SSLClient> ssl_client_;
|
||||
#endif
|
||||
std::string host_;
|
||||
int port_;
|
||||
std::string api_key_;
|
||||
|
|
|
|||
|
|
@ -101,6 +101,9 @@ size_t DetectPageSize() {
|
|||
size_t len = sizeof(data);
|
||||
HWY_ASSERT(sysctlbyname("vm.pagesize", &data, &len, nullptr, 0) == 0);
|
||||
return data;
|
||||
#elif defined(__EMSCRIPTEN__)
|
||||
// Pages in Wasm are always 64KiB.
|
||||
return 65536;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
|
|
@ -123,6 +126,9 @@ size_t DetectTotalMiB(size_t page_bytes) {
|
|||
HWY_ASSERT(sysctl(mib, sizeof(mib) / sizeof(*mib), &data, &len, nullptr, 0) ==
|
||||
0);
|
||||
return data >> 20;
|
||||
#elif defined(__EMSCRIPTEN__)
|
||||
// The maximum linear memory in Wasm is currently specified at 16GiB.
|
||||
return 16384;
|
||||
#else
|
||||
#error "Port"
|
||||
#endif
|
||||
|
|
@ -199,6 +205,9 @@ size_t Allocator::FreeMiB() const {
|
|||
sysctlbyname("vm.page_inactive_count", &inactive, &len, nullptr, 0);
|
||||
sysctlbyname("vm.page_speculative_count", &speculative, &len, nullptr, 0);
|
||||
return (free + inactive + speculative) * base_page_bytes_ >> 20;
|
||||
#elif defined(__EMSCRIPTEN__)
|
||||
// There's no way to emulate this in emscripten so we lie.
|
||||
return 16384;
|
||||
#else
|
||||
#error "Port"
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -113,6 +113,10 @@ NestedPools::NestedPools(const BoundedTopology& topology,
|
|||
const Allocator& allocator, size_t max_threads,
|
||||
Tristate pin)
|
||||
: pinning_(pin) {
|
||||
#ifdef __EMSCRIPTEN__
|
||||
// Node runs out of memory with a large number of workers. Cap it for now.
|
||||
if (max_threads == 0 || max_threads > 32) max_threads = 32;
|
||||
#endif
|
||||
const size_t num_clusters = topology.NumClusters();
|
||||
const size_t cluster_workers_cap = DivideMaxAcross(max_threads, num_clusters);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue