// Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 // DeepSpeed Team #include #include #include #include #include #include #include #include #include #include "cpu_lion.h" using namespace std::string_literals; static std::unordered_map> s_optimizers; // C++ interface template void Lion_Optimizer::Step_1(ds_params_precision_t* _params, ds_params_precision_t* grads, ds_state_precision_t* _exp_avg, size_t _param_size) { size_t rounded_size = 0; #if defined(__AVX512__) or defined(__AVX256__) Step_AVX<1>(&rounded_size, _params, grads, _exp_avg, _param_size); #endif if (_param_size > rounded_size) { float betta1_minus1 = 1 - _betta1; float betta2_minus1 = 1 - _betta2; float alpha = _alpha; float after_decay = 1 - alpha * _weight_decay; for (size_t t = rounded_size; t < _param_size; t += TILE) { size_t copy_size = TILE; if ((t + TILE) > _param_size) copy_size = _param_size - t; size_t offset = copy_size + t; #pragma omp parallel for for (size_t k = t; k < offset; k++) { float grad = (float)grads[k]; float param = (float)_params[k]; float momentum = _exp_avg[k]; float tmp = momentum * _betta1; tmp = grad * betta1_minus1 + tmp; // Rely on portable C++ methods to manipulate the sign bit of a floating-point // number. tmp = -std::copysignf(alpha, tmp); if (_weight_decay > 0) { param = param * after_decay + tmp; } else { param = param + tmp; } momentum = momentum * _betta2; momentum = grad * betta2_minus1 + momentum; _params[k] = param; _exp_avg[k] = momentum; } } } } template void Lion_Optimizer::Step_4(ds_params_precision_t* _params, ds_params_precision_t* grads, ds_state_precision_t* _exp_avg, size_t _param_size) { size_t rounded_size = 0; #if defined(__AVX512__) or defined(__AVX256__) Step_AVX<4>(&rounded_size, _params, grads, _exp_avg, _param_size); #endif if (_param_size > rounded_size) Step_1((_params + rounded_size), (grads + rounded_size), (_exp_avg + rounded_size), (_param_size - rounded_size)); } int create_lion_optimizer(int optimizer_id, float alpha, float betta1, float betta2, float weight_decay, bool should_log) { auto opt = std::make_shared(alpha, betta1, betta2, weight_decay); s_optimizers[optimizer_id] = opt; if (should_log) { std::string avx_type = ""; #if defined(__AVX512__) avx_type = "AVX512"; #else #if defined(__AVX256__) avx_type = "AVX2"; #else avx_type = "scalar"; #endif #endif printf("Lion Optimizer #%d is created with %s arithmetic capability.\n", optimizer_id, avx_type.c_str()); printf("Config: alpha=%f, betas=(%f, %f), weight_decay=%f\n", alpha, betta1, betta2, weight_decay); } return 0; } template void Lion_Optimizer::Step_8(ds_params_precision_t* _params, ds_params_precision_t* grads, ds_state_precision_t* _exp_avg, size_t _param_size) { size_t rounded_size = 0; #if defined(__AVX512__) or defined(__AVX256__) Step_AVX<8>(&rounded_size, _params, grads, _exp_avg, _param_size); #endif if (_param_size > rounded_size) Step_4((_params + rounded_size), (grads + rounded_size), (_exp_avg + rounded_size), (_param_size - rounded_size)); } template void step_invoker(std::shared_ptr opt, void* _params, void* grads, void* _exp_avg, size_t _param_size) { opt->Step_8((ds_params_precision_t*)(_params), (ds_params_precision_t*)(grads), (ds_state_precision_t*)(_exp_avg), _param_size); } std::map, std::function, void*, void*, void*, size_t)>> invokers; // Fill map with template functions for each type template void create_invoker() { invokers[std::tuple(c10::CppTypeToScalarType(), c10::CppTypeToScalarType())] = step_invoker; } struct InvokerInitializer { InvokerInitializer() { create_invoker(); create_invoker(); create_invoker(); create_invoker(); create_invoker(); } } _invoker_initializer; void invoke(std::shared_ptr opt, torch::Tensor& params, torch::Tensor& grads, torch::Tensor& exp_avg, size_t param_size) { c10::ScalarType params_type = at::typeMetaToScalarType(params.options().dtype()); c10::ScalarType state_type = at::typeMetaToScalarType(exp_avg.options().dtype()); auto it = invokers.find(std::tuple(params_type, state_type)); if (it == invokers.end()) { throw std::runtime_error("Lion optimizer with param type "s + c10::toString(params_type) + " and state type "s + c10::toString(state_type) + " is not supported on current hardware"s); } it->second(opt, params.data_ptr(), grads.data_ptr(), exp_avg.data_ptr(), param_size); } int ds_lion_step(int optimizer_id, size_t step, float lr, float beta1, float beta2, float weight_decay, torch::Tensor& params, torch::Tensor& grads, torch::Tensor& exp_avg) { auto params_c = params.contiguous(); auto grads_c = grads.contiguous(); auto exp_avg_c = exp_avg.contiguous(); std::shared_ptr opt = std::static_pointer_cast(s_optimizers[optimizer_id]); opt->IncrementStep(step, beta1, beta2); opt->update_state(lr, weight_decay); invoke(opt, params_c, grads_c, exp_avg_c, params_c.numel()); return 0; } int destroy_lion_optimizer(int optimizer_id) { s_optimizers.erase(optimizer_id); return 0; }