Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

print is println? #2142

Open
igouy opened this issue Mar 13, 2024 · 19 comments
Open

print is println? #2142

igouy opened this issue Mar 13, 2024 · 19 comments

Comments

@igouy
Copy link

igouy commented Mar 13, 2024

Seems like host print is actually println, is there something without the /n ?

@floitsch
Copy link
Member

Indeed. The print function is always adding a newline at the end.

The core libraries don't have a function that doesn't add a new-line character, but the host package (https://pkg.toit.io/package/github.com%2Ftoitlang%2Fpkg-host) has a stdout pipe which has a write method: https://pkg.toit.io/github.com/toitlang/pkg-host@1.14.1/docs/host/pipe/class-OpenPipe#write(3%2C0%2C0%2C)

It's a bit more complicated but here are roughly the steps to get it.

Install the package. If you don't have any package installed yet follow these steps:

jag pkg init  # In your project root.
jag pkg install host

To write to stdout:

import host.pipe
import writer show Writer

main:
  writer := Writer pipe.stdout
  writer.write "foo"

Note that stdout.write is not guaranteed to write all bytes (if the buffer is full), and it returns the amount of bytes that were consumed. This is why we use a Writer here. That one just loops until the data is fully written.
We are working on a rewrite of the IO library where this won't be necessary anymore. (Hopefully landing within the next few weeks).

@floitsch
Copy link
Member

Actually, I just saw that we have a private function that does a print without adding a newline:

main:
  write-on-stdout_ "foo" false  // The 'false' avoids the newline.

Given the trailing '_' it's private and therefore subject to change, but if you just want to run a few tests it might be easier than to install the host package.

@igouy
Copy link
Author

igouy commented Mar 14, 2024

Thanks, that allowed me to hit the next problem :-)

EXCEPTION error. 
ILLEGAL_UTF_8
  0: ByteArrayBase_.to-string  <sdk>/core/collections.toit:1424:5
  1: main                      mandelbrot.toit:35:35

Trying to write binary, a byte rather than a char (just mandelbrot in pbm format, nothing of any importance). Maybe something I should come back to in a few weeks, after the IO library work.

@erikcorry
Copy link

erikcorry commented Mar 14, 2024

I would use PNG instead of PPM, but the following could be adapted to PPM if that's what you want. Once you have stdout from host.pipe you can use the write method which takes binary data and does not append a newline.

You need to install the host and png-tools packages:

mkdir project
cd project
toit.pkg init
toit.pkg install host
toit.pkg install png-tools

Use jag pkg instead of toit.pkg if that's what you have installed.

import png-tools.png-writer
import host.pipe

main:
  stream := pipe.stdout

  WIDTH := 256
  HEIGHT := 256

  // Create default PNG type, which is 8 bit RGBA.
  writer := png-writer.PngWriter stream WIDTH HEIGHT

  // Create RGBA array.
  pixels := ByteArray (WIDTH * HEIGHT * 4)

  // Fill the array with a gradient.
  HEIGHT.repeat: | y |
    WIDTH.repeat: | x |
      index := (y * WIDTH + x) * 4
      pixels[index] = x        // R.
      pixels[index + 1] = y    // G.
      pixels[index + 2] = 0    // B.
      pixels[index + 3] = 255  // Opaque.

  // Each line is written as a single 0 byte followed by
  // the line's pixels in RGBA format.
  line-buffer := ByteArray (WIDTH * 4 + 1)
  HEIGHT.repeat: | y |
    index := y * WIDTH * 4
    line-buffer.replace 1 pixels index (index + WIDTH * 4)
    writer.write-uncompressed line-buffer

  writer.close

Instead of importing host.pipe and using pipe.stdout you could use:

import host.file

main:
  stream := file.Stream.for-write "file.png"

@igouy
Copy link
Author

igouy commented Mar 15, 2024

Thanks. I discovered that the most foolish approach gives the expected output:

import host.pipe
  …
  byte-acc ::= ByteArray 1
  out := pipe.stdout  
  …
  out.write byte-acc
  …
  out.close

Next byte-acc as a row buffer or I'll look at Writer. Thanks again.

@floitsch
Copy link
Member

Not sure it is relevant in your case, but fyi: you can create byte-arrays with #[value].

The writer is really simple:
Conceptually:

to-write := data.size
// As long as the whole data wasn't written try again.
while written < to-write:
  written += out.write data[written..]

@igouy
Copy link
Author

igouy commented Mar 15, 2024

