Initial commit: FunASR Speech Recognition Toolkit
Update API Documentation / build-api-docs (push) Has been cancelled

Add complete FunASR codebase including models, runtime, and documentation.
This commit is contained in:
freedakgmail
2026-07-09 22:38:58 +08:00
commit 6116b1f3c6
3683 changed files with 990984 additions and 0 deletions
+27
View File
@@ -0,0 +1,27 @@
# Whenever make is run in this directory, call ./get_version.sh as the
# first thing. This script regenereates ./version.h if necessary, e.g.
# if it does not already exist or if the version number has changed.
LOG := $(shell ./get_version.sh; echo " $$?")
ifneq ($(strip $(LOG)), 0)
RC := $(lastword $(LOG))
OUT := $(wordlist 1,$(shell echo $$(($(words $(LOG))-1))),$(LOG))
ifeq ($(RC),0)
$(info $(OUT))
else
$(error $(OUT))
endif
endif
all:
include ../kaldi.mk
TESTFILES = kaldi-math-test io-funcs-test kaldi-error-test timer-test
OBJFILES = kaldi-math.o kaldi-error.o io-funcs.o kaldi-utils.o timer.o
LIBNAME = kaldi-base
ADDLIBS =
include ../makefiles/default_rules.mk
+94
View File
@@ -0,0 +1,94 @@
#!/usr/bin/env bash
# Copyright 2017 University of Southern California (Author: Dogan Can)
# See ../../COPYING for clarification regarding multiple authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
# WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
# MERCHANTABLITY OR NON-INFRINGEMENT.
# See the Apache 2 License for the specific language governing permissions and
# limitations under the License.
# Kaldi versioning is loosely based on the semantic versioning scheme. This
# script tries to work out the version string from the partial version number
# specified in src/.version along with the recent git history. By convention
# src/.version specifies the first two components (MAJOR.MINOR) of the version
# number. The third component (PATCH) is determined by counting how many
# commits there are that are newer than than the last commit modifiying
# src/.version. If there are uncommitted changes in the src/ directory, then
# the version string is extended with a suffix (~N) specifiying the number of
# files with uncommitted changes. The last component of the version string is
# the abbreviated hash of the HEAD commit. If git history is not available or
# if the file src/.short_version exists, then the version string defaults to
# the number specified in src/.version.
set -e
# Change working directory to the directory where this script is located.
cd "$(dirname ${BASH_SOURCE[0]})"
# Read the partial version number specified in the first line of src/.version.
version=$(head -1 ../.version)
if [ -e ../.short_version ]; then
echo "$0: File src/.short_version exists."
echo "$0: Stopping the construction of full version number from git history."
elif ! [[ $version =~ ^[0-9][0-9]*.[0-9][0-9]*$ ]]; then
echo "$0: The version number \"$version\" specified in src/.version is not" \
"in MAJOR.MINOR format."
echo "$0: Stopping the construction of full version number from git history."
elif ! which git >&/dev/null; then
echo "$0: Git is not installed."
echo "$0: Using the version number \"$version\" specified in src/.version."
elif [ "$(git rev-parse --is-inside-work-tree 2>/dev/null)" != true ]; then
echo "$0: Git history is not available."
echo "$0: Using the version number \"$version\" specified in src/.version."
else
# Figure out patch number.
version_commit=$(git log -1 --pretty=oneline ../.version | awk '{print $1}')
patch_number=$(git rev-list ${version_commit}..HEAD | wc -l | awk '{print $1}')
version="$version.$patch_number"
# Check for uncommitted changes in src/.
uncommitted_changes=$(git diff-index HEAD -- .. | wc -l | awk '{print $1}')
if [ $uncommitted_changes -gt 0 ]; then
# Add suffix ~N if there are N files in src/ with uncommitted changes
version="$version~$uncommitted_changes"
fi
# Figure out HEAD commit SHA-1.
head_commit=$(git log -1 --pretty=oneline | awk '{print $1}')
head_commit_short=$(git log -1 --oneline --abbrev=4 | awk '{print $1}')
version="$version-${head_commit_short}"
fi
# Empty version number is not allowed.
if [ -z "$version" ]; then
version="?"
fi
# Write version info to a temporary file.
temp=$(mktemp /tmp/temp.XXXXXX)
trap 'rm -f "$temp"' EXIT
echo "// This file was automatically created by ./get_version.sh." > $temp
echo "// It is only included by ./kaldi-error.cc." >> $temp
echo "#define KALDI_VERSION \"$version\"" >> $temp
if [ -n "$head_commit" ]; then
echo "#define KALDI_GIT_HEAD \"$head_commit\"" >> $temp
fi
# Overwrite ./version.h with the temporary file if they are different.
if ! cmp -s $temp version.h; then
cp $temp version.h
chmod 644 version.h
fi
exit 0
@@ -0,0 +1,327 @@
// base/io-funcs-inl.h
// Copyright 2009-2011 Microsoft Corporation; Saarland University;
// Jan Silovsky; Yanmin Qian;
// Johns Hopkins University (Author: Daniel Povey)
// 2016 Xiaohui Zhang
// See ../../COPYING for clarification regarding multiple authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
// WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
// MERCHANTABLITY OR NON-INFRINGEMENT.
// See the Apache 2 License for the specific language governing permissions and
// limitations under the License.
#ifndef KALDI_BASE_IO_FUNCS_INL_H_
#define KALDI_BASE_IO_FUNCS_INL_H_ 1
// Do not include this file directly. It is included by base/io-funcs.h
#include <limits>
#include <vector>
namespace kaldi {
// Template that covers integers.
template<class T> void WriteBasicType(std::ostream &os,
bool binary, T t) {
// Compile time assertion that this is not called with a wrong type.
KALDI_ASSERT_IS_INTEGER_TYPE(T);
if (binary) {
char len_c = (std::numeric_limits<T>::is_signed ? 1 : -1)
* static_cast<char>(sizeof(t));
os.put(len_c);
os.write(reinterpret_cast<const char *>(&t), sizeof(t));
} else {
if (sizeof(t) == 1)
os << static_cast<int16>(t) << " ";
else
os << t << " ";
}
if (os.fail()) {
KALDI_ERR << "Write failure in WriteBasicType.";
}
}
// Template that covers integers.
template<class T> inline void ReadBasicType(std::istream &is,
bool binary, T *t) {
KALDI_PARANOID_ASSERT(t != NULL);
// Compile time assertion that this is not called with a wrong type.
KALDI_ASSERT_IS_INTEGER_TYPE(T);
if (binary) {
int len_c_in = is.get();
if (len_c_in == -1)
KALDI_ERR << "ReadBasicType: encountered end of stream.";
char len_c = static_cast<char>(len_c_in), len_c_expected
= (std::numeric_limits<T>::is_signed ? 1 : -1)
* static_cast<char>(sizeof(*t));
if (len_c != len_c_expected) {
KALDI_ERR << "ReadBasicType: did not get expected integer type, "
<< static_cast<int>(len_c)
<< " vs. " << static_cast<int>(len_c_expected)
<< ". You can change this code to successfully"
<< " read it later, if needed.";
// insert code here to read "wrong" type. Might have a switch statement.
}
is.read(reinterpret_cast<char *>(t), sizeof(*t));
} else {
if (sizeof(*t) == 1) {
int16 i;
is >> i;
*t = i;
} else {
is >> *t;
}
}
if (is.fail()) {
KALDI_ERR << "Read failure in ReadBasicType, file position is "
<< is.tellg() << ", next char is " << is.peek();
}
}
// Template that covers integers.
template<class T>
inline void WriteIntegerPairVector(std::ostream &os, bool binary,
const std::vector<std::pair<T, T> > &v) {
// Compile time assertion that this is not called with a wrong type.
KALDI_ASSERT_IS_INTEGER_TYPE(T);
if (binary) {
char sz = sizeof(T); // this is currently just a check.
os.write(&sz, 1);
int32 vecsz = static_cast<int32>(v.size());
KALDI_ASSERT((size_t)vecsz == v.size());
os.write(reinterpret_cast<const char *>(&vecsz), sizeof(vecsz));
if (vecsz != 0) {
os.write(reinterpret_cast<const char *>(&(v[0])), sizeof(T) * vecsz * 2);
}
} else {
// focus here is on prettiness of text form rather than
// efficiency of reading-in.
// reading-in is dominated by low-level operations anyway:
// for efficiency use binary.
os << "[ ";
typename std::vector<std::pair<T, T> >::const_iterator iter = v.begin(),
end = v.end();
for (; iter != end; ++iter) {
if (sizeof(T) == 1)
os << static_cast<int16>(iter->first) << ','
<< static_cast<int16>(iter->second) << ' ';
else
os << iter->first << ','
<< iter->second << ' ';
}
os << "]\n";
}
if (os.fail()) {
KALDI_ERR << "Write failure in WriteIntegerPairVector.";
}
}
// Template that covers integers.
template<class T>
inline void ReadIntegerPairVector(std::istream &is, bool binary,
std::vector<std::pair<T, T> > *v) {
KALDI_ASSERT_IS_INTEGER_TYPE(T);
KALDI_ASSERT(v != NULL);
if (binary) {
int sz = is.peek();
if (sz == sizeof(T)) {
is.get();
} else { // this is currently just a check.
KALDI_ERR << "ReadIntegerPairVector: expected to see type of size "
<< sizeof(T) << ", saw instead " << sz << ", at file position "
<< is.tellg();
}
int32 vecsz;
is.read(reinterpret_cast<char *>(&vecsz), sizeof(vecsz));
if (is.fail() || vecsz < 0) goto bad;
v->resize(vecsz);
if (vecsz > 0) {
is.read(reinterpret_cast<char *>(&((*v)[0])), sizeof(T)*vecsz*2);
}
} else {
std::vector<std::pair<T, T> > tmp_v; // use temporary so v doesn't use extra memory
// due to resizing.
is >> std::ws;
if (is.peek() != static_cast<int>('[')) {
KALDI_ERR << "ReadIntegerPairVector: expected to see [, saw "
<< is.peek() << ", at file position " << is.tellg();
}
is.get(); // consume the '['.
is >> std::ws; // consume whitespace.
while (is.peek() != static_cast<int>(']')) {
if (sizeof(T) == 1) { // read/write chars as numbers.
int16 next_t1, next_t2;
is >> next_t1;
if (is.fail()) goto bad;
if (is.peek() != static_cast<int>(','))
KALDI_ERR << "ReadIntegerPairVector: expected to see ',', saw "
<< is.peek() << ", at file position " << is.tellg();
is.get(); // consume the ','.
is >> next_t2 >> std::ws;
if (is.fail()) goto bad;
else
tmp_v.push_back(std::make_pair<T, T>((T)next_t1, (T)next_t2));
} else {
T next_t1, next_t2;
is >> next_t1;
if (is.fail()) goto bad;
if (is.peek() != static_cast<int>(','))
KALDI_ERR << "ReadIntegerPairVector: expected to see ',', saw "
<< is.peek() << ", at file position " << is.tellg();
is.get(); // consume the ','.
is >> next_t2 >> std::ws;
if (is.fail()) goto bad;
else
tmp_v.push_back(std::pair<T, T>(next_t1, next_t2));
}
}
is.get(); // get the final ']'.
*v = tmp_v; // could use std::swap to use less temporary memory, but this
// uses less permanent memory.
}
if (!is.fail()) return;
bad:
KALDI_ERR << "ReadIntegerPairVector: read failure at file position "
<< is.tellg();
}
template<class T> inline void WriteIntegerVector(std::ostream &os, bool binary,
const std::vector<T> &v) {
// Compile time assertion that this is not called with a wrong type.
KALDI_ASSERT_IS_INTEGER_TYPE(T);
if (binary) {
char sz = sizeof(T); // this is currently just a check.
os.write(&sz, 1);
int32 vecsz = static_cast<int32>(v.size());
KALDI_ASSERT((size_t)vecsz == v.size());
os.write(reinterpret_cast<const char *>(&vecsz), sizeof(vecsz));
if (vecsz != 0) {
os.write(reinterpret_cast<const char *>(&(v[0])), sizeof(T)*vecsz);
}
} else {
// focus here is on prettiness of text form rather than
// efficiency of reading-in.
// reading-in is dominated by low-level operations anyway:
// for efficiency use binary.
os << "[ ";
typename std::vector<T>::const_iterator iter = v.begin(), end = v.end();
for (; iter != end; ++iter) {
if (sizeof(T) == 1)
os << static_cast<int16>(*iter) << " ";
else
os << *iter << " ";
}
os << "]\n";
}
if (os.fail()) {
KALDI_ERR << "Write failure in WriteIntegerVector.";
}
}
template<class T> inline void ReadIntegerVector(std::istream &is,
bool binary,
std::vector<T> *v) {
KALDI_ASSERT_IS_INTEGER_TYPE(T);
KALDI_ASSERT(v != NULL);
if (binary) {
int sz = is.peek();
if (sz == sizeof(T)) {
is.get();
} else { // this is currently just a check.
KALDI_ERR << "ReadIntegerVector: expected to see type of size "
<< sizeof(T) << ", saw instead " << sz << ", at file position "
<< is.tellg();
}
int32 vecsz;
is.read(reinterpret_cast<char *>(&vecsz), sizeof(vecsz));
if (is.fail() || vecsz < 0) goto bad;
v->resize(vecsz);
if (vecsz > 0) {
is.read(reinterpret_cast<char *>(&((*v)[0])), sizeof(T)*vecsz);
}
} else {
std::vector<T> tmp_v; // use temporary so v doesn't use extra memory
// due to resizing.
is >> std::ws;
if (is.peek() != static_cast<int>('[')) {
KALDI_ERR << "ReadIntegerVector: expected to see [, saw "
<< is.peek() << ", at file position " << is.tellg();
}
is.get(); // consume the '['.
is >> std::ws; // consume whitespace.
while (is.peek() != static_cast<int>(']')) {
if (sizeof(T) == 1) { // read/write chars as numbers.
int16 next_t;
is >> next_t >> std::ws;
if (is.fail()) goto bad;
else
tmp_v.push_back((T)next_t);
} else {
T next_t;
is >> next_t >> std::ws;
if (is.fail()) goto bad;
else
tmp_v.push_back(next_t);
}
}
is.get(); // get the final ']'.
*v = tmp_v; // could use std::swap to use less temporary memory, but this
// uses less permanent memory.
}
if (!is.fail()) return;
bad:
KALDI_ERR << "ReadIntegerVector: read failure at file position "
<< is.tellg();
}
// Initialize an opened stream for writing by writing an optional binary
// header and modifying the floating-point precision.
inline void InitKaldiOutputStream(std::ostream &os, bool binary) {
// This does not throw exceptions (does not check for errors).
if (binary) {
os.put('\0');
os.put('B');
}
// Note, in non-binary mode we may at some point want to mess with
// the precision a bit.
// 7 is a bit more than the precision of float..
if (os.precision() < 7)
os.precision(7);
}
/// Initialize an opened stream for reading by detecting the binary header and
// setting the "binary" value appropriately.
inline bool InitKaldiInputStream(std::istream &is, bool *binary) {
// Sets the 'binary' variable.
// Throws exception in the very unusual situation that stream
// starts with '\0' but not then 'B'.
if (is.peek() == '\0') { // seems to be binary
is.get();
if (is.peek() != 'B') {
return false;
}
is.get();
*binary = true;
return true;
} else {
*binary = false;
return true;
}
}
} // end namespace kaldi.
#endif // KALDI_BASE_IO_FUNCS_INL_H_
@@ -0,0 +1,160 @@
// base/io-funcs-test.cc
// Copyright 2009-2011 Microsoft Corporation
// See ../../COPYING for clarification regarding multiple authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
// WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
// MERCHANTABLITY OR NON-INFRINGEMENT.
// See the Apache 2 License for the specific language governing permissions and
// limitations under the License.
#include "base/io-funcs.h"
#include "base/kaldi-math.h"
namespace kaldi {
void UnitTestIo(bool binary) {
{
const char *filename = "tmpf";
std::ofstream outfile(filename, std::ios_base::out | std::ios_base::binary);
InitKaldiOutputStream(outfile, binary);
if (!binary) outfile << "\t";
int64 i1 = Rand() % 10000;
WriteBasicType(outfile, binary, i1);
uint16 i2 = Rand() % 10000;
WriteBasicType(outfile, binary, i2);
if (!binary) outfile << "\t";
char c = Rand();
WriteBasicType(outfile, binary, c);
if (!binary && Rand()%2 == 0) outfile << " \n";
std::vector<int32> vec1;
WriteIntegerVector(outfile, binary, vec1);
if (!binary && Rand()%2 == 0) outfile << " \n";
std::vector<uint16> vec2;
for (size_t i = 0; i < 10; i++) vec2.push_back(Rand()%100 - 10);
WriteIntegerVector(outfile, binary, vec2);
if (!binary) outfile << " \n";
std::vector<char> vec3;
int32 size = RandInt(0, 10);
for (size_t i = 0; i < size; i++) vec3.push_back(Rand()%100);
WriteIntegerVector(outfile, binary, vec3);
std::vector<std::pair<int32, int32> > vec4;
WriteIntegerPairVector(outfile, binary, vec4);
if (!binary && Rand()%2 == 0) outfile << " \n";
std::vector<std::pair<uint16, uint16> > vec5;
for (size_t i = 0; i < size; i++) vec5.push_back(std::make_pair<uint16, uint16>(Rand()%100 - 10, Rand()%100 - 10));
WriteIntegerPairVector(outfile, binary, vec5);
if (!binary) outfile << " \n";
std::vector<std::pair<char, char> > vec6;
for (size_t i = 0; i < size; i++) vec6.push_back(std::make_pair<char, char>(Rand()%100, Rand()%100));
WriteIntegerPairVector(outfile, binary, vec6);
if (!binary && Rand()%2 == 0) outfile << " \n";
const char *token1 = "Hi";
WriteToken(outfile, binary, token1);
if (!binary) outfile << " \n";
std::string token2 = "There.";
WriteToken(outfile, binary, token2);
if (!binary && Rand()%2 == 0) outfile << " \n";
std::string token3 = "You.";
WriteToken(outfile, binary, token3);
if (!binary && Rand()%2 == 0) outfile << " ";
float f1 = RandUniform();
WriteBasicType(outfile, binary, f1);
if (!binary && Rand()%2 == 0) outfile << "\t";
float f2 = RandUniform();
WriteBasicType(outfile, binary, f2);
double d1 = RandUniform();
WriteBasicType(outfile, binary, d1);
if (!binary && Rand()%2 == 0) outfile << "\t";
double d2 = RandUniform();
WriteBasicType(outfile, binary, d2);
if (!binary && Rand()%2 == 0) outfile << "\t";
outfile.close();
{
std::ifstream infile(filename, std::ios_base::in | std::ios_base::binary);
bool binary_in;
InitKaldiInputStream(infile, &binary_in);
int64 i1_in;
ReadBasicType(infile, binary_in, &i1_in);
KALDI_ASSERT(i1_in == i1);
uint16 i2_in;
ReadBasicType(infile, binary_in, &i2_in);
KALDI_ASSERT(i2_in == i2);
char c_in;
ReadBasicType(infile, binary_in, &c_in);
KALDI_ASSERT(c_in == c);
std::vector<int32> vec1_in;
ReadIntegerVector(infile, binary_in, &vec1_in);
KALDI_ASSERT(vec1_in == vec1);
std::vector<uint16> vec2_in;
ReadIntegerVector(infile, binary_in, &vec2_in);
KALDI_ASSERT(vec2_in == vec2);
std::vector<char> vec3_in;
ReadIntegerVector(infile, binary_in, &vec3_in);
KALDI_ASSERT(vec3_in == vec3);
std::vector<std::pair<int32, int32> > vec4_in;
ReadIntegerPairVector(infile, binary_in, &vec4_in);
KALDI_ASSERT(vec4_in == vec4);
std::vector<std::pair<uint16, uint16> > vec5_in;
ReadIntegerPairVector(infile, binary_in, &vec5_in);
KALDI_ASSERT(vec5_in == vec5);
std::vector<std::pair<char, char> > vec6_in;
ReadIntegerPairVector(infile, binary_in, &vec6_in);
KALDI_ASSERT(vec6_in == vec6);
std::string token1_in, token2_in;
KALDI_ASSERT(Peek(infile, binary_in) == static_cast<int>(*token1));
KALDI_ASSERT(PeekToken(infile, binary_in) == static_cast<int>(*token1));
// Note:
// the stuff with skipping over '<' is tested in ../util/kaldi-io-test.cc,
// since we need to make sure it works with pipes.
ReadToken(infile, binary_in, &token1_in);
KALDI_ASSERT(token1_in == std::string(token1));
ReadToken(infile, binary_in, &token2_in);
KALDI_ASSERT(token2_in == std::string(token2));
if (Rand() % 2 == 0)
ExpectToken(infile, binary_in, token3.c_str());
else
ExpectToken(infile, binary_in, token3);
float f1_in; // same type.
ReadBasicType(infile, binary_in, &f1_in);
AssertEqual(f1_in, f1);
double f2_in; // wrong type.
ReadBasicType(infile, binary_in, &f2_in);
AssertEqual(f2_in, f2);
double d1_in; // same type.
ReadBasicType(infile, binary_in, &d1_in);
AssertEqual(d1_in, d1);
float d2_in; // wrong type.
ReadBasicType(infile, binary_in, &d2_in);
AssertEqual(d2_in, d2);
KALDI_ASSERT(Peek(infile, binary_in) == -1);
KALDI_ASSERT(PeekToken(infile, binary_in) == -1);
}
unlink(filename);
}
}
} // end namespace kaldi.
int main() {
using namespace kaldi;
for (size_t i = 0; i < 10; i++) {
UnitTestIo(false);
UnitTestIo(true);
}
KALDI_ASSERT(1); // just to check that KALDI_ASSERT does not fail for 1.
return 0;
}
+218
View File
@@ -0,0 +1,218 @@
// base/io-funcs.cc
// Copyright 2009-2011 Microsoft Corporation; Saarland University
// See ../../COPYING for clarification regarding multiple authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
// WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
// MERCHANTABLITY OR NON-INFRINGEMENT.
// See the Apache 2 License for the specific language governing permissions and
// limitations under the License.
#include "base/io-funcs.h"
#include "base/kaldi-math.h"
namespace kaldi {
template<>
void WriteBasicType<bool>(std::ostream &os, bool binary, bool b) {
os << (b ? "T":"F");
if (!binary) os << " ";
if (os.fail())
KALDI_ERR << "Write failure in WriteBasicType<bool>";
}
template<>
void ReadBasicType<bool>(std::istream &is, bool binary, bool *b) {
KALDI_PARANOID_ASSERT(b != NULL);
if (!binary) is >> std::ws; // eat up whitespace.
char c = is.peek();
if (c == 'T') {
*b = true;
is.get();
} else if (c == 'F') {
*b = false;
is.get();
} else {
KALDI_ERR << "Read failure in ReadBasicType<bool>, file position is "
<< is.tellg() << ", next char is " << CharToString(c);
}
}
template<>
void WriteBasicType<float>(std::ostream &os, bool binary, float f) {
if (binary) {
char c = sizeof(f);
os.put(c);
os.write(reinterpret_cast<const char *>(&f), sizeof(f));
} else {
os << f << " ";
}
}
template<>
void WriteBasicType<double>(std::ostream &os, bool binary, double f) {
if (binary) {
char c = sizeof(f);
os.put(c);
os.write(reinterpret_cast<const char *>(&f), sizeof(f));
} else {
os << f << " ";
}
}
template<>
void ReadBasicType<float>(std::istream &is, bool binary, float *f) {
KALDI_PARANOID_ASSERT(f != NULL);
if (binary) {
double d;
int c = is.peek();
if (c == sizeof(*f)) {
is.get();
is.read(reinterpret_cast<char*>(f), sizeof(*f));
} else if (c == sizeof(d)) {
ReadBasicType(is, binary, &d);
*f = d;
} else {
KALDI_ERR << "ReadBasicType: expected float, saw " << is.peek()
<< ", at file position " << is.tellg();
}
} else {
is >> *f;
}
if (is.fail()) {
KALDI_ERR << "ReadBasicType: failed to read, at file position "
<< is.tellg();
}
}
template<>
void ReadBasicType<double>(std::istream &is, bool binary, double *d) {
KALDI_PARANOID_ASSERT(d != NULL);
if (binary) {
float f;
int c = is.peek();
if (c == sizeof(*d)) {
is.get();
is.read(reinterpret_cast<char*>(d), sizeof(*d));
} else if (c == sizeof(f)) {
ReadBasicType(is, binary, &f);
*d = f;
} else {
KALDI_ERR << "ReadBasicType: expected float, saw " << is.peek()
<< ", at file position " << is.tellg();
}
} else {
is >> *d;
}
if (is.fail()) {
KALDI_ERR << "ReadBasicType: failed to read, at file position "
<< is.tellg();
}
}
void CheckToken(const char *token) {
if (*token == '\0')
KALDI_ERR << "Token is empty (not a valid token)";
const char *orig_token = token;
while (*token != '\0') {
if (::isspace(*token))
KALDI_ERR << "Token is not a valid token (contains space): '"
<< orig_token << "'";
token++;
}
}
void WriteToken(std::ostream &os, bool binary, const char *token) {
// binary mode is ignored;
// we use space as termination character in either case.
KALDI_ASSERT(token != NULL);
CheckToken(token); // make sure it's valid (can be read back)
os << token << " ";
if (os.fail()) {
KALDI_ERR << "Write failure in WriteToken.";
}
}
int Peek(std::istream &is, bool binary) {
if (!binary) is >> std::ws; // eat up whitespace.
return is.peek();
}
void WriteToken(std::ostream &os, bool binary, const std::string & token) {
WriteToken(os, binary, token.c_str());
}
void ReadToken(std::istream &is, bool binary, std::string *str) {
KALDI_ASSERT(str != NULL);
if (!binary) is >> std::ws; // consume whitespace.
is >> *str;
if (is.fail()) {
KALDI_ERR << "ReadToken, failed to read token at file position "
<< is.tellg();
}
if (!isspace(is.peek())) {
KALDI_ERR << "ReadToken, expected space after token, saw instead "
<< CharToString(static_cast<char>(is.peek()))
<< ", at file position " << is.tellg();
}
is.get(); // consume the space.
}
int PeekToken(std::istream &is, bool binary) {
if (!binary) is >> std::ws; // consume whitespace.
bool read_bracket;
if (static_cast<char>(is.peek()) == '<') {
read_bracket = true;
is.get();
} else {
read_bracket = false;
}
int ans = is.peek();
if (read_bracket) {
if (!is.unget()) {
// Clear the bad bit. This code can be (and is in fact) reached, since the
// C++ standard does not guarantee that a call to unget() must succeed.
is.clear();
}
}
return ans;
}
void ExpectToken(std::istream &is, bool binary, const char *token) {
int pos_at_start = is.tellg();
KALDI_ASSERT(token != NULL);
CheckToken(token); // make sure it's valid (can be read back)
if (!binary) is >> std::ws; // consume whitespace.
std::string str;
is >> str;
is.get(); // consume the space.
if (is.fail()) {
KALDI_ERR << "Failed to read token [started at file position "
<< pos_at_start << "], expected " << token;
}
// The second half of the '&&' expression below is so that if we're expecting
// "<Foo>", we will accept "Foo>" instead. This is so that the model-reading
// code will tolerate errors in PeekToken where is.unget() failed; search for
// is.clear() in PeekToken() for an explanation.
if (strcmp(str.c_str(), token) != 0 &&
!(token[0] == '<' && strcmp(str.c_str(), token + 1) == 0)) {
KALDI_ERR << "Expected token \"" << token << "\", got instead \""
<< str <<"\".";
}
}
void ExpectToken(std::istream &is, bool binary, const std::string &token) {
ExpectToken(is, binary, token.c_str());
}
} // end namespace kaldi
+245
View File
@@ -0,0 +1,245 @@
// base/io-funcs.h
// Copyright 2009-2011 Microsoft Corporation; Saarland University;
// Jan Silovsky; Yanmin Qian
// 2016 Xiaohui Zhang
// See ../../COPYING for clarification regarding multiple authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
// WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
// MERCHANTABLITY OR NON-INFRINGEMENT.
// See the Apache 2 License for the specific language governing permissions and
// limitations under the License.
#ifndef KALDI_BASE_IO_FUNCS_H_
#define KALDI_BASE_IO_FUNCS_H_
// This header only contains some relatively low-level I/O functions.
// The full Kaldi I/O declarations are in ../util/kaldi-io.h
// and ../util/kaldi-table.h
// They were put in util/ in order to avoid making the Matrix library
// dependent on them.
#include <cctype>
#include <vector>
#include <string>
#include "base/kaldi-common.h"
#include "base/io-funcs-inl.h"
namespace kaldi {
/*
This comment describes the Kaldi approach to I/O. All objects can be written
and read in two modes: binary and text. In addition we want to make the I/O
work if we redefine the typedef "BaseFloat" between floats and doubles.
We also want to have control over whitespace in text mode without affecting
the meaning of the file, for pretty-printing purposes.
Errors are handled by throwing a KaldiFatalError exception.
For integer and floating-point types (and boolean values):
WriteBasicType(std::ostream &, bool binary, const T&);
ReadBasicType(std::istream &, bool binary, T*);
and we expect these functions to be defined in such a way that they work when
the type T changes between float and double, so you can read float into double
and vice versa]. Note that for efficiency and space-saving reasons, the Vector
and Matrix classes do not use these functions [but they preserve the type
interchangeability in their own way]
For a class (or struct) C:
class C {
..
Write(std::ostream &, bool binary, [possibly extra optional args for specific classes]) const;
Read(std::istream &, bool binary, [possibly extra optional args for specific classes]);
..
}
NOTE: The only actual optional args we used are the "add" arguments in
Vector/Matrix classes, which specify whether we should sum the data already
in the class with the data being read.
For types which are typedef's involving stl classes, I/O is as follows:
typedef std::vector<std::pair<A, B> > MyTypedefName;
The user should define something like:
WriteMyTypedefName(std::ostream &, bool binary, const MyTypedefName &t);
ReadMyTypedefName(std::ostream &, bool binary, MyTypedefName *t);
The user would have to write these functions.
For a type std::vector<T>:
void WriteIntegerVector(std::ostream &os, bool binary, const std::vector<T> &v);
void ReadIntegerVector(std::istream &is, bool binary, std::vector<T> *v);
For other types, e.g. vectors of pairs, the user should create a routine of the
type WriteMyTypedefName. This is to avoid introducing confusing templated functions;
we could easily create templated functions to handle most of these cases but they
would have to share the same name.
It also often happens that the user needs to write/read special tokens as part
of a file. These might be class headers, or separators/identifiers in the class.
We provide special functions for manipulating these. These special tokens must
be nonempty and must not contain any whitespace.
void WriteToken(std::ostream &os, bool binary, const char*);
void WriteToken(std::ostream &os, bool binary, const std::string & token);
int Peek(std::istream &is, bool binary);
void ReadToken(std::istream &is, bool binary, std::string *str);
void PeekToken(std::istream &is, bool binary, std::string *str);
WriteToken writes the token and one space (whether in binary or text mode).
Peek returns the first character of the next token, by consuming whitespace
(in text mode) and then returning the peek() character. It returns -1 at EOF;
it doesn't throw. It's useful if a class can have various forms based on
typedefs and virtual classes, and wants to know which version to read.
ReadToken allows the caller to obtain the next token. PeekToken works just
like ReadToken, but seeks back to the beginning of the token. A subsequent
call to ReadToken will read the same token again. This is useful when
different object types are written to the same file; using PeekToken one can
decide which of the objects to read.
There is currently no special functionality for writing/reading strings (where the strings
contain data rather than "special tokens" that are whitespace-free and nonempty). This is
because Kaldi is structured in such a way that strings don't appear, except as OpenFst symbol
table entries (and these have their own format).
NOTE: you should not call ReadIntegerType and WriteIntegerType with types,
such as int and size_t, that are machine-independent -- at least not
if you want your file formats to port between machines. Use int32 and
int64 where necessary. There is no way to detect this using compile-time
assertions because C++ only keeps track of the internal representation of
the type.
*/
/// \addtogroup io_funcs_basic
/// @{
/// WriteBasicType is the name of the write function for bool, integer types,
/// and floating-point types. They all throw on error.
template<class T> void WriteBasicType(std::ostream &os, bool binary, T t);
/// ReadBasicType is the name of the read function for bool, integer types,
/// and floating-point types. They all throw on error.
template<class T> void ReadBasicType(std::istream &is, bool binary, T *t);
// Declare specialization for bool.
template<>
void WriteBasicType<bool>(std::ostream &os, bool binary, bool b);
template <>
void ReadBasicType<bool>(std::istream &is, bool binary, bool *b);
// Declare specializations for float and double.
template<>
void WriteBasicType<float>(std::ostream &os, bool binary, float f);
template<>
void WriteBasicType<double>(std::ostream &os, bool binary, double f);
template<>
void ReadBasicType<float>(std::istream &is, bool binary, float *f);
template<>
void ReadBasicType<double>(std::istream &is, bool binary, double *f);
// Define ReadBasicType that accepts an "add" parameter to add to
// the destination. Caution: if used in Read functions, be careful
// to initialize the parameters concerned to zero in the default
// constructor.
template<class T>
inline void ReadBasicType(std::istream &is, bool binary, T *t, bool add) {
if (!add) {
ReadBasicType(is, binary, t);
} else {
T tmp = T(0);
ReadBasicType(is, binary, &tmp);
*t += tmp;
}
}
/// Function for writing STL vectors of integer types.
template<class T> inline void WriteIntegerVector(std::ostream &os, bool binary,
const std::vector<T> &v);
/// Function for reading STL vector of integer types.
template<class T> inline void ReadIntegerVector(std::istream &is, bool binary,
std::vector<T> *v);
/// Function for writing STL vectors of pairs of integer types.
template<class T>
inline void WriteIntegerPairVector(std::ostream &os, bool binary,
const std::vector<std::pair<T, T> > &v);
/// Function for reading STL vector of pairs of integer types.
template<class T>
inline void ReadIntegerPairVector(std::istream &is, bool binary,
std::vector<std::pair<T, T> > *v);
/// The WriteToken functions are for writing nonempty sequences of non-space
/// characters. They are not for general strings.
void WriteToken(std::ostream &os, bool binary, const char *token);
void WriteToken(std::ostream &os, bool binary, const std::string & token);
/// Peek consumes whitespace (if binary == false) and then returns the peek()
/// value of the stream.
int Peek(std::istream &is, bool binary);
/// ReadToken gets the next token and puts it in str (exception on failure). If
/// PeekToken() had been previously called, it is possible that the stream had
/// failed to unget the starting '<' character. In this case ReadToken() returns
/// the token string without the leading '<'. You must be prepared to handle
/// this case. ExpectToken() handles this internally, and is not affected.
void ReadToken(std::istream &is, bool binary, std::string *token);
/// PeekToken will return the first character of the next token, or -1 if end of
/// file. It's the same as Peek(), except if the first character is '<' it will
/// skip over it and will return the next character. It will attempt to unget
/// the '<' so the stream is where it was before you did PeekToken(), however,
/// this is not guaranteed (see ReadToken()).
int PeekToken(std::istream &is, bool binary);
/// ExpectToken tries to read in the given token, and throws an exception
/// on failure.
void ExpectToken(std::istream &is, bool binary, const char *token);
void ExpectToken(std::istream &is, bool binary, const std::string & token);
/// ExpectPretty attempts to read the text in "token", but only in non-binary
/// mode. Throws exception on failure. It expects an exact match except that
/// arbitrary whitespace matches arbitrary whitespace.
void ExpectPretty(std::istream &is, bool binary, const char *token);
void ExpectPretty(std::istream &is, bool binary, const std::string & token);
/// @} end "addtogroup io_funcs_basic"
/// InitKaldiOutputStream initializes an opened stream for writing by writing an
/// optional binary header and modifying the floating-point precision; it will
/// typically not be called by users directly.
inline void InitKaldiOutputStream(std::ostream &os, bool binary);
/// InitKaldiInputStream initializes an opened stream for reading by detecting
/// the binary header and setting the "binary" value appropriately;
/// It will typically not be called by users directly.
inline bool InitKaldiInputStream(std::istream &is, bool *binary);
} // end namespace kaldi.
#endif // KALDI_BASE_IO_FUNCS_H_
@@ -0,0 +1,41 @@
// base/kaldi-common.h
// Copyright 2009-2011 Microsoft Corporation
// See ../../COPYING for clarification regarding multiple authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
// WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
// MERCHANTABLITY OR NON-INFRINGEMENT.
// See the Apache 2 License for the specific language governing permissions and
// limitations under the License.
#ifndef KALDI_BASE_KALDI_COMMON_H_
#define KALDI_BASE_KALDI_COMMON_H_ 1
#include <cstddef>
#include <cstdlib>
#include <cstring> // C string stuff like strcpy
#include <string>
#include <sstream>
#include <stdexcept>
#include <cassert>
#include <vector>
#include <iostream>
#include <fstream>
#include "base/kaldi-utils.h"
#include "base/kaldi-error.h"
#include "base/kaldi-types.h"
#include "base/io-funcs.h"
#include "base/kaldi-math.h"
#include "base/timer.h"
#endif // KALDI_BASE_KALDI_COMMON_H_
@@ -0,0 +1,82 @@
// base/kaldi-error-test.cc
// Copyright 2009-2011 Microsoft Corporation
// See ../../COPYING for clarification regarding multiple authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
// WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
// MERCHANTABLITY OR NON-INFRINGEMENT.
// See the Apache 2 License for the specific language governing permissions and
// limitations under the License.
#include "base/kaldi-common.h"
// testing that we get the stack trace.
namespace kaldi {
void MyFunction2() { KALDI_ERR << "Ignore this error"; }
void MyFunction1() { MyFunction2(); }
void UnitTestError() {
{
std::cerr << "Ignore next error:\n";
MyFunction1();
}
}
void VerifySymbolRange(const std::string &trace, const bool want_found,
const std::string &want_symbol) {
size_t begin, end;
const bool found = internal::LocateSymbolRange(trace, &begin, &end);
if (found != want_found) {
KALDI_ERR << "Found mismatch, got " << found << " want " << want_found;
}
if (!found) {
return;
}
const std::string symbol = trace.substr(begin, end - begin);
if (symbol != want_symbol) {
KALDI_ERR << "Symbol mismatch, got " << symbol << " want " << want_symbol;
}
}
void TestLocateSymbolRange() {
VerifySymbolRange("", false, "");
VerifySymbolRange(
R"TRACE(./kaldi-error-test(_ZN5kaldi13UnitTestErrorEv+0xb) [0x804965d])TRACE",
true, "_ZN5kaldi13UnitTestErrorEv");
// It is ok thread_start is not found because it is a C symbol.
VerifySymbolRange(
R"TRACE(31 libsystem_pthread.dylib 0x00007fff6fe4e40d thread_start + 13)TRACE",
false, "");
VerifySymbolRange(
R"TRACE(0 server 0x000000010f67614d _ZNK5kaldi13MessageLogger10LogMessageEv + 813)TRACE",
true, "_ZNK5kaldi13MessageLogger10LogMessageEv");
VerifySymbolRange(
R"TRACE(29 libsystem_pthread.dylib 0x00007fff6fe4f2eb _pthread_body + 126)TRACE",
true, "_pthread_body");
}
} // namespace kaldi
int main() {
kaldi::TestLocateSymbolRange();
kaldi::SetProgramName("/foo/bar/kaldi-error-test");
try {
kaldi::UnitTestError();
KALDI_ASSERT(0); // should not happen.
exit(1);
} catch (kaldi::KaldiFatalError &e) {
std::cout << "The error we generated was: '" << e.KaldiMessage() << "'\n";
}
}
@@ -0,0 +1,248 @@
// base/kaldi-error.cc
// Copyright 2019 LAIX (Yi Sun)
// Copyright 2019 SmartAction LLC (kkm)
// Copyright 2016 Brno University of Technology (author: Karel Vesely)
// Copyright 2009-2011 Microsoft Corporation; Lukas Burget; Ondrej Glembek
// See ../../COPYING for clarification regarding multiple authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
// WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
// MERCHANTABLITY OR NON-INFRINGEMENT.
// See the Apache 2 License for the specific language governing permissions and
// limitations under the License.
#ifdef HAVE_EXECINFO_H
#include <execinfo.h> // To get stack trace in error messages.
// If this #include fails there is an error in the Makefile, it does not
// support your platform well. Make sure HAVE_EXECINFO_H is undefined,
// and the code will compile.
#ifdef HAVE_CXXABI_H
#include <cxxabi.h> // For name demangling.
// Useful to decode the stack trace, but only used if we have execinfo.h
#endif // HAVE_CXXABI_H
#endif // HAVE_EXECINFO_H
#include "base/kaldi-common.h"
#include "base/kaldi-error.h"
// KALDI_GIT_HEAD is useless currently in full repo
//#if !defined(KALDI_VERSION)
//#include "base/version.h"
//#endif
namespace kaldi {
/***** GLOBAL VARIABLES FOR LOGGING *****/
int32 g_kaldi_verbose_level = 0;
static std::string program_name;
static LogHandler log_handler = NULL;
void SetProgramName(const char *basename) {
// Using the 'static std::string' for the program name is mostly harmless,
// because (a) Kaldi logging is undefined before main(), and (b) no stdc++
// string implementation has been found in the wild that would not be just
// an empty string when zero-initialized but not yet constructed.
program_name = basename;
}
/***** HELPER FUNCTIONS *****/
// Trim filename to at most 1 trailing directory long. Given a filename like
// "/a/b/c/d/e/f.cc", return "e/f.cc". Support both '/' and '\' as the path
// separator.
static const char *GetShortFileName(const char *path) {
if (path == nullptr)
return "";
const char *prev = path, *last = path;
while ((path = std::strpbrk(path, "\\/")) != nullptr) {
++path;
prev = last;
last = path;
}
return prev;
}
/***** STACK TRACE *****/
namespace internal {
bool LocateSymbolRange(const std::string &trace_name, size_t *begin,
size_t *end) {
// Find the first '_' with leading ' ' or '('.
*begin = std::string::npos;
for (size_t i = 1; i < trace_name.size(); i++) {
if (trace_name[i] != '_') {
continue;
}
if (trace_name[i - 1] == ' ' || trace_name[i - 1] == '(') {
*begin = i;
break;
}
}
if (*begin == std::string::npos) {
return false;
}
*end = trace_name.find_first_of(" +", *begin);
return *end != std::string::npos;
}
} // namespace internal
#ifdef HAVE_EXECINFO_H
static std::string Demangle(std::string trace_name) {
#ifndef HAVE_CXXABI_H
return trace_name;
#else // HAVE_CXXABI_H
// Try demangle the symbol. We are trying to support the following formats
// produced by different platforms:
//
// Linux:
// ./kaldi-error-test(_ZN5kaldi13UnitTestErrorEv+0xb) [0x804965d]
//
// Mac:
// 0 server 0x000000010f67614d _ZNK5kaldi13MessageLogger10LogMessageEv + 813
//
// We want to extract the name e.g., '_ZN5kaldi13UnitTestErrorEv' and
// demangle it info a readable name like kaldi::UnitTextError.
size_t begin, end;
if (!internal::LocateSymbolRange(trace_name, &begin, &end)) {
return trace_name;
}
std::string symbol = trace_name.substr(begin, end - begin);
int status;
char *demangled_name = abi::__cxa_demangle(symbol.c_str(), 0, 0, &status);
if (status == 0 && demangled_name != nullptr) {
symbol = demangled_name;
free(demangled_name);
}
return trace_name.substr(0, begin) + symbol +
trace_name.substr(end, std::string::npos);
#endif // HAVE_CXXABI_H
}
#endif // HAVE_EXECINFO_H
static std::string KaldiGetStackTrace() {
std::string ans;
#ifdef HAVE_EXECINFO_H
const size_t KALDI_MAX_TRACE_SIZE = 50;
const size_t KALDI_MAX_TRACE_PRINT = 50; // Must be even.
// Buffer for the trace.
void *trace[KALDI_MAX_TRACE_SIZE];
// Get the trace.
size_t size = backtrace(trace, KALDI_MAX_TRACE_SIZE);
// Get the trace symbols.
char **trace_symbol = backtrace_symbols(trace, size);
if (trace_symbol == NULL)
return ans;
// Compose a human-readable backtrace string.
ans += "[ Stack-Trace: ]\n";
if (size <= KALDI_MAX_TRACE_PRINT) {
for (size_t i = 0; i < size; i++) {
ans += Demangle(trace_symbol[i]) + "\n";
}
} else { // Print out first+last (e.g.) 5.
for (size_t i = 0; i < KALDI_MAX_TRACE_PRINT / 2; i++) {
ans += Demangle(trace_symbol[i]) + "\n";
}
ans += ".\n.\n.\n";
for (size_t i = size - KALDI_MAX_TRACE_PRINT / 2; i < size; i++) {
ans += Demangle(trace_symbol[i]) + "\n";
}
if (size == KALDI_MAX_TRACE_SIZE)
ans += ".\n.\n.\n"; // Stack was too long, probably a bug.
}
// We must free the array of pointers allocated by backtrace_symbols(),
// but not the strings themselves.
free(trace_symbol);
#endif // HAVE_EXECINFO_H
return ans;
}
/***** KALDI LOGGING *****/
MessageLogger::MessageLogger(LogMessageEnvelope::Severity severity,
const char *func, const char *file, int32 line) {
// Obviously, we assume the strings survive the destruction of this object.
envelope_.severity = severity;
envelope_.func = func;
envelope_.file = GetShortFileName(file); // Points inside 'file'.
envelope_.line = line;
}
void MessageLogger::LogMessage() const {
// Send to the logging handler if provided.
if (log_handler != NULL) {
log_handler(envelope_, GetMessage().c_str());
return;
}
// Otherwise, use the default Kaldi logging.
// Build the log-message header.
std::stringstream full_message;
if (envelope_.severity > LogMessageEnvelope::kInfo) {
full_message << "VLOG[" << envelope_.severity << "] (";
} else {
switch (envelope_.severity) {
case LogMessageEnvelope::kInfo:
//full_message << "LOG (\n";
break;
case LogMessageEnvelope::kWarning:
//full_message << "WARNING (\n";
break;
case LogMessageEnvelope::kAssertFailed:
full_message << "ASSERTION_FAILED (\n";
break;
case LogMessageEnvelope::kError:
default: // If not the ERROR, it still an error!
full_message << "ERROR (\n";
break;
}
}
// Add other info from the envelope and the message text.
//full_message << program_name.c_str() << "[" KALDI_VERSION "]" << ':'
// << envelope_.func << "():" << envelope_.file << ':'
// << envelope_.line << ") " << GetMessage().c_str();
// Add stack trace for errors and assertion failures, if available.
if (envelope_.severity < LogMessageEnvelope::kWarning) {
const std::string &stack_trace = KaldiGetStackTrace();
if (!stack_trace.empty()) {
full_message << "\n\n" << stack_trace;
}
}
// Print the complete message to stderr.
std::cerr << full_message.str();
}
/***** KALDI ASSERTS *****/
void KaldiAssertFailure_(const char *func, const char *file, int32 line,
const char *cond_str) {
MessageLogger::Log() =
MessageLogger(LogMessageEnvelope::kAssertFailed, func, file, line)
<< "Assertion failed: (" << cond_str << ")";
fflush(NULL); // Flush all pending buffers, abort() may not flush stderr.
std::abort();
}
/***** THIRD-PARTY LOG-HANDLER *****/
LogHandler SetLogHandler(LogHandler handler) {
LogHandler old_handler = log_handler;
log_handler = handler;
return old_handler;
}
} // namespace kaldi
+231
View File
@@ -0,0 +1,231 @@
// base/kaldi-error.h
// Copyright 2019 LAIX (Yi Sun)
// Copyright 2019 SmartAction LLC (kkm)
// Copyright 2016 Brno University of Technology (author: Karel Vesely)
// Copyright 2009-2011 Microsoft Corporation; Ondrej Glembek; Lukas Burget;
// Saarland University
// See ../../COPYING for clarification regarding multiple authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
// WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
// MERCHANTABLITY OR NON-INFRINGEMENT.
// See the Apache 2 License for the specific language governing permissions and
// limitations under the License.
#ifndef KALDI_BASE_KALDI_ERROR_H_
#define KALDI_BASE_KALDI_ERROR_H_ 1
#include <cstdio>
#include <cstring>
#include <sstream>
#include <stdexcept>
#include <string>
#include <vector>
#include "base/kaldi-types.h"
#include "base/kaldi-utils.h"
/* Important that this file does not depend on any other kaldi headers. */
#ifdef _MSC_VER
#define __func__ __FUNCTION__
#endif
namespace kaldi {
/// \addtogroup error_group
/// @{
/***** PROGRAM NAME AND VERBOSITY LEVEL *****/
/// Called by ParseOptions to set base name (no directory) of the executing
/// program. The name is printed in logging code along with every message,
/// because in our scripts, we often mix together the stderr of many programs.
/// This function is very thread-unsafe.
void SetProgramName(const char *basename);
/// This is set by util/parse-options.{h,cc} if you set --verbose=? option.
/// Do not use directly, prefer {Get,Set}VerboseLevel().
extern int32 g_kaldi_verbose_level;
/// Get verbosity level, usually set via command line '--verbose=' switch.
inline int32 GetVerboseLevel() { return g_kaldi_verbose_level; }
/// This should be rarely used, except by programs using Kaldi as library;
/// command-line programs set the verbose level automatically from ParseOptions.
inline void SetVerboseLevel(int32 i) { g_kaldi_verbose_level = i; }
/***** KALDI LOGGING *****/
/// Log message severity and source location info.
struct LogMessageEnvelope {
/// Message severity. In addition to these levels, positive values (1 to 6)
/// specify verbose logging level. Verbose messages are produced only when
/// SetVerboseLevel() has been called to set logging level to at least the
/// corresponding value.
enum Severity {
kAssertFailed = -3, //!< Assertion failure. abort() will be called.
kError = -2, //!< Fatal error. KaldiFatalError will be thrown.
kWarning = -1, //!< Indicates a recoverable but abnormal condition.
kInfo = 0, //!< Informational message.
};
int severity; //!< A Severity value, or positive verbosity level.
const char *func; //!< Name of the function invoking the logging.
const char *file; //!< Source file name with up to 1 leading directory.
int32 line; //<! Line number in the source file.
};
/// Kaldi fatal runtime error exception. This exception is thrown from any use
/// of the KALDI_ERR logging macro after the logging function, either set by
/// SetLogHandler(), or the Kaldi's internal one, has returned.
class KaldiFatalError : public std::runtime_error {
public:
explicit KaldiFatalError(const std::string &message)
: std::runtime_error(message) {}
explicit KaldiFatalError(const char *message) : std::runtime_error(message) {}
/// Returns the exception name, "kaldi::KaldiFatalError".
virtual const char *what() const noexcept override {
return "kaldi::KaldiFatalError";
}
/// Returns the Kaldi error message logged by KALDI_ERR.
const char *KaldiMessage() const { return std::runtime_error::what(); }
};
// Class MessageLogger is the workhorse behind the KALDI_ASSERT, KALDI_ERR,
// KALDI_WARN, KALDI_LOG and KALDI_VLOG macros. It formats the message, then
// either prints it to stderr or passes to the custom logging handler if
// provided. Then, in case of the error, throws a KaldiFatalError exception, or
// in case of failed KALDI_ASSERT, calls std::abort().
class MessageLogger {
public:
/// The constructor stores the message's "envelope", a set of data which
// identifies the location in source which is sending the message to log.
// The pointers to strings are stored internally, and not owned or copied,
// so that their storage must outlive this object.
MessageLogger(LogMessageEnvelope::Severity severity, const char *func,
const char *file, int32 line);
// The stream insertion operator, used in e.g. 'KALDI_LOG << "Message"'.
template <typename T> MessageLogger &operator<<(const T &val) {
ss_ << val;
return *this;
}
// When assigned a MessageLogger, log its contents.
struct Log final {
void operator=(const MessageLogger &logger) { logger.LogMessage(); }
};
// When assigned a MessageLogger, log its contents and then throw
// a KaldiFatalError.
struct LogAndThrow final {
[[noreturn]] void operator=(const MessageLogger &logger) {
logger.LogMessage();
throw KaldiFatalError(logger.GetMessage());
}
};
private:
std::string GetMessage() const { return ss_.str(); }
void LogMessage() const;
LogMessageEnvelope envelope_;
std::ostringstream ss_;
};
// Logging macros.
#define KALDI_ERR \
::kaldi::MessageLogger::LogAndThrow() = ::kaldi::MessageLogger( \
::kaldi::LogMessageEnvelope::kError, __func__, __FILE__, __LINE__)
#define KALDI_WARN \
::kaldi::MessageLogger::Log() = ::kaldi::MessageLogger( \
::kaldi::LogMessageEnvelope::kWarning, __func__, __FILE__, __LINE__)
#define KALDI_LOG \
::kaldi::MessageLogger::Log() = ::kaldi::MessageLogger( \
::kaldi::LogMessageEnvelope::kInfo, __func__, __FILE__, __LINE__)
#define KALDI_VLOG(v) \
if ((v) <= ::kaldi::GetVerboseLevel()) \
::kaldi::MessageLogger::Log() = \
::kaldi::MessageLogger((::kaldi::LogMessageEnvelope::Severity)(v), \
__func__, __FILE__, __LINE__)
/***** KALDI ASSERTS *****/
[[noreturn]] void KaldiAssertFailure_(const char *func, const char *file,
int32 line, const char *cond_str);
// Note on KALDI_ASSERT and KALDI_PARANOID_ASSERT:
//
// A single block {} around if /else does not work, because it causes
// syntax error (unmatched else block) in the following code:
//
// if (condition)
// KALDI_ASSERT(condition2);
// else
// SomethingElse();
//
// do {} while(0) -- note there is no semicolon at the end! -- works nicely,
// and compilers will be able to optimize the loop away (as the condition
// is always false).
//
// Also see KALDI_COMPILE_TIME_ASSERT, defined in base/kaldi-utils.h, and
// KALDI_ASSERT_IS_INTEGER_TYPE and KALDI_ASSERT_IS_FLOATING_TYPE, also defined
// there.
#ifndef NDEBUG
#define KALDI_ASSERT(cond) \
do { \
if (cond) \
(void)0; \
else \
::kaldi::KaldiAssertFailure_(__func__, __FILE__, __LINE__, #cond); \
} while (0)
#else
#define KALDI_ASSERT(cond) (void)0
#endif
// Some more expensive asserts only checked if this defined.
#ifdef KALDI_PARANOID
#define KALDI_PARANOID_ASSERT(cond) \
do { \
if (cond) \
(void)0; \
else \
::kaldi::KaldiAssertFailure_(__func__, __FILE__, __LINE__, #cond); \
} while (0)
#else
#define KALDI_PARANOID_ASSERT(cond) (void)0
#endif
/***** THIRD-PARTY LOG-HANDLER *****/
/// Type of third-party logging function.
typedef void (*LogHandler)(const LogMessageEnvelope &envelope,
const char *message);
/// Set logging handler. If called with a non-NULL function pointer, the
/// function pointed by it is called to send messages to a caller-provided log.
/// If called with a NULL pointer, restores default Kaldi error logging to
/// stderr. This function is obviously not thread safe; the log handler must be.
/// Returns a previously set logging handler pointer, or NULL.
LogHandler SetLogHandler(LogHandler);
/// @} end "addtogroup error_group"
//// Functions within internal is exported for testing only, do not use.
//namespace internal {
//bool LocateSymbolRange(const std::string &trace_name, size_t *begin,
// size_t *end);
//} // namespace internal
} // namespace kaldi
#endif // KALDI_BASE_KALDI_ERROR_H_
@@ -0,0 +1,334 @@
// base/kaldi-math-test.cc
//
// Copyright 2009-2011 Microsoft Corporation; Yanmin Qian; Jan Silovsky
// See ../../COPYING for clarification regarding multiple authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
// WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
// MERCHANTABLITY OR NON-INFRINGEMENT.
// See the Apache 2 License for the specific language governing permissions and
// limitations under the License.
#include "base/kaldi-math.h"
#include <limits>
#include "base/timer.h"
namespace kaldi {
template<class I> void UnitTestGcdLcmTpl() {
for (I a = 1; a < 15; a++) { // a is min gcd.
I b = (I)(Rand() % 10);
I c = (I)(Rand() % 10);
if (std::numeric_limits<I>::is_signed) {
if (Rand() % 2 == 0) b = -b;
if (Rand() % 2 == 0) c = -c;
}
if (b == 0 && c == 0) continue; // gcd not defined for such numbers.
I g = Gcd(b*a, c*a);
KALDI_ASSERT(g >= a);
KALDI_ASSERT((b*a) % g == 0);
KALDI_ASSERT((c*a) % g == 0);
// test least common multiple
if (b <= 0 || c <= 0) continue; // lcm not defined unless both positive.
I h = Lcm(b*a, c*a);
KALDI_ASSERT(h != 0 && (h % (b*a)) == 0 &&
(h % (c*a)) == 0);
}
}
void UnitTestRoundUpToNearestPowerOfTwo() {
KALDI_ASSERT(RoundUpToNearestPowerOfTwo(1) == 1);
KALDI_ASSERT(RoundUpToNearestPowerOfTwo(2) == 2);
KALDI_ASSERT(RoundUpToNearestPowerOfTwo(3) == 4);
KALDI_ASSERT(RoundUpToNearestPowerOfTwo(4) == 4);
KALDI_ASSERT(RoundUpToNearestPowerOfTwo(7) == 8);
KALDI_ASSERT(RoundUpToNearestPowerOfTwo(8) == 8);
KALDI_ASSERT(RoundUpToNearestPowerOfTwo(255) == 256);
KALDI_ASSERT(RoundUpToNearestPowerOfTwo(256) == 256);
KALDI_ASSERT(RoundUpToNearestPowerOfTwo(257) == 512);
KALDI_ASSERT(RoundUpToNearestPowerOfTwo(1073700000) == 1073741824);
}
void UnitTestDivideRoundingDown() {
for (int32 i = 0; i < 100; i++) {
int32 a = RandInt(-100, 100);
int32 b = 0;
while (b == 0)
b = RandInt(-100, 100);
KALDI_ASSERT(DivideRoundingDown(a, b) ==
std::floor(static_cast<double>(a) / static_cast<double>(b)));
}
}
void UnitTestGcdLcm() {
UnitTestGcdLcmTpl<int>();
UnitTestGcdLcmTpl<size_t>();
UnitTestGcdLcmTpl<int16>();
}
void UnitTestRand() {
// Testing random-number generation.
std::cout << "Testing random-number generation. "
<< "If there is an error this may not terminate.\n";
std::cout << "If this does not terminate, look more closely. "
<< "There might be a problem [but might not be]\n";
for (int i = 1; i < 10; i++) {
{ // test RandUniform.
std::cout << "Test RandUniform\n";
KALDI_ASSERT(RandUniform() >= 0 && RandUniform() <= 1);
float sum = RandUniform()-0.5;
for (int j = 0; ; j++) {
sum += RandUniform()-0.5;
if (std::abs(sum) < 0.5*sqrt(static_cast<double>(j))) break;
}
}
{ // test RandGauss.
float sum = RandGauss();
for (int j = 0; ; j++) {
sum += RandGauss();
if (std::abs(sum) < 0.5*sqrt(static_cast<double>(j))) break;
}
}
{ // test RandGauss.
float sum = RandGauss();
for (int j = 0; ; j++) {
float a, b;
RandGauss2(&a, &b);
if (i % 2 == 0) sum += a;
else
sum += b;
if (std::abs(sum) < 0.5*sqrt(static_cast<double>(j))) break;
}
}
{ // test poisson_Rand().
KALDI_ASSERT(RandPoisson(3.0) >= 0);
KALDI_ASSERT(RandPoisson(0.0) == 0);
std::cout << "Test RandPoisson\n";
float lambda = RandUniform() * 3.0; // between 0 and 3.
double sum = RandPoisson(lambda) - lambda; // expected value is zero.
for (int j = 0; ; j++) {
sum += RandPoisson(lambda) - lambda;
if (std::abs(sum) < 0.5*sqrt(static_cast<double>(j))) break;
}
}
{ // test WithProb().
for (int32 i = 0; i < 10; i++) {
KALDI_ASSERT((WithProb(0.0) == false) && (WithProb(1.0) == true));
}
{
int32 tot = 0, n = 10000;
BaseFloat p = 0.5;
for (int32 i = 0; i < n; i++)
tot += WithProb(p);
KALDI_ASSERT(tot > (n * p * 0.8) && tot < (n * p * 1.2));
}
{
int32 tot = 0, n = 10000;
BaseFloat p = 0.25;
for (int32 i = 0; i < n; i++)
tot += WithProb(p);
KALDI_ASSERT(tot > (n * p * 0.8) && tot < (n * p * 1.2));
}
}
{ // test RandInt().
KALDI_ASSERT(RandInt(0, 3) >= 0 && RandInt(0, 3) <= 3);
std::cout << "Test RandInt\n";
int minint = Rand() % 200;
int maxint = minint + 1 + Rand() % 20;
float sum = RandInt(minint, maxint) + 0.5*(minint+maxint);
for (int j = 0; ; j++) {
sum += RandInt(minint, maxint) - 0.5*(minint+maxint);
if (std::abs(static_cast<float>(sum)) <
0.5*sqrt(static_cast<double>(j))*(maxint-minint)) break;
}
}
{ // test RandPrune in basic way.
KALDI_ASSERT(RandPrune(1.1, 1.0) == 1.1);
KALDI_ASSERT(RandPrune(0.0, 0.0) == 0.0);
KALDI_ASSERT(RandPrune(-1.1, 1.0) == -1.1);
KALDI_ASSERT(RandPrune(0.0, 1.0) == 0.0);
KALDI_ASSERT(RandPrune(0.5, 1.0) >= 0.0);
KALDI_ASSERT(RandPrune(-0.5, 1.0) <= 0.0);
BaseFloat f = RandPrune(-0.5, 1.0);
KALDI_ASSERT(f == 0.0 || f == -1.0);
f = RandPrune(0.5, 1.0);
KALDI_ASSERT(f == 0.0 || f == 1.0);
}
}
}
void UnitTestLogAddSub() {
for (int i = 0; i < 100; i++) {
double f1 = Rand() % 10000, f2 = Rand() % 20;
double add1 = Exp(LogAdd(Log(f1), Log(f2)));
double add2 = Exp(LogAdd(Log(f2), Log(f1)));
double add = f1 + f2, thresh = add*0.00001;
KALDI_ASSERT(std::abs(add-add1) < thresh && std::abs(add-add2) < thresh);
try {
double f2_check = Exp(LogSub(Log(add), Log(f1))),
thresh = (f2*0.01)+0.001;
KALDI_ASSERT(std::abs(f2_check-f2) < thresh);
} catch(...) {
KALDI_ASSERT(f2 == 0); // It will probably crash for f2=0.
}
}
}
void UnitTestDefines() { // Yes, we even unit-test the preprocessor statements.
KALDI_ASSERT(Exp(kLogZeroFloat) == 0.0);
KALDI_ASSERT(Exp(kLogZeroDouble) == 0.0);
BaseFloat den = 0.0;
KALDI_ASSERT(KALDI_ISNAN(0.0 / den));
KALDI_ASSERT(!KALDI_ISINF(0.0 / den));
KALDI_ASSERT(!KALDI_ISFINITE(0.0 / den));
KALDI_ASSERT(!KALDI_ISNAN(1.0 / den));
KALDI_ASSERT(KALDI_ISINF(1.0 / den));
KALDI_ASSERT(!KALDI_ISFINITE(1.0 / den));
KALDI_ASSERT(KALDI_ISFINITE(0.0));
KALDI_ASSERT(!KALDI_ISINF(0.0));
KALDI_ASSERT(!KALDI_ISNAN(0.0));
std::cout << 1.0+DBL_EPSILON;
std::cout << 1.0 + 0.5*DBL_EPSILON;
KALDI_ASSERT(1.0 + DBL_EPSILON != 1.0 && 1.0 + (0.5*DBL_EPSILON) == 1.0
&& "If this test fails, you can probably just comment it out-- "
"may mean your CPU exceeds expected floating point precision");
KALDI_ASSERT(1.0f + FLT_EPSILON != 1.0f && 1.0f + (0.5f*FLT_EPSILON) == 1.0f
&& "If this test fails, you can probably just comment it out-- "
"may mean your CPU exceeds expected floating point precision");
KALDI_ASSERT(std::abs(sin(M_PI)) < 1.0e-05
&& std::abs(cos(M_PI)+1.0) < 1.0e-05);
KALDI_ASSERT(std::abs(sin(M_2PI)) < 1.0e-05
&& std::abs(cos(M_2PI)-1.0) < 1.0e-05);
KALDI_ASSERT(std::abs(sin(Exp(M_LOG_2PI))) < 1.0e-05);
KALDI_ASSERT(std::abs(cos(Exp(M_LOG_2PI)) - 1.0) < 1.0e-05);
}
void UnitTestAssertFunc() { // Testing Assert** *functions
for (int i = 1; i < 100; i++) {
float f1 = Rand() % 10000 + 1, f2 = Rand() % 20 + 1;
float tmp1 = f1 * f2;
float tmp2 = (1/f1 + 1/f2);
float add = f1 + f2;
float addeql = tmp1 * tmp2;
float thresh = 0.00001;
AssertEqual(add, addeql, thresh); // test AssertEqual()
}
}
template<class I> void UnitTestFactorizeTpl() {
for (int p= 0; p < 100; p++) {
I m = Rand() % 100000;
if (m >= 1) {
std::vector<I> factors;
Factorize(m, &factors);
I m2 = 1;
for (size_t i = 0; i < factors.size(); i++) {
m2 *= factors[i];
if (i+1 < factors.size())
KALDI_ASSERT(factors[i+1] >= factors[i]); // check sorted.
}
KALDI_ASSERT(m2 == m); // check correctness.
}
}
}
void UnitTestFactorize() {
UnitTestFactorizeTpl<int>();
UnitTestFactorizeTpl<size_t>();
UnitTestFactorizeTpl<int16>();
}
void UnitTestApproxEqual() {
KALDI_ASSERT(ApproxEqual(1.0, 1.00001));
KALDI_ASSERT(ApproxEqual(1.0, 1.00001, 0.001));
KALDI_ASSERT(!ApproxEqual(1.0, 1.1));
KALDI_ASSERT(!ApproxEqual(1.0, 1.01, 0.001));
KALDI_ASSERT(!ApproxEqual(1.0, 0.0));
KALDI_ASSERT(ApproxEqual(0.0, 0.0));
KALDI_ASSERT(!ApproxEqual(0.0, 0.00001));
KALDI_ASSERT(!ApproxEqual(std::numeric_limits<float>::infinity(),
-std::numeric_limits<float>::infinity()));
KALDI_ASSERT(ApproxEqual(std::numeric_limits<float>::infinity(),
std::numeric_limits<float>::infinity()));
KALDI_ASSERT(ApproxEqual(-std::numeric_limits<float>::infinity(),
-std::numeric_limits<float>::infinity()));
KALDI_ASSERT(!ApproxEqual(-std::numeric_limits<float>::infinity(),
0));
KALDI_ASSERT(!ApproxEqual(-std::numeric_limits<float>::infinity(),
1));
}
template<class Real>
void UnitTestExpSpeed() {
Real sum = 0.0; // compute the sum to avoid optimizing it away.
Real time = 0.01; // how long this should last.
int block_size = 10;
int num_ops = 0;
Timer tim;
while (tim.Elapsed() < time) {
for (int i = 0; i < block_size; i++) {
sum += Exp((Real)i);
}
num_ops += block_size;
}
KALDI_ASSERT(sum > 0.0); // make it harder for the compiler to optimize Exp
// away, as we have a conditional.
Real flops = 1.0e-06 * num_ops / tim.Elapsed();
KALDI_LOG << "Megaflops doing Exp("
<< (sizeof(Real) == 4 ? "float" : "double") << ") is " << flops;
}
template<class Real>
void UnitTestLogSpeed() {
Real sum = 0.0; // compute the sum to avoid optimizing it away.
Real time = 0.01; // how long this should last.
int block_size = 10;
int num_ops = 0;
Timer tim;
while (tim.Elapsed() < time) {
for (int i = 0; i < block_size; i++) {
sum += Log(static_cast<float>(i + 1));
}
num_ops += block_size;
}
KALDI_ASSERT(sum > 0.0); // make it harder for the compiler to optimize Log
// away, as we have a conditional.
Real flops = 1.0e-06 * num_ops / tim.Elapsed();
KALDI_LOG << "Megaflops doing Log("
<< (sizeof(Real) == 4 ? "float" : "double") << ") is " << flops;
}
} // end namespace kaldi.
int main() {
using namespace kaldi;
UnitTestApproxEqual();
UnitTestGcdLcm();
UnitTestFactorize();
UnitTestDefines();
UnitTestLogAddSub();
UnitTestRand();
UnitTestAssertFunc();
UnitTestRoundUpToNearestPowerOfTwo();
UnitTestDivideRoundingDown();
UnitTestExpSpeed<float>();
UnitTestExpSpeed<double>();
UnitTestLogSpeed<float>();
UnitTestLogSpeed<double>();
}
+162
View File
@@ -0,0 +1,162 @@
// base/kaldi-math.cc
// Copyright 2009-2011 Microsoft Corporation; Yanmin Qian;
// Saarland University; Jan Silovsky
// See ../../COPYING for clarification regarding multiple authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
// WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
// MERCHANTABLITY OR NON-INFRINGEMENT.
// See the Apache 2 License for the specific language governing permissions and
// limitations under the License.
#include "base/kaldi-math.h"
#ifndef _MSC_VER
#include <stdlib.h>
#include <unistd.h>
#endif
#include <string>
#include <mutex>
namespace kaldi {
// These routines are tested in matrix/matrix-test.cc
int32 RoundUpToNearestPowerOfTwo(int32 n) {
KALDI_ASSERT(n > 0);
n--;
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
return n+1;
}
static std::mutex _RandMutex;
int Rand(struct RandomState* state) {
#if !defined(_POSIX_THREAD_SAFE_FUNCTIONS)
// On Windows and Cygwin, just call Rand()
return rand();
#else
if (state) {
return rand_r(&(state->seed));
} else {
std::lock_guard<std::mutex> lock(_RandMutex);
return rand();
}
#endif
}
RandomState::RandomState() {
// we initialize it as Rand() + 27437 instead of just Rand(), because on some
// systems, e.g. at the very least Mac OSX Yosemite and later, it seems to be
// the case that rand_r when initialized with rand() will give you the exact
// same sequence of numbers that rand() will give if you keep calling rand()
// after that initial call. This can cause problems with repeated sequences.
// For example if you initialize two RandomState structs one after the other
// without calling rand() in between, they would give you the same sequence
// offset by one (if we didn't have the "+ 27437" in the code). 27437 is just
// a randomly chosen prime number.
seed = unsigned(Rand()) + 27437;
}
bool WithProb(BaseFloat prob, struct RandomState* state) {
KALDI_ASSERT(prob >= 0 && prob <= 1.1); // prob should be <= 1.0,
// but we allow slightly larger values that could arise from roundoff in
// previous calculations.
KALDI_COMPILE_TIME_ASSERT(RAND_MAX > 128 * 128);
if (prob == 0) return false;
else if (prob == 1.0) return true;
else if (prob * RAND_MAX < 128.0) {
// prob is very small but nonzero, and the "main algorithm"
// wouldn't work that well. So: with probability 1/128, we
// return WithProb (prob * 128), else return false.
if (Rand(state) < RAND_MAX / 128) { // with probability 128...
// Note: we know that prob * 128.0 < 1.0, because
// we asserted RAND_MAX > 128 * 128.
return WithProb(prob * 128.0);
} else {
return false;
}
} else {
return (Rand(state) < ((RAND_MAX + static_cast<BaseFloat>(1.0)) * prob));
}
}
int32 RandInt(int32 min_val, int32 max_val, struct RandomState* state) {
// This is not exact.
KALDI_ASSERT(max_val >= min_val);
if (max_val == min_val) return min_val;
#ifdef _MSC_VER
// RAND_MAX is quite small on Windows -> may need to handle larger numbers.
if (RAND_MAX > (max_val-min_val)*8) {
// *8 to avoid large inaccuracies in probability, from the modulus...
return min_val +
((unsigned int)Rand(state) % (unsigned int)(max_val+1-min_val));
} else {
if ((unsigned int)(RAND_MAX*RAND_MAX) >
(unsigned int)((max_val+1-min_val)*8)) {
// *8 to avoid inaccuracies in probability, from the modulus...
return min_val + ( (unsigned int)( (Rand(state)+RAND_MAX*Rand(state)))
% (unsigned int)(max_val+1-min_val));
} else {
KALDI_ERR << "rand_int failed because we do not support such large "
"random numbers. (Extend this function).";
}
}
#else
return min_val +
(static_cast<int32>(Rand(state)) % static_cast<int32>(max_val+1-min_val));
#endif
}
// Returns poisson-distributed random number.
// Take care: this takes time proportional
// to lambda. Faster algorithms exist but are more complex.
int32 RandPoisson(float lambda, struct RandomState* state) {
// Knuth's algorithm.
KALDI_ASSERT(lambda >= 0);
float L = expf(-lambda), p = 1.0;
int32 k = 0;
do {
k++;
float u = RandUniform(state);
p *= u;
} while (p > L);
return k-1;
}
void RandGauss2(float *a, float *b, RandomState *state) {
KALDI_ASSERT(a);
KALDI_ASSERT(b);
float u1 = RandUniform(state);
float u2 = RandUniform(state);
u1 = sqrtf(-2.0f * logf(u1));
u2 = 2.0f * M_PI * u2;
*a = u1 * cosf(u2);
*b = u1 * sinf(u2);
}
void RandGauss2(double *a, double *b, RandomState *state) {
KALDI_ASSERT(a);
KALDI_ASSERT(b);
float a_float, b_float;
// Just because we're using doubles doesn't mean we need super-high-quality
// random numbers, so we just use the floating-point version internally.
RandGauss2(&a_float, &b_float, state);
*a = a_float;
*b = b_float;
}
} // end namespace kaldi
+363
View File
@@ -0,0 +1,363 @@
// base/kaldi-math.h
// Copyright 2009-2011 Ondrej Glembek; Microsoft Corporation; Yanmin Qian;
// Jan Silovsky; Saarland University
//
// See ../../COPYING for clarification regarding multiple authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
// WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
// MERCHANTABLITY OR NON-INFRINGEMENT.
// See the Apache 2 License for the specific language governing permissions and
// limitations under the License.
#ifndef KALDI_BASE_KALDI_MATH_H_
#define KALDI_BASE_KALDI_MATH_H_ 1
#ifdef _MSC_VER
#include <float.h>
#endif
#include <cmath>
#include <limits>
#include <vector>
#include "base/kaldi-types.h"
#include "base/kaldi-common.h"
#ifndef DBL_EPSILON
#define DBL_EPSILON 2.2204460492503131e-16
#endif
#ifndef FLT_EPSILON
#define FLT_EPSILON 1.19209290e-7f
#endif
#ifndef M_PI
#define M_PI 3.1415926535897932384626433832795
#endif
#ifndef M_SQRT2
#define M_SQRT2 1.4142135623730950488016887
#endif
#ifndef M_2PI
#define M_2PI 6.283185307179586476925286766559005
#endif
#ifndef M_SQRT1_2
#define M_SQRT1_2 0.7071067811865475244008443621048490
#endif
#ifndef M_LOG_2PI
#define M_LOG_2PI 1.8378770664093454835606594728112
#endif
#ifndef M_LN2
#define M_LN2 0.693147180559945309417232121458
#endif
#ifndef M_LN10
#define M_LN10 2.302585092994045684017991454684
#endif
#define KALDI_ISNAN std::isnan
#define KALDI_ISINF std::isinf
#define KALDI_ISFINITE(x) std::isfinite(x)
#if !defined(KALDI_SQR)
# define KALDI_SQR(x) ((x) * (x))
#endif
namespace kaldi {
#if !defined(_MSC_VER) || (_MSC_VER >= 1900)
inline double Exp(double x) { return exp(x); }
#ifndef KALDI_NO_EXPF
inline float Exp(float x) { return expf(x); }
#else
inline float Exp(float x) { return exp(static_cast<double>(x)); }
#endif // KALDI_NO_EXPF
#else
inline double Exp(double x) { return exp(x); }
#if !defined(__INTEL_COMPILER) && _MSC_VER == 1800 && defined(_M_X64)
// Microsoft CL v18.0 buggy 64-bit implementation of
// expf() incorrectly returns -inf for exp(-inf).
inline float Exp(float x) { return exp(static_cast<double>(x)); }
#else
inline float Exp(float x) { return expf(x); }
#endif // !defined(__INTEL_COMPILER) && _MSC_VER == 1800 && defined(_M_X64)
#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900)
inline double Log(double x) { return log(x); }
inline float Log(float x) { return logf(x); }
#if !defined(_MSC_VER) || (_MSC_VER >= 1700)
inline double Log1p(double x) { return log1p(x); }
inline float Log1p(float x) { return log1pf(x); }
#else
inline double Log1p(double x) {
const double cutoff = 1.0e-08;
if (x < cutoff)
return x - 0.5 * x * x;
else
return Log(1.0 + x);
}
inline float Log1p(float x) {
const float cutoff = 1.0e-07;
if (x < cutoff)
return x - 0.5 * x * x;
else
return Log(1.0 + x);
}
#endif
static const double kMinLogDiffDouble = Log(DBL_EPSILON); // negative!
static const float kMinLogDiffFloat = Log(FLT_EPSILON); // negative!
// -infinity
const float kLogZeroFloat = -std::numeric_limits<float>::infinity();
const double kLogZeroDouble = -std::numeric_limits<double>::infinity();
const BaseFloat kLogZeroBaseFloat = -std::numeric_limits<BaseFloat>::infinity();
// Returns a random integer between 0 and RAND_MAX, inclusive
int Rand(struct RandomState* state = NULL);
// State for thread-safe random number generator
struct RandomState {
RandomState();
unsigned seed;
};
// Returns a random integer between first and last inclusive.
int32 RandInt(int32 first, int32 last, struct RandomState* state = NULL);
// Returns true with probability "prob",
bool WithProb(BaseFloat prob, struct RandomState* state = NULL);
// with 0 <= prob <= 1 [we check this].
// Internally calls Rand(). This function is carefully implemented so
// that it should work even if prob is very small.
/// Returns a random number strictly between 0 and 1.
inline float RandUniform(struct RandomState* state = NULL) {
return static_cast<float>((Rand(state) + 1.0) / (RAND_MAX+2.0));
}
inline float RandGauss(struct RandomState* state = NULL) {
return static_cast<float>(sqrtf (-2 * Log(RandUniform(state)))
* cosf(2*M_PI*RandUniform(state)));
}
// Returns poisson-distributed random number. Uses Knuth's algorithm.
// Take care: this takes time proportional
// to lambda. Faster algorithms exist but are more complex.
int32 RandPoisson(float lambda, struct RandomState* state = NULL);
// Returns a pair of gaussian random numbers. Uses Box-Muller transform
void RandGauss2(float *a, float *b, RandomState *state = NULL);
void RandGauss2(double *a, double *b, RandomState *state = NULL);
// Also see Vector<float,double>::RandCategorical().
// This is a randomized pruning mechanism that preserves expectations,
// that we typically use to prune posteriors.
template<class Float>
inline Float RandPrune(Float post, BaseFloat prune_thresh,
struct RandomState* state = NULL) {
KALDI_ASSERT(prune_thresh >= 0.0);
if (post == 0.0 || std::abs(post) >= prune_thresh)
return post;
return (post >= 0 ? 1.0 : -1.0) *
(RandUniform(state) <= fabs(post)/prune_thresh ? prune_thresh : 0.0);
}
// returns log(exp(x) + exp(y)).
inline double LogAdd(double x, double y) {
double diff;
if (x < y) {
diff = x - y;
x = y;
} else {
diff = y - x;
}
// diff is negative. x is now the larger one.
if (diff >= kMinLogDiffDouble) {
double res;
res = x + Log1p(Exp(diff));
return res;
} else {
return x; // return the larger one.
}
}
// returns log(exp(x) + exp(y)).
inline float LogAdd(float x, float y) {
float diff;
if (x < y) {
diff = x - y;
x = y;
} else {
diff = y - x;
}
// diff is negative. x is now the larger one.
if (diff >= kMinLogDiffFloat) {
float res;
res = x + Log1p(Exp(diff));
return res;
} else {
return x; // return the larger one.
}
}
// returns log(exp(x) - exp(y)).
inline double LogSub(double x, double y) {
if (y >= x) { // Throws exception if y>=x.
if (y == x)
return kLogZeroDouble;
else
KALDI_ERR << "Cannot subtract a larger from a smaller number.";
}
double diff = y - x; // Will be negative.
double res = x + Log(1.0 - Exp(diff));
// res might be NAN if diff ~0.0, and 1.0-exp(diff) == 0 to machine precision
if (KALDI_ISNAN(res))
return kLogZeroDouble;
return res;
}
// returns log(exp(x) - exp(y)).
inline float LogSub(float x, float y) {
if (y >= x) { // Throws exception if y>=x.
if (y == x)
return kLogZeroDouble;
else
KALDI_ERR << "Cannot subtract a larger from a smaller number.";
}
float diff = y - x; // Will be negative.
float res = x + Log(1.0f - Exp(diff));
// res might be NAN if diff ~0.0, and 1.0-exp(diff) == 0 to machine precision
if (KALDI_ISNAN(res))
return kLogZeroFloat;
return res;
}
/// return abs(a - b) <= relative_tolerance * (abs(a)+abs(b)).
static inline bool ApproxEqual(float a, float b,
float relative_tolerance = 0.001) {
// a==b handles infinities.
if (a == b) return true;
float diff = std::abs(a-b);
if (diff == std::numeric_limits<float>::infinity()
|| diff != diff) return false; // diff is +inf or nan.
return (diff <= relative_tolerance*(std::abs(a)+std::abs(b)));
}
/// assert abs(a - b) <= relative_tolerance * (abs(a)+abs(b))
static inline void AssertEqual(float a, float b,
float relative_tolerance = 0.001) {
// a==b handles infinities.
KALDI_ASSERT(ApproxEqual(a, b, relative_tolerance));
}
// RoundUpToNearestPowerOfTwo does the obvious thing. It crashes if n <= 0.
int32 RoundUpToNearestPowerOfTwo(int32 n);
/// Returns a / b, rounding towards negative infinity in all cases.
static inline int32 DivideRoundingDown(int32 a, int32 b) {
KALDI_ASSERT(b != 0);
if (a * b >= 0)
return a / b;
else if (a < 0)
return (a - b + 1) / b;
else
return (a - b - 1) / b;
}
template<class I> I Gcd(I m, I n) {
if (m == 0 || n == 0) {
if (m == 0 && n == 0) { // gcd not defined, as all integers are divisors.
KALDI_ERR << "Undefined GCD since m = 0, n = 0.";
}
return (m == 0 ? (n > 0 ? n : -n) : ( m > 0 ? m : -m));
// return absolute value of whichever is nonzero
}
// could use compile-time assertion
// but involves messing with complex template stuff.
KALDI_ASSERT(std::numeric_limits<I>::is_integer);
while (1) {
m %= n;
if (m == 0) return (n > 0 ? n : -n);
n %= m;
if (n == 0) return (m > 0 ? m : -m);
}
}
/// Returns the least common multiple of two integers. Will
/// crash unless the inputs are positive.
template<class I> I Lcm(I m, I n) {
KALDI_ASSERT(m > 0 && n > 0);
I gcd = Gcd(m, n);
return gcd * (m/gcd) * (n/gcd);
}
template<class I> void Factorize(I m, std::vector<I> *factors) {
// Splits a number into its prime factors, in sorted order from
// least to greatest, with duplication. A very inefficient
// algorithm, which is mainly intended for use in the
// mixed-radix FFT computation (where we assume most factors
// are small).
KALDI_ASSERT(factors != NULL);
KALDI_ASSERT(m >= 1); // Doesn't work for zero or negative numbers.
factors->clear();
I small_factors[10] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 };
// First try small factors.
for (I i = 0; i < 10; i++) {
if (m == 1) return; // We're done.
while (m % small_factors[i] == 0) {
m /= small_factors[i];
factors->push_back(small_factors[i]);
}
}
// Next try all odd numbers starting from 31.
for (I j = 31;; j += 2) {
if (m == 1) return;
while (m % j == 0) {
m /= j;
factors->push_back(j);
}
}
}
inline double Hypot(double x, double y) { return hypot(x, y); }
inline float Hypot(float x, float y) { return hypotf(x, y); }
} // namespace kaldi
#endif // KALDI_BASE_KALDI_MATH_H_
@@ -0,0 +1,76 @@
// base/kaldi-types.h
// Copyright 2009-2011 Microsoft Corporation; Saarland University;
// Jan Silovsky; Yanmin Qian
// See ../../COPYING for clarification regarding multiple authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
// WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
// MERCHANTABLITY OR NON-INFRINGEMENT.
// See the Apache 2 License for the specific language governing permissions and
// limitations under the License.
#ifndef KALDI_BASE_KALDI_TYPES_H_
#define KALDI_BASE_KALDI_TYPES_H_ 1
namespace kaldi {
// TYPEDEFS ..................................................................
#if (KALDI_DOUBLEPRECISION != 0)
typedef double BaseFloat;
#else
typedef float BaseFloat;
#endif
}
#ifdef _MSC_VER
#include <basetsd.h>
// Don't define ssize_t as macro - let fst/types.h handle it
// #define ssize_t SSIZE_T // This causes conflicts with fst/types.h
#endif
// we can do this a different way if some platform
// we find in the future lacks stdint.h
#include <stdint.h>
// for discussion on what to do if you need compile kaldi
// without OpenFST, see the bottom of this this file
#include <fst/types.h>
namespace kaldi {
using ::int16;
using ::int32;
using ::int64;
using ::uint16;
using ::uint32;
using ::uint64;
typedef float float32;
typedef double double64;
} // end namespace kaldi
// In a theoretical case you decide compile Kaldi without the OpenFST
// comment the previous namespace statement and uncomment the following
/*
namespace kaldi {
typedef int8_t int8;
typedef int16_t int16;
typedef int32_t int32;
typedef int64_t int64;
typedef uint8_t uint8;
typedef uint16_t uint16;
typedef uint32_t uint32;
typedef uint64_t uint64;
typedef float float32;
typedef double double64;
} // end namespace kaldi
*/
#endif // KALDI_BASE_KALDI_TYPES_H_
@@ -0,0 +1,46 @@
// base/kaldi-utils.cc
// Copyright 2009-2011 Karel Vesely; Yanmin Qian; Microsoft Corporation
// See ../../COPYING for clarification regarding multiple authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
// WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
// MERCHANTABLITY OR NON-INFRINGEMENT.
// See the Apache 2 License for the specific language governing permissions and
// limitations under the License.
#include "base/kaldi-utils.h"
#include <cctype>
#include <chrono>
#include <cstdio>
#include <thread>
namespace kaldi {
std::string CharToString(const char &c) {
char buf[20];
if (std::isprint(c))
std::snprintf(buf, sizeof(buf), "\'%c\'", c);
else
std::snprintf(buf, sizeof(buf), "[character %d]", static_cast<int>(c));
return buf;
}
void Sleep(double sec) {
// duration_cast<> rounds down, add 0.5 to compensate.
auto dur_nanos = std::chrono::duration<double, std::nano>(sec * 1E9 + 0.5);
auto dur_syshires = std::chrono::duration_cast<
typename std::chrono::high_resolution_clock::duration>(dur_nanos);
std::this_thread::sleep_for(dur_syshires);
}
} // end namespace kaldi
+162
View File
@@ -0,0 +1,162 @@
// base/kaldi-utils.h
// Copyright 2009-2011 Ondrej Glembek; Microsoft Corporation;
// Saarland University; Karel Vesely; Yanmin Qian
// See ../../COPYING for clarification regarding multiple authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
// WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
// MERCHANTABLITY OR NON-INFRINGEMENT.
// See the Apache 2 License for the specific language governing permissions and
// limitations under the License.
#ifndef KALDI_BASE_KALDI_UTILS_H_
#define KALDI_BASE_KALDI_UTILS_H_ 1
#if _MSC_VER
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN 1
#endif
#ifndef NOMINMAX
#define NOMINMAX 1
#endif
#include <windows.h>
#endif
#ifdef _MSC_VER
#include <stdio.h>
#define unlink _unlink
#else
#include <unistd.h>
#endif
#include <limits>
#include <string>
#if defined(_MSC_VER)
#pragma warning(disable: 4244 4056 4305 4800 4267 4996 4756 4661)
#if _MSC_VER < 1400
#define __restrict__
#else
#define __restrict__ __restrict
#endif
#endif
#if defined(_MSC_VER)
# define KALDI_MEMALIGN(align, size, pp_orig) \
(*(pp_orig) = _aligned_malloc(size, align))
# define KALDI_MEMALIGN_FREE(x) _aligned_free(x)
#elif defined(__CYGWIN__)
# define KALDI_MEMALIGN(align, size, pp_orig) \
(*(pp_orig) = aligned_alloc(align, size))
# define KALDI_MEMALIGN_FREE(x) free(x)
#else
# define KALDI_MEMALIGN(align, size, pp_orig) \
(!posix_memalign(pp_orig, align, size) ? *(pp_orig) : NULL)
# define KALDI_MEMALIGN_FREE(x) free(x)
#endif
#ifdef __ICC
#pragma warning(disable: 383) // ICPC remark we don't want.
#pragma warning(disable: 810) // ICPC remark we don't want.
#pragma warning(disable: 981) // ICPC remark we don't want.
#pragma warning(disable: 1418) // ICPC remark we don't want.
#pragma warning(disable: 444) // ICPC remark we don't want.
#pragma warning(disable: 869) // ICPC remark we don't want.
#pragma warning(disable: 1287) // ICPC remark we don't want.
#pragma warning(disable: 279) // ICPC remark we don't want.
#pragma warning(disable: 981) // ICPC remark we don't want.
#endif
namespace kaldi {
// CharToString prints the character in a human-readable form, for debugging.
std::string CharToString(const char &c);
inline int MachineIsLittleEndian() {
int check = 1;
return (*reinterpret_cast<char*>(&check) != 0);
}
// This function kaldi::Sleep() provides a portable way
// to sleep for a possibly fractional
// number of seconds. On Windows it's only accurate to microseconds.
void Sleep(double seconds);
}
#define KALDI_SWAP8(a) do { \
int t = (reinterpret_cast<char*>(&a))[0];\
(reinterpret_cast<char*>(&a))[0]=(reinterpret_cast<char*>(&a))[7];\
(reinterpret_cast<char*>(&a))[7]=t;\
t = (reinterpret_cast<char*>(&a))[1];\
(reinterpret_cast<char*>(&a))[1]=(reinterpret_cast<char*>(&a))[6];\
(reinterpret_cast<char*>(&a))[6]=t;\
t = (reinterpret_cast<char*>(&a))[2];\
(reinterpret_cast<char*>(&a))[2]=(reinterpret_cast<char*>(&a))[5];\
(reinterpret_cast<char*>(&a))[5]=t;\
t = (reinterpret_cast<char*>(&a))[3];\
(reinterpret_cast<char*>(&a))[3]=(reinterpret_cast<char*>(&a))[4];\
(reinterpret_cast<char*>(&a))[4]=t;} while (0)
#define KALDI_SWAP4(a) do { \
int t = (reinterpret_cast<char*>(&a))[0];\
(reinterpret_cast<char*>(&a))[0]=(reinterpret_cast<char*>(&a))[3];\
(reinterpret_cast<char*>(&a))[3]=t;\
t = (reinterpret_cast<char*>(&a))[1];\
(reinterpret_cast<char*>(&a))[1]=(reinterpret_cast<char*>(&a))[2];\
(reinterpret_cast<char*>(&a))[2]=t;} while (0)
#define KALDI_SWAP2(a) do { \
int t = (reinterpret_cast<char*>(&a))[0];\
(reinterpret_cast<char*>(&a))[0]=(reinterpret_cast<char*>(&a))[1];\
(reinterpret_cast<char*>(&a))[1]=t;} while (0)
///\brief Declare deleted copy constructor and copy assignment operator=().
///
/// Use this macro in the \e public part of a class declaration in a header
/// file, next to its constructors, so that uncopyability of the type is
/// clearly readable. Place a semicolon after it.
///\param type The exact enclosing class name.
#define KALDI_DISALLOW_COPY_AND_ASSIGN(type) \
type(const type&) = delete; \
type& operator=(const type&) = delete
#if __cplusplus >= 201703L
#define KALDI_COMPILE_TIME_ASSERT static_assert
#else
#define KALDI_COMPILE_TIME_ASSERT(b) static_assert((b), #b)
#endif
#define KALDI_ASSERT_IS_INTEGER_TYPE(I) \
KALDI_COMPILE_TIME_ASSERT(std::numeric_limits<I>::is_specialized \
&& std::numeric_limits<I>::is_integer)
#define KALDI_ASSERT_IS_FLOATING_TYPE(F) \
KALDI_COMPILE_TIME_ASSERT(std::numeric_limits<F>::is_specialized \
&& !std::numeric_limits<F>::is_integer)
#if defined(_MSC_VER)
#define KALDI_STRCASECMP _stricmp
#elif defined(__CYGWIN__)
#include <strings.h>
#define KALDI_STRCASECMP strcasecmp
#else
#define KALDI_STRCASECMP strcasecmp
#endif
#ifdef _MSC_VER
# define KALDI_STRTOLL(cur_cstr, end_cstr) _strtoi64(cur_cstr, end_cstr, 10);
#else
# define KALDI_STRTOLL(cur_cstr, end_cstr) strtoll(cur_cstr, end_cstr, 10);
#endif
#endif // KALDI_BASE_KALDI_UTILS_H_
@@ -0,0 +1,45 @@
// base/timer-test.cc
// Copyright 2009-2011 Microsoft Corporation
// 2014 Johns Hopkins University (author: Daniel Povey)
// See ../../COPYING for clarification regarding multiple authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
// WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
// MERCHANTABLITY OR NON-INFRINGEMENT.
// See the Apache 2 License for the specific language governing permissions and
// limitations under the License.
#include "base/timer.h"
#include "base/kaldi-common.h"
#include "base/kaldi-utils.h"
namespace kaldi {
void TimerTest() {
float time_secs = 0.025 * (rand() % 10);
std::cout << "target is " << time_secs << "\n";
Timer timer;
Sleep(time_secs);
BaseFloat f = timer.Elapsed();
std::cout << "time is " << f << std::endl;
if (fabs(time_secs - f) > 0.05)
KALDI_ERR << "Timer fail: waited " << f << " seconds instead of "
<< time_secs << " secs.";
}
}
int main() {
for (int i = 0; i < 4; i++)
kaldi::TimerTest();
}
+85
View File
@@ -0,0 +1,85 @@
// base/timer.cc
// Copyright 2018 Johns Hopkins University (author: Daniel Povey)
// See ../../COPYING for clarification regarding multiple authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
// WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
// MERCHANTABLITY OR NON-INFRINGEMENT.
// See the Apache 2 License for the specific language governing permissions and
// limitations under the License.
#include "base/timer.h"
#include "base/kaldi-error.h"
#include <algorithm>
#include <iomanip>
#include <map>
#include <unordered_map>
namespace kaldi {
class ProfileStats {
public:
void AccStats(const char *function_name, double elapsed) {
std::unordered_map<const char*, ProfileStatsEntry>::iterator
iter = map_.find(function_name);
if (iter == map_.end()) {
map_[function_name] = ProfileStatsEntry(function_name);
map_[function_name].total_time = elapsed;
} else {
iter->second.total_time += elapsed;
}
}
~ProfileStats() {
// This map makes sure we agglomerate the time if there were any duplicate
// addresses of strings.
std::unordered_map<std::string, double> total_time;
for (auto iter = map_.begin(); iter != map_.end(); iter++)
total_time[iter->second.name] += iter->second.total_time;
ReverseSecondComparator comp;
std::vector<std::pair<std::string, double> > pairs(total_time.begin(),
total_time.end());
std::sort(pairs.begin(), pairs.end(), comp);
for (size_t i = 0; i < pairs.size(); i++) {
KALDI_LOG << "Time taken in " << pairs[i].first << " is "
<< std::fixed << std::setprecision(2) << pairs[i].second << "s.";
}
}
private:
struct ProfileStatsEntry {
std::string name;
double total_time;
ProfileStatsEntry() { }
ProfileStatsEntry(const char *name): name(name) { }
};
struct ReverseSecondComparator {
bool operator () (const std::pair<std::string, double> &a,
const std::pair<std::string, double> &b) {
return a.second > b.second;
}
};
// Note: this map is keyed on the address of the string, there is no proper
// hash function. The assumption is that the strings are compile-time
// constants.
std::unordered_map<const char*, ProfileStatsEntry> map_;
};
ProfileStats g_profile_stats;
Profiler::~Profiler() {
g_profile_stats.AccStats(name_, tim_.Elapsed());
}
} // namespace kaldi
+115
View File
@@ -0,0 +1,115 @@
// base/timer.h
// Copyright 2009-2011 Ondrej Glembek; Microsoft Corporation
// See ../../COPYING for clarification regarding multiple authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
// WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
// MERCHANTABLITY OR NON-INFRINGEMENT.
// See the Apache 2 License for the specific language governing permissions and
// limitations under the License.
#ifndef KALDI_BASE_TIMER_H_
#define KALDI_BASE_TIMER_H_
#include "base/kaldi-utils.h"
#include "base/kaldi-error.h"
#if defined(_MSC_VER) || defined(MINGW)
namespace kaldi {
class Timer {
public:
Timer() { Reset(); }
// You can initialize with bool to control whether or not you want the time to
// be set when the object is created.
explicit Timer(bool set_timer) { if (set_timer) Reset(); }
void Reset() {
QueryPerformanceCounter(&time_start_);
}
double Elapsed() const {
LARGE_INTEGER time_end;
LARGE_INTEGER freq;
QueryPerformanceCounter(&time_end);
if (QueryPerformanceFrequency(&freq) == 0) {
// Hardware does not support this.
return 0.0;
}
return (static_cast<double>(time_end.QuadPart) -
static_cast<double>(time_start_.QuadPart)) /
(static_cast<double>(freq.QuadPart));
}
private:
LARGE_INTEGER time_start_;
};
#else
#include <sys/time.h>
#include <unistd.h>
namespace kaldi {
class Timer {
public:
Timer() { Reset(); }
// You can initialize with bool to control whether or not you want the time to
// be set when the object is created.
explicit Timer(bool set_timer) { if (set_timer) Reset(); }
void Reset() { gettimeofday(&this->time_start_, &time_zone_); }
/// Returns time in seconds.
double Elapsed() const {
struct timeval time_end;
struct timezone time_zone;
gettimeofday(&time_end, &time_zone);
double t1, t2;
t1 = static_cast<double>(time_start_.tv_sec) +
static_cast<double>(time_start_.tv_usec)/(1000*1000);
t2 = static_cast<double>(time_end.tv_sec) +
static_cast<double>(time_end.tv_usec)/(1000*1000);
return t2-t1;
}
private:
struct timeval time_start_;
struct timezone time_zone_;
};
#endif
class Profiler {
public:
// Caution: the 'const char' should always be a string constant; for speed,
// internally the profiling code uses the address of it as a lookup key.
Profiler(const char *function_name): name_(function_name) { }
~Profiler();
private:
Timer tim_;
const char *name_;
};
// To add timing info for a function, you just put
// KALDI_PROFILE;
// at the beginning of the function. Caution: this doesn't
// include the class name.
#define KALDI_PROFILE Profiler _profiler(__func__)
} // namespace kaldi
#endif // KALDI_BASE_TIMER_H_