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
|
||||
StackMachine::run()
|
||||
{
|
||||
while( not tokens.empty() )
|
||||
while( not tokenStack.empty() and tokenStack.back().hasNext() )
|
||||
{
|
||||
runWord( next() );
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
std::string
|
||||
std::string_view
|
||||
StackMachine::next()
|
||||
{
|
||||
if( tokens.empty() )
|
||||
if( tokenStack.empty() )
|
||||
{
|
||||
throw std::runtime_error{ "FATAL: Token required, no more tokens left." };
|
||||
}
|
||||
|
||||
const auto rv= tokens.front();
|
||||
tokens.pop_front();
|
||||
const auto rv= tokenStack.back().next();
|
||||
while( not tokenStack.empty() and not tokenStack.back().hasNext() ) tokenStack.pop_back();
|
||||
return rv;
|
||||
}
|
||||
|
||||
@ -161,7 +164,7 @@ namespace Dillo::Hydrogen::JavaScriptForge ::detail:: StackMachine_m
|
||||
}
|
||||
else if( word == "@print" )
|
||||
{
|
||||
output << pop() << std::endl;
|
||||
output << pop< std::string >() << std::endl;
|
||||
}
|
||||
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." };
|
||||
|
||||
definition= pop();
|
||||
definition= pop< std::string >();
|
||||
|
||||
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 );
|
||||
|
||||
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
|
||||
{
|
||||
@ -203,10 +208,12 @@ namespace Dillo::Hydrogen::JavaScriptForge ::detail:: StackMachine_m
|
||||
T
|
||||
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()
|
||||
{
|
||||
const auto rv= peek();
|
||||
@ -214,24 +221,38 @@ namespace Dillo::Hydrogen::JavaScriptForge ::detail:: StackMachine_m
|
||||
return rv;
|
||||
}
|
||||
|
||||
const std::string &
|
||||
const std::variant< std::string, StackMachine::Integer > &
|
||||
StackMachine::peek()
|
||||
{
|
||||
if( stack.empty() ) throw std::runtime_error{ "FATAL: No more elements in stack." };
|
||||
return stack.back();
|
||||
}
|
||||
|
||||
void
|
||||
StackMachine::push( std::variant< std::string, Integer > element )
|
||||
{
|
||||
stack.push_back( std::move( element ) );
|
||||
}
|
||||
|
||||
void
|
||||
StackMachine::push( std::string element )
|
||||
{
|
||||
stack.push_back( std::move( element ) );
|
||||
}
|
||||
|
||||
void
|
||||
StackMachine::push( Integer element )
|
||||
{
|
||||
stack.push_back( std::move( element ) );
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
void
|
||||
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 <vector>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <deque>
|
||||
|
||||
namespace Dillo::Hydrogen::JavaScriptForge ::detail:: StackMachine_m
|
||||
@ -27,22 +28,38 @@ namespace Dillo::Hydrogen::JavaScriptForge ::detail:: StackMachine_m
|
||||
class exports::StackMachine
|
||||
{
|
||||
private:
|
||||
using Integer= std::int64_t;
|
||||
using Float= long double;
|
||||
|
||||
// The main runtime stack of the forth engine.
|
||||
// It grows on the "back".
|
||||
// A `std::deque` is used instead of `std::vector`, because
|
||||
// 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?
|
||||
// Upside: compressed.
|
||||
// Downside: more memory management in the machine end?
|
||||
std::deque< std::string > tokens;
|
||||
|
||||
using Integer= std::int64_t;
|
||||
using Float= long double;
|
||||
struct Tokenizer
|
||||
{
|
||||
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.
|
||||
enum ConditionalState { If, Else, Skipped };
|
||||
@ -58,10 +75,12 @@ namespace Dillo::Hydrogen::JavaScriptForge ::detail:: StackMachine_m
|
||||
|
||||
void runWord( std::string_view word );
|
||||
|
||||
std::string pop();
|
||||
const std::string &peek();
|
||||
std::variant< std::string, Integer > pop();
|
||||
const std::variant< std::string, Integer > &peek();
|
||||
|
||||
void push( std::string s );
|
||||
void push( Integer s );
|
||||
void push( std::variant< std::string, Integer > s );
|
||||
|
||||
template< typename T >
|
||||
T pop();
|
||||
@ -69,7 +88,10 @@ namespace Dillo::Hydrogen::JavaScriptForge ::detail:: StackMachine_m
|
||||
template< typename T >
|
||||
void push( const T &t );
|
||||
|
||||
std::string next();
|
||||
|
||||
std::string_view next();
|
||||
|
||||
void recurse( std::string_view func );
|
||||
|
||||
public:
|
||||
StackMachine( std::ostream &output= std::cout );
|
||||
@ -78,6 +100,8 @@ namespace Dillo::Hydrogen::JavaScriptForge ::detail:: StackMachine_m
|
||||
loadProgram( std::deque< std::string > tokens )
|
||||
{
|
||||
this->tokens= std::move( tokens );
|
||||
tokenStack.clear();
|
||||
tokenStack.emplace_back( this->tokens );
|
||||
}
|
||||
|
||||
void run();
|
||||
|
||||
@ -29,8 +29,8 @@ do_fibs
|
||||
@dup
|
||||
@i_dec
|
||||
@do_fibs
|
||||
@print
|
||||
@fib
|
||||
@print
|
||||
@else
|
||||
0
|
||||
@endif
|
||||
@ -42,6 +42,6 @@ junk
|
||||
}
|
||||
|
||||
|
||||
37 @do_fibs
|
||||
39 @do_fibs
|
||||
|
||||
|
||||
|
||||
@ -1,15 +1,17 @@
|
||||
def fib( a ):
|
||||
if a == 0:
|
||||
return 1
|
||||
return 0
|
||||
elif a == 1:
|
||||
return 1
|
||||
else:
|
||||
return fib( a - 1 ) + fib( a - 2 )
|
||||
|
||||
def do_fibs( cnt ):
|
||||
if cnt != 0:
|
||||
if cnt > 0:
|
||||
do_fibs( cnt - 1 )
|
||||
print( fib( cnt ) )
|
||||
else:
|
||||
print( fib( 0 ) )
|
||||
|
||||
|
||||
do_fibs( 35 )
|
||||
do_fibs( 38 )
|
||||
|
||||
Reference in New Issue
Block a user