I've copied a few tiny programs line-by-line from C and Java and Python into Toit. Very much C written in Toit. Java written in Toit. Python written in Toit.

The programs have nothing to do with micro controllers. Nothing to do with the use you intend for Toit.

I've timed those tiny tiny programs and would probably publish the program source and times, alongside programs and times for other programming languages.

This is always kind-of foolish. With Toit it feels more foolish. The programs are being run on a desktop machine not ESP32. The comparison is with CPython not MicroPython, Java SE not Java ME, Go not TinyGo.

So Toit programs will be presented as slower than everything except ruby-1.8.7 — and I'm not hopeful that people will do more than take that at face value. Best-case it's irrelevant, no one will notice :-)

Is it too much like a misrepresentation?

@floitsch
Copy link
Member

If you send us the programs we can have a look and make them a bit more "toity" and avoid the worst pitfalls.
Toit is designed to run well on embedded devices, but it also works nicely on the desktop. Most of our tools are written in it.

Obviously, it won't be able to compete with a VM that has a good JIT (like Java, or JavaScript), but IIRC we were faster than Python on typical benchmarks (deltablue, richards, ...).

@igouy
Copy link
Author

igouy commented Mar 16, 2024

simple.toit is line-by-line to match these simplistic implementations.

Takes ~53 minutes, output redirected to /dev/null. After removing the out.write byte-acc it takes much the same.

Others simple-programs.zip

Command line is shown last in simple-toit-1.html

@floitsch
Copy link
Member

Thanks.
I will take a look at them.
Could you tell us how you run the benchmarks?

@igouy
Copy link
Author

igouy commented Mar 16, 2024

Various ways which seem to give similar times for the toit programs.

$ time /opt/src/toit/bin/toit.run mandelbrot.toit 16000 After removing the out.write byte-acc

Like this for the first measurement which has stdout checked against an expected value

 Sat, 16 Mar 2024 02:24:55 GMT

MAKE:
cp -r Include/toit/.packages .
cp -r Include/toit/package.lock .
cp -r Include/toit/package.yaml .	
/opt/src/toit/bin/toit.compile -O2 --strip -o simple.toit_run simple.toit 

1.25s to complete and log all make actions

COMMAND LINE:
 ./simple.toit_run 16000

(BINARY) PROGRAM OUTPUT NOT SHOWN

@erikcorry
Copy link

I've speeded up floating point operations. This will be in the next release.

@igouy
Copy link
Author

igouy commented Mar 21, 2024

I look forward to seeing how that works out.

@erikcorry
Copy link

Jaguar v1.31.0 and Toit SDK version v2.0.0-alpha.143 are released now with the improved performance.

@igouy
Copy link
Author

igouy commented Mar 21, 2024

I see

$ ./toit142/bin/toit.compile -O2 --strip -o nbody142 nbody.toit
$ hyperfine "./nbody142 500000"
Benchmark 1: ./nbody142 500000
  Time (mean ± σ):     11.945 s ±  0.237 s    [User: 11.810 s, System: 0.131 s]
  Range (min … max):   11.666 s … 12.321 s    10 runs

and then

$ ./toit143/bin/toit.compile -O2 --strip -o nbody143 nbody.toit
$ hyperfine "./nbody143 500000"
Benchmark 1: ./nbody143 500000
  Time (mean ± σ):      7.230 s ±  0.221 s    [User: 7.219 s, System: 0.005 s]
  Range (min … max):    6.955 s …  7.509 s    10 runs

With these alpha versions are the compile line options like -O2 --strip expected to make a significant difference to program times or memory use or …

@floitsch
Copy link
Member

The -O2 option enables a few more optimizations, but the most important ones are already run with -O1.
Stripping only reduces the size of the output file. It should not make a difference in performance.

@floitsch
Copy link
Member

Looked a bit more: the -O2 runs our type inference algorithm. However, that one only really does something when the program has function calls. None of the benchmarks seem to exercise method calls a lot, so running with -O2 doesn't do a lot here.

@igouy
Copy link
Author

igouy commented Mar 22, 2024

Whatever the max performance / lowest memory use options might be.

@igouy
Copy link
Author

igouy commented Mar 27, 2024

github doesn't seem to allow html attachments, so: python3-toit.zip

  1. Seems like I did a very poor job of naive k-nucleotide in Toit, which I'll remove.

  2. Is there anything objectionable about the other program measurements?

  3. This is shown with the version info "Toit is for live-programming low-power
    microcontrollers over WiFi." but I don't think that really helps set expectations for program times compared to CPython program times. Any suggestions?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

3 participants