Significant speed improvements.
By not cloning code for recursion directly into the processing buffer and by not copying as many strings and by not doing string->int conversions all the time, it's significantly faster. Specifically, it's about 5.5x slower than Python.
This commit is contained in:
@ -24,27 +24,30 @@ namespace Dillo::Hydrogen::JavaScriptForge ::detail:: StackMachine_m
|
|||||||
void
|
void
|
||||||
StackMachine::run()
|
StackMachine::run()
|
||||||
{
|
{
|
||||||
while( not tokens.empty() )
|
while( not tokenStack.empty() and tokenStack.back().hasNext() )
|
||||||
{
|
{
|
||||||
runWord( next() );
|
runWord( next() );
|
||||||
|
|
||||||
if( C::debug ) std::cerr << "After processing stack is now: " << std::endl;
|
if( C::debug ) std::cerr << "After processing stack is now: " << std::endl;
|
||||||
if( C::debug ) std::copy( rbegin( stack ), rend( stack ), std::ostream_iterator< std::string >{ std::cerr, "\n" } );
|
if( C::debug ) for( const auto &element: stack )
|
||||||
|
{
|
||||||
|
std::cerr << std::get< std::string >( element ) << std::endl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( C::debug ) std::cerr << "Run done with stack at size: " << stack.size() << std::endl;
|
if( C::debug ) std::cerr << "Run done with stack at size: " << stack.size() << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string
|
std::string_view
|
||||||
StackMachine::next()
|
StackMachine::next()
|
||||||
{
|
{
|
||||||
if( tokens.empty() )
|
if( tokenStack.empty() )
|
||||||
{
|
{
|
||||||
throw std::runtime_error{ "FATAL: Token required, no more tokens left." };
|
throw std::runtime_error{ "FATAL: Token required, no more tokens left." };
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto rv= tokens.front();
|
const auto rv= tokenStack.back().next();
|
||||||
tokens.pop_front();
|
while( not tokenStack.empty() and not tokenStack.back().hasNext() ) tokenStack.pop_back();
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,7 +164,7 @@ namespace Dillo::Hydrogen::JavaScriptForge ::detail:: StackMachine_m
|
|||||||
}
|
}
|
||||||
else if( word == "@print" )
|
else if( word == "@print" )
|
||||||
{
|
{
|
||||||
output << pop() << std::endl;
|
output << pop< std::string >() << std::endl;
|
||||||
}
|
}
|
||||||
else if( word == "@if" )
|
else if( word == "@if" )
|
||||||
{
|
{
|
||||||
@ -174,7 +177,7 @@ namespace Dillo::Hydrogen::JavaScriptForge ::detail:: StackMachine_m
|
|||||||
{
|
{
|
||||||
if( definition.has_value() ) throw std::runtime_error{ "Nested definitions not supported." };
|
if( definition.has_value() ) throw std::runtime_error{ "Nested definitions not supported." };
|
||||||
|
|
||||||
definition= pop();
|
definition= pop< std::string >();
|
||||||
|
|
||||||
if( C::debug ) std::cerr << "Starting function definition for " << definition.value() << std::endl;
|
if( C::debug ) std::cerr << "Starting function definition for " << definition.value() << std::endl;
|
||||||
}
|
}
|
||||||
@ -189,9 +192,11 @@ namespace Dillo::Hydrogen::JavaScriptForge ::detail:: StackMachine_m
|
|||||||
|
|
||||||
const auto &def= words.at( invoke );
|
const auto &def= words.at( invoke );
|
||||||
|
|
||||||
std::copy( rbegin( def ), rend( def ), front_inserter( tokens ) );
|
//std::copy( rbegin( def ), rend( def ), front_inserter( tokens ) );
|
||||||
|
|
||||||
if( tokens.size() >= 1000 ) abort();
|
//if( tokens.size() >= 1000 ) abort();
|
||||||
|
|
||||||
|
tokenStack.emplace_back( def );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -203,10 +208,12 @@ namespace Dillo::Hydrogen::JavaScriptForge ::detail:: StackMachine_m
|
|||||||
T
|
T
|
||||||
StackMachine::pop()
|
StackMachine::pop()
|
||||||
{
|
{
|
||||||
return boost::lexical_cast< T >( pop() );
|
if( std::holds_alternative< T >( peek() ) ) return std::get< T >( pop() );
|
||||||
|
else if( std::holds_alternative< Integer >( peek() ) ) return boost::lexical_cast< T >( std::get< Integer >( pop() ) );
|
||||||
|
else return boost::lexical_cast< T >( std::get< std::string >( pop() ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string
|
std::variant< std::string, StackMachine::Integer >
|
||||||
StackMachine::pop()
|
StackMachine::pop()
|
||||||
{
|
{
|
||||||
const auto rv= peek();
|
const auto rv= peek();
|
||||||
@ -214,24 +221,38 @@ namespace Dillo::Hydrogen::JavaScriptForge ::detail:: StackMachine_m
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string &
|
const std::variant< std::string, StackMachine::Integer > &
|
||||||
StackMachine::peek()
|
StackMachine::peek()
|
||||||
{
|
{
|
||||||
if( stack.empty() ) throw std::runtime_error{ "FATAL: No more elements in stack." };
|
if( stack.empty() ) throw std::runtime_error{ "FATAL: No more elements in stack." };
|
||||||
return stack.back();
|
return stack.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
StackMachine::push( std::variant< std::string, Integer > element )
|
||||||
|
{
|
||||||
|
stack.push_back( std::move( element ) );
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
StackMachine::push( std::string element )
|
StackMachine::push( std::string element )
|
||||||
{
|
{
|
||||||
stack.push_back( std::move( element ) );
|
stack.push_back( std::move( element ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
StackMachine::push( Integer element )
|
||||||
|
{
|
||||||
|
stack.push_back( std::move( element ) );
|
||||||
|
}
|
||||||
|
|
||||||
template< typename T >
|
template< typename T >
|
||||||
void
|
void
|
||||||
StackMachine::push( const T &t )
|
StackMachine::push( const T &t )
|
||||||
{
|
{
|
||||||
push( boost::lexical_cast< std::string >( t ) );
|
if constexpr( std::is_same_v< Integer, T > ) push( t );
|
||||||
|
else if constexpr( std::is_same_v< std::string, T > ) push( t );
|
||||||
|
else push( boost::lexical_cast< std::string >( t ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -9,6 +9,7 @@ static_assert( __cplusplus >= 2023'02 );
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <variant>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
|
||||||
namespace Dillo::Hydrogen::JavaScriptForge ::detail:: StackMachine_m
|
namespace Dillo::Hydrogen::JavaScriptForge ::detail:: StackMachine_m
|
||||||
@ -27,22 +28,38 @@ namespace Dillo::Hydrogen::JavaScriptForge ::detail:: StackMachine_m
|
|||||||
class exports::StackMachine
|
class exports::StackMachine
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
using Integer= std::int64_t;
|
||||||
|
using Float= long double;
|
||||||
|
|
||||||
// The main runtime stack of the forth engine.
|
// The main runtime stack of the forth engine.
|
||||||
// It grows on the "back".
|
// It grows on the "back".
|
||||||
// A `std::deque` is used instead of `std::vector`, because
|
// A `std::deque` is used instead of `std::vector`, because
|
||||||
// it helps cut down on large allocations, by subdividing things.
|
// it helps cut down on large allocations, by subdividing things.
|
||||||
std::deque< std::string > stack;
|
std::deque< std::variant< std::string, Integer > > stack;
|
||||||
|
|
||||||
// TODO: Should these tokens be binary?
|
// TODO: Should these tokens be binary?
|
||||||
// Upside: compressed.
|
// Upside: compressed.
|
||||||
// Downside: more memory management in the machine end?
|
// Downside: more memory management in the machine end?
|
||||||
std::deque< std::string > tokens;
|
std::deque< std::string > tokens;
|
||||||
|
|
||||||
using Integer= std::int64_t;
|
struct Tokenizer
|
||||||
using Float= long double;
|
{
|
||||||
|
std::deque< std::string >::const_iterator pos;
|
||||||
|
std::deque< std::string >::const_iterator end;
|
||||||
|
|
||||||
|
explicit
|
||||||
|
Tokenizer( const std::deque< std::string > &tokens )
|
||||||
|
: pos( tokens.begin() ), end( tokens.end() ) {}
|
||||||
|
|
||||||
|
bool hasNext() const { return pos != end; }
|
||||||
|
|
||||||
|
std::string_view next() { return *pos++; }
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector< Tokenizer > tokenStack;
|
||||||
|
|
||||||
|
|
||||||
std::map< std::string, std::vector< std::string > > words;
|
std::map< std::string, std::deque< std::string > > words;
|
||||||
|
|
||||||
// Which side of the current conditional to take.
|
// Which side of the current conditional to take.
|
||||||
enum ConditionalState { If, Else, Skipped };
|
enum ConditionalState { If, Else, Skipped };
|
||||||
@ -58,10 +75,12 @@ namespace Dillo::Hydrogen::JavaScriptForge ::detail:: StackMachine_m
|
|||||||
|
|
||||||
void runWord( std::string_view word );
|
void runWord( std::string_view word );
|
||||||
|
|
||||||
std::string pop();
|
std::variant< std::string, Integer > pop();
|
||||||
const std::string &peek();
|
const std::variant< std::string, Integer > &peek();
|
||||||
|
|
||||||
void push( std::string s );
|
void push( std::string s );
|
||||||
|
void push( Integer s );
|
||||||
|
void push( std::variant< std::string, Integer > s );
|
||||||
|
|
||||||
template< typename T >
|
template< typename T >
|
||||||
T pop();
|
T pop();
|
||||||
@ -69,7 +88,10 @@ namespace Dillo::Hydrogen::JavaScriptForge ::detail:: StackMachine_m
|
|||||||
template< typename T >
|
template< typename T >
|
||||||
void push( const T &t );
|
void push( const T &t );
|
||||||
|
|
||||||
std::string next();
|
|
||||||
|
std::string_view next();
|
||||||
|
|
||||||
|
void recurse( std::string_view func );
|
||||||
|
|
||||||
public:
|
public:
|
||||||
StackMachine( std::ostream &output= std::cout );
|
StackMachine( std::ostream &output= std::cout );
|
||||||
@ -78,6 +100,8 @@ namespace Dillo::Hydrogen::JavaScriptForge ::detail:: StackMachine_m
|
|||||||
loadProgram( std::deque< std::string > tokens )
|
loadProgram( std::deque< std::string > tokens )
|
||||||
{
|
{
|
||||||
this->tokens= std::move( tokens );
|
this->tokens= std::move( tokens );
|
||||||
|
tokenStack.clear();
|
||||||
|
tokenStack.emplace_back( this->tokens );
|
||||||
}
|
}
|
||||||
|
|
||||||
void run();
|
void run();
|
||||||
|
|||||||
@ -29,8 +29,8 @@ do_fibs
|
|||||||
@dup
|
@dup
|
||||||
@i_dec
|
@i_dec
|
||||||
@do_fibs
|
@do_fibs
|
||||||
@print
|
|
||||||
@fib
|
@fib
|
||||||
|
@print
|
||||||
@else
|
@else
|
||||||
0
|
0
|
||||||
@endif
|
@endif
|
||||||
@ -42,6 +42,6 @@ junk
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
37 @do_fibs
|
39 @do_fibs
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,15 +1,17 @@
|
|||||||
def fib( a ):
|
def fib( a ):
|
||||||
if a == 0:
|
if a == 0:
|
||||||
return 1
|
return 0
|
||||||
elif a == 1:
|
elif a == 1:
|
||||||
return 1
|
return 1
|
||||||
else:
|
else:
|
||||||
return fib( a - 1 ) + fib( a - 2 )
|
return fib( a - 1 ) + fib( a - 2 )
|
||||||
|
|
||||||
def do_fibs( cnt ):
|
def do_fibs( cnt ):
|
||||||
if cnt != 0:
|
if cnt > 0:
|
||||||
do_fibs( cnt - 1 )
|
do_fibs( cnt - 1 )
|
||||||
print( fib( cnt ) )
|
print( fib( cnt ) )
|
||||||
|
else:
|
||||||
|
print( fib( 0 ) )
|
||||||
|
|
||||||
|
|
||||||
do_fibs( 35 )
|
do_fibs( 38 )
|
||||||
|
|||||||
Reference in New Issue
Block a user