]> Repositorios git - scryer-prolog.git/commitdiff
ENHANCED: Faster format/3 for binary streams.
authorMarkus Triska <[email protected]>
Fri, 22 May 2020 15:30:29 +0000 (17:30 +0200)
committerMarkus Triska <[email protected]>
Fri, 22 May 2020 15:30:29 +0000 (17:30 +0200)
This speeds up web servers considerably when sending binary files.

src/prolog/clause_types.rs
src/prolog/lib/format.pl
src/prolog/machine/system_calls.rs

index b783ac0ce3de31d277143bb114761c14192b7b3a..e685c65fccf623e5e8c97e12ac90c4af830956b5 100644 (file)
@@ -229,6 +229,7 @@ pub enum SystemClauseType {
     PeekCode,
     PointsToContinuationResetMarker,
     PutByte,
+    PutBytes,
     PutChar,
     PutCode,
     REPL(REPLCodePtr),
@@ -415,6 +416,9 @@ impl SystemClauseType {
             &SystemClauseType::PutByte => {
                 clause_name!("$put_byte")
             }
+            &SystemClauseType::PutBytes => {
+                clause_name!("$put_bytes")
+            }
             &SystemClauseType::PutChar => {
                 clause_name!("$put_char")
             }
@@ -552,6 +556,9 @@ impl SystemClauseType {
             ("$put_byte", 2) => {
                 Some(SystemClauseType::PutByte)
             }
+            ("$put_bytes", 2) => {
+                Some(SystemClauseType::PutBytes)
+            }
             ("$put_char", 2) => {
                 Some(SystemClauseType::PutChar)
             }
index c0ef87c110b54ebb1750af412056f750312249a2..18289094c5d9492aa09ac2e37242b291860d20dd 100644 (file)
@@ -367,8 +367,20 @@ format(Fs, Args) :-
 format(Stream, Fs, Args) :-
         phrase(format_(Fs, Args), Cs),
         (   stream_property(Stream, type(binary)) ->
-            maplist(char_code, Cs, Bytes),
-            maplist(put_byte(Stream), Bytes)
+            % maplist(char_code, Cs, Bytes) is currently a lot slower
+            % than first converting Cs to an atom, and then to codes.
+            % In the future, we can ideally avoid creating an atom here,
+            % since an atom leaves traces in the system.
+            atom_chars(A, Cs),
+            atom_codes(A, Bytes),
+            (   member(NonByte, Bytes), NonByte > 255 ->
+                char_code(Char, NonByte),
+                throw(error(representation_error(Char), format/3))
+            ;   true
+            ),
+            % For binary streams, we use a specialised internal predicate
+            % that uses only a single "write" operation for efficiency.
+            '$put_bytes'(Stream, Bytes)
         ;   maplist(put_char(Stream), Cs)
         ).
 
index 1fe7b5767164181d0409f0201ec285359944121b..0ab8411eddb5bb22790780d7d1f401ada32ea4e4 100644 (file)
@@ -2153,6 +2153,37 @@ impl MachineState {
                     }
                 }
             }
+            &SystemClauseType::PutBytes => {
+                let mut stream =
+                    self.get_stream_or_alias(self[temp_v!(1)], indices, "$put_bytes", 2)?;
+
+                let stub = MachineError::functor_stub(clause_name!("$put_bytes"), 2);
+                let bytes = self.integers_to_bytevec(temp_v!(2), stub);
+
+                match stream.write(&bytes) {
+                    Ok(_) => {
+                        return return_from_clause!(self.last_call, self);
+                    }
+                    _ => {
+                        let stub = MachineError::functor_stub(
+                            clause_name!("$put_bytes"),
+                            2,
+                        );
+
+                        let addr = self.heap.to_unifiable(
+                            HeapCellValue::Stream(stream.clone()),
+                        );
+
+                        return Err(self.error_form(
+                            MachineError::existence_error(
+                                self.heap.h(),
+                                ExistenceError::Stream(addr),
+                            ),
+                            stub,
+                        ));
+                    }
+                }
+            }
             &SystemClauseType::GetByte => {
                 let mut stream =
                     self.get_stream_or_alias(self[temp_v!(1)], indices, "get_byte", 2)?;