Ask Your Question
2

Running Sage from other languages with high(er) performance?

asked 2014-07-16 11:45:14 +0200

Woodgnome gravatar image

updated 2014-07-16 16:50:46 +0200

slelievre gravatar image

I'm in the process of creating af CAS tool that uses Sage as the underlying math engine. It's a web application, so most of the code is HTML/Javascript and some underlying PHP from which I'd like to use Sage. Currently I've resorted to the -c command line option and executing it through PHP with exec().

This approach is extremely slow (takes several seconds and CPU load is fairly high). This is - as far as I'm aware - a result of Sage having to load all its libraries each time it's run.

Is there any way I can optimize this approach? I would prefer an approach similar to the -c option where I can just send a command and read the printed result.

I have no issues writing a wrapper in C (or Python if neccessary), nor working with sockets (if any are available) provided it can be done as simple as the -c option (the WebSocket messages seen in Sage Cell seems too much trouble to do manually).

What I need to know is mostly whether or not it's even possible, if so then where to start otherwise I'd like to know if there are any alternative approaches?

Why not use Sage Cell/Cloud?

Several reasons:

  • I don't have complete control of the UI, so I can't actually embed a Sage Cell.
  • The CAS tool is part of a much larger system with other services sharing resources.
  • Both are a bit overkill for the job since they include a UI and webserver - I just need to send the command and read the response.
edit retag flag offensive close merge delete

Comments

2 Answers

Sort by ยป oldest newest most voted
0

answered 2014-07-16 16:20:43 +0200

slelievre gravatar image

updated 2014-07-16 16:49:53 +0200

This is because everytime you run sage -c 'some command', you have to wait for Sage to start up. I remember this came up a few years ago either on ask-sage or on one of the mailing lists sage-support, sage-devel, ... and someone said one solution was to have an instance of Sage running constantly in the background, so that the request would just be executed without having to start up Sage at each request. Trying to track down a reference, I came across the following three:
sage-devel, 2011-01-15, Sage forker
sage-support, 2011-08-02, start-up time in sage scripts
sage-support, 2012-06-28, Faster sage command line startup?

It seems that sage-forker, developed by Jeroen Demeyer and Jason Grout, was an important ingredient. I don't know if it is still current or if there is now some other way to achieve the same result.

A lot of thought also goes into improving the start-up time: a quick search for sage start-up time will reveal many discussions on the above-mentioned mailing lists, and on the Sage trac. But here what you want is not to start Sage each time, more than a lower start-up time.

edit flag offensive delete link more

Comments

sage-forker still seems to work, trying to modify it to accept a `-c` command, but not really having any luck with the Python part of the code :<

Woodgnome gravatar imageWoodgnome ( 2014-07-17 10:29:15 +0200 )edit
1

answered 2014-07-17 14:44:54 +0200

Woodgnome gravatar image

updated 2014-07-21 09:52:47 +0200

I finally realised that the Sage shell is a Python shell and that it's possible to run Sage inside a Python script. With this knowledge I was able to write a Python script that can execute Sage commands and uses sockets for inter-process communication.

I decided to write the script to act as much like running sage -c 'commmand' as possible. I copied most of the code from sage/local/sage-eval and paired it up with a Python socket example and ended with the following Python script:

import socket
import sys
from cStringIO import StringIO
from sage.all import *
from sage.calculus.predefined import x
from sage.misc.preparser import preparse

SHUTDOWN = False
HOST = 'localhost'
PORT = 8888
MAX_MSG_LENGTH = 102400

# Create socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
print 'Socket created'

# Bind socket to localhost and port
try:
  s.bind((HOST, PORT))
except socket.error , msg:
  print 'Bind failed. Error Code : ' + str(msg[0]) + ' Message ' + msg[1]
  sys.exit()

print 'Socket bind complete'

# Start listening on socket
s.listen(10)
print 'Socket now listening'

# Loop listener for new connections
while not SHUTDOWN:
  # Wait to accept a new client connection
  conn, addr = s.accept()
  print 'Connected with ' + addr[0] + ':' + str(addr[1])

  # Receive message from client
  msg = conn.recv(MAX_MSG_LENGTH)
  if msg:
    if msg == "stop":
      SHUTDOWN = True
    else:
      parsed = preparse(msg)
      if parsed.startswith('load') or parsed.startswith('attach'):
        os.system('sage "' + os.path.join(os.getcwd(), parsed.split(None, 1)[1]) + '"')
      else:
        # Redirect stdout to my stdout to capture into a string
        sys.stdout = mystdout = StringIO()

        # Evalutate msg
        try:
          eval(compile(parsed,'<cmdline>','exec'))
          result = mystdout.getvalue() # Get result from mystdout
        except Exception as e:
          result = "ERROR: " + str(type(e)) + " " + str(e)

        # Restore stdout
        sys.stdout = sys.__stdout__

        # Send response to connected client
        if result == "":
          conn.sendall("Empty result, did you remember to print?")
        else:
          conn.sendall(result)

  # Close client connection
  conn.close()

# Close listener
s.close()

I saved the script to a file called sage-daemon.py in my Sage root folder. The stopmessage handler isn't really neccessary, but it's useful if anyone can "properly" daemonize the script (and also to avoid interrupting the script with CTRL+C, because that will leave the socket as in-use for a while after the script has been interrupted). See my other question http://ask.sagemath.org/question/2350... .

To run the script: sage -python sage-daemon.py This will leave the Sage shell waiting for socket input.

Here's a PHP script I used to test the functionality with:

<?php

$socket = socket_create(AF_INET, SOCK_STREAM, getprotobyname("tcp"));
if (!$socket) die("Could not create socket\n");

$connected = socket_connect($socket, "localhost", 8888);
if (!$connected) echo "Not connected\n";
else {
  $msg = "print solve(5*x==3,x)";

  $sent = socket_send($socket, $msg, strlen($msg), 0);
  if (!$sent) echo "Error sending message\n";
  else {
    if ($msg != "stop"){
      $response = socket_read($socket, 1024);
      if (!$response) echo "Error receiving response\n";
      else echo $response . "\n";
    }
  }
}

socket_close($socket);

If successfull this should print ... (more)

edit flag offensive delete link more

Comments

change the last import to sage.repl.preparse in newest version

Andriy gravatar imageAndriy ( 2017-06-22 21:55:38 +0200 )edit

Your Answer

Please start posting anonymously - your entry will be published after you log in or create a new account.

Add Answer

Question Tools

1 follower

Stats

Asked: 2014-07-16 11:45:14 +0200

Seen: 1,787 times

Last updated: Jul 21 '